pax_global_header00006660000000000000000000000064146426607220014523gustar00rootroot0000000000000052 comment=ec4e5210017897403c1e27a073c0465812863f02 python-2.3.14/000077500000000000000000000000001464266072200131335ustar00rootroot00000000000000python-2.3.14/.coveragerc000066400000000000000000000002141464266072200152510ustar00rootroot00000000000000[run] omit = meshtastic/protobuf/*_pb2.py,meshtastic/tests/*.py,meshtastic/test.py [report] exclude_lines = if __name__ == .__main__.: python-2.3.14/.gitattributes000066400000000000000000000001651464266072200160300ustar00rootroot00000000000000* text=auto eol=lf *.{cmd,[cC][mM][dD]} text eol=crlf *.{bat,[bB][aA][tT]} text eol=crlf *.{sh,[sS][hH]} text eol=lf python-2.3.14/.github/000077500000000000000000000000001464266072200144735ustar00rootroot00000000000000python-2.3.14/.github/workflows/000077500000000000000000000000001464266072200165305ustar00rootroot00000000000000python-2.3.14/.github/workflows/ci.yml000066400000000000000000000035641464266072200176560ustar00rootroot00000000000000name: CI on: push: branches: - master pull_request: branches: - master jobs: build: runs-on: ubuntu-latest strategy: matrix: python-version: - "3.8" - "3.9" - "3.10" - "3.11" steps: - uses: actions/checkout@v4 - name: Install Python 3 uses: actions/setup-python@v5 - name: Uninstall meshtastic run: | pip3 uninstall -y meshtastic - name: Install dependencies run: | python -m pip install --upgrade pip pip3 install poetry - name: Install meshtastic from local run: | poetry install poetry run meshtastic --version - name: Run pylint run: poetry run pylint meshtastic examples/ --ignore-patterns ".*_pb2.pyi?$" - name: Check types with mypy run: poetry run mypy meshtastic/ - name: Run tests with pytest run: poetry run pytest --cov=meshtastic - name: Generate coverage report run: | poetry run pytest --cov=meshtastic --cov-report=xml - name: Upload coverage to Codecov uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} env_vars: OS, PYTHON files: ./coverage.xml flags: unittests name: codecov-umbrella fail_ci_if_error: true verbose: true validate: runs-on: ubuntu-latest strategy: matrix: python-version: - "3.8" - "3.9" - "3.10" - "3.11" steps: - uses: actions/checkout@v4 - name: Install Python 3 uses: actions/setup-python@v5 - name: Install meshtastic from local run: | python -m pip install --upgrade pip pip3 install poetry poetry install poetry run meshtastic --version python-2.3.14/.github/workflows/cleanup_artifacts.yml000066400000000000000000000005401464266072200227410ustar00rootroot00000000000000name: Remove old artifacts on: schedule: # Every day at 1am - cron: "0 1 * * *" workflow_dispatch: jobs: remove-old-artifacts: runs-on: ubuntu-latest timeout-minutes: 10 steps: - name: Remove old artifacts uses: c-hive/gha-remove-artifacts@v1 with: age: "1 month" skip-tags: true python-2.3.14/.github/workflows/release.yml000066400000000000000000000136141464266072200207000ustar00rootroot00000000000000name: Make Release 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@v4 - name: Set up Python 3.9 uses: actions/setup-python@v5 with: python-version: "3.9" - name: Install dependencies run: | python -m pip install --upgrade pip pip3 install poetry - name: Bump version run: >- poetry version patch - name: Commit updated version. 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 pyproject.toml 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: >- poetry version --short | sed 's/^/::set-output name=version::/' - name: Create GitHub release uses: actions/create-release@v1 id: create_release with: draft: true prerelease: true release_name: Meshtastic Python ${{ steps.get_version.outputs.version }} tag_name: ${{ steps.get_version.outputs.version }} body: | Autogenerated by github action, developer should edit as required before publishing... env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Build a binary wheel and a source tarball run: >- poetry build - 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@v4 # with: # ref: ${{ needs.release_create.outputs.new_sha }} # - name: Set up Python 3.9 # uses: actions/setup-python@v5 # 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@v4 with: ref: ${{ needs.release_create.outputs.new_sha }} - name: Set up Python 3.9 uses: actions/setup-python@v5 with: python-version: "3.9" - name: Build run: | pip install poetry bin/build-bin.sh - 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@v4 # with: # ref: ${{ needs.release_create.outputs.new_sha }} # - name: Set up Python 3.9 # uses: actions/setup-python@v5 # with: # python-version: "3.9" # - name: Build # run: | # pip install poetry # bin/build-bin.sh # - 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 python-2.3.14/.github/workflows/update_protobufs.yml000066400000000000000000000023761464266072200226500ustar00rootroot00000000000000name: "Update protobufs" on: workflow_dispatch jobs: update-protobufs: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 with: submodules: true - name: Update Submodule run: | git pull --recurse-submodules git submodule update --remote --recursive - name: Download nanopb run: | wget https://jpa.kapsi.fi/nanopb/download/nanopb-0.4.8-linux-x86.tar.gz tar xvzf nanopb-0.4.8-linux-x86.tar.gz mv nanopb-0.4.8-linux-x86 nanopb-0.4.8 - name: Install poetry (needed by regen-protobufs.sh) run: | python -m pip install --upgrade pip pip3 install poetry - name: Re-generate protocol buffers run: | ./bin/regen-protobufs.sh - name: Commit update run: | git config --global user.name 'github-actions' git config --global user.email 'bot@noreply.github.com' git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }} git add protobufs git add meshtastic/protobuf git commit -m "Update protobuf submodule" && git push || echo "No changes to commit" python-2.3.14/.gitignore000066400000000000000000000002561464266072200151260ustar00rootroot00000000000000README docs/ build dist *.egg-info log_* .eggs nanopb-* .*swp .coverage *.py-E venv/ *pyc .DS_Store __pycache__ examples/__pycache__ meshtastic.spec .hypothesis/ coverage.xmlpython-2.3.14/.gitmodules000066400000000000000000000001301464266072200153020ustar00rootroot00000000000000[submodule "protobufs"] path = protobufs url = http://github.com/meshtastic/protobufs python-2.3.14/.pylintrc000066400000000000000000000047261464266072200150110ustar00rootroot00000000000000# pylint configuration file # # Note: "pylint --generate-rcfile" is helpful to see what values to add to this file [MASTER] # Add files or directories matching the regex patterns to the blacklist. The # regex matches against base names, not paths. ignore-patterns=mqtt_pb2.py,channel_pb2.py,telemetry_pb2.py,admin_pb2.py,config_pb2.py,deviceonly_pb2.py,apponly_pb2.py,remote_hardware_pb2.py,portnums_pb2.py,mesh_pb2.py,storeforward_pb2.py,cannedmessages_pb2.py,module_config_pb2.py,localonly_pb2.py,node.py,device_metadata_pb2.py,nanopb_pb2.py [MESSAGES CONTROL] # Disable the message, report, category or checker with the given id(s). You # can either give multiple identifiers separated by comma (,) or put this # option multiple times (only on the command line, not in the configuration # file where it should appear only once).You can also use "--disable=all" to # disable everything first and then reenable specific checks. For example, if # you want to run only the similarities checker, you can use "--disable=all # --enable=similarities". If you want to run only the classes checker, but have # 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,pointless-string-statement,too-few-public-methods,broad-except,no-else-return,no-else-raise,bare-except,too-many-public-methods [BASIC] # Good variable names which should always be accepted, separated by a comma good-names=i,j,k,ex,Run,_ # Bad variable names which should always be refused, separated by a comma bad-names=foo,bar,baz,toto,tutu,tata [FORMAT] # Maximum number of characters on a single line. max-line-length=150 # Maximum number of lines in a module max-module-lines=1600 [REFACTORING] # Maximum number of nested blocks for function / method body max-nested-blocks=10 [MISCELLANEOUS] # List of note tags to take in consideration, separated by a comma. notes=FIXME,fixme,XXX,TODO [SIMILARITIES] # Minimum lines number of a similarity. min-similarity-lines=30 # Ignore comments when computing similarities. ignore-comments=yes # Ignore docstrings when computing similarities. ignore-docstrings=yes # Ignore imports when computing similarities. ignore-imports=yes [DESIGN] # Maximum number of arguments for function / method. max-args=10 # Maximum number of attributes for a class (see R0902). max-attributes=20 python-2.3.14/.reuse/000077500000000000000000000000001464266072200143345ustar00rootroot00000000000000python-2.3.14/.reuse/dep5000066400000000000000000000045201464266072200151150ustar00rootroot00000000000000Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: Meshtastic Python Upstream-Contact: Various Authors Source: https://github.com/meshtastic/python Files: * Copyright: 2020-2020 Nils Werner 2020-2020 Paul Vivier 2020-2020 Tim Gunter 2020-2021 Charles Crossan 2020-2021 IZ1IVA 2020-2022 Jm Casler 2020-2024 geeksville 2021-2021 Andrew Cabey 2021-2021 dylan 2021-2021 Fabian Affolter 2021-2021 Foster Irwin 2021-2021 Manuel Giolo 2021-2021 meehow 2021-2021 srichs 2021-2021 ChuckNorrison 2021-2021 Aivaras-s 2021-2021 a-f-G-U-C 2021-2021 2021-2021 jdstroy 2021-2021 linagee 2021-2021 Simonas 2021-2022 2021-2023 Sacha Weatherstone 2021-2024 Ben Meadors 2022-2022 Balázs Kelemen <10376327+prampec 2022-2022 2022-2022 2022-2022 2022-2022 2022-2022 2022-2022 Rohan King 2022-2022 Tom Douile 2022-2023 Thomas Göttgens 2022-2024 thijs@havinga.eu> 2023-2023 Eli Schleifer 2023-2023 Manuel 2023-2023 Marek Küthe 2023-2023 2023-2023 2023-2023 2023-2023 luzpaz 2023-2023 2023-2023 Toby Murray 2023-2024 Brad Midgley 2024-2024 Ian McEwen 2024-2024 John Hollowell 2024-2024 Jonathan Bennett 2024-2024 2024-2024 2024-2024 2024-2024 2024-2024 rc14193 2024-2024 Steve Holden 2024-2024 Thomas Herrmann 2024-2024 Timothy Harder 2024-2024 Wolfgang Nagele License: GPL-3.0-only python-2.3.14/.trunk/000077500000000000000000000000001464266072200143545ustar00rootroot00000000000000python-2.3.14/.trunk/.gitignore000066400000000000000000000001141464266072200163400ustar00rootroot00000000000000*out *logs *actions *notifications *tools plugins user_trunk.yaml user.yaml python-2.3.14/.trunk/configs/000077500000000000000000000000001464266072200160045ustar00rootroot00000000000000python-2.3.14/.trunk/configs/.isort.cfg000066400000000000000000000000311464266072200176750ustar00rootroot00000000000000[settings] profile=black python-2.3.14/.trunk/configs/.markdownlint.yaml000066400000000000000000000003311464266072200214540ustar00rootroot00000000000000# Autoformatter friendly markdownlint config (all formatting rules disabled) default: true blank_lines: false bullet: false html: false indentation: false line_length: false spaces: false url: false whitespace: false python-2.3.14/.trunk/configs/.shellcheckrc000066400000000000000000000002471464266072200204420ustar00rootroot00000000000000enable=all source-path=SCRIPTDIR disable=SC2154 # If you're having issues with shellcheck following source, disable the errors via: # disable=SC1090 # disable=SC1091 python-2.3.14/.trunk/configs/.yamllint.yaml000066400000000000000000000003551464266072200206020ustar00rootroot00000000000000rules: quoted-strings: required: only-when-needed extra-allowed: ["{|}"] empty-values: forbid-in-block-mappings: true forbid-in-flow-mappings: true key-duplicates: {} octal-values: forbid-implicit-octal: true python-2.3.14/.trunk/configs/ruff.toml000066400000000000000000000002651464266072200176460ustar00rootroot00000000000000# Generic, formatter-friendly config. select = ["B", "D3", "D4", "E", "F"] # Never enforce `E501` (line length violations). This should be handled by formatters. ignore = ["E501"] python-2.3.14/.trunk/trunk.yaml000066400000000000000000000015321464266072200164040ustar00rootroot00000000000000version: 0.1 cli: version: 1.15.0 plugins: sources: - id: trunk ref: v1.2.2 uri: https://github.com/trunk-io/plugins lint: disabled: - bandit ignore: - linters: [ALL] paths: # Ignore generated files - meshtastic/*_pb2.py enabled: - actionlint@1.6.25 - black@23.7.0 - checkov@2.4.9 - git-diff-check - gitleaks@8.18.0 - isort@5.12.0 - markdownlint@0.36.0 - osv-scanner@1.3.6 - prettier@3.0.3 - pylint@2.17.5 - ruff@0.0.287 - shellcheck@0.9.0 - shfmt@3.6.0 - taplo@0.8.1 - trivy@0.44.1 - trufflehog@3.54.3 - yamllint@1.32.0 runtimes: enabled: - go@1.21.0 - node@18.12.1 - python@3.10.8 actions: disabled: - trunk-announce - trunk-check-pre-push - trunk-fmt-pre-commit enabled: - trunk-upgrade-available python-2.3.14/.vscode/000077500000000000000000000000001464266072200144745ustar00rootroot00000000000000python-2.3.14/.vscode/launch.json000066400000000000000000000135401464266072200166440ustar00rootroot00000000000000{ // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "name": "meshtastic BLE", "type": "python", "request": "launch", "module": "meshtastic", "justMyCode": false, "args": ["--ble", "Meshtastic_9f6e"] }, { "name": "meshtastic BLE scan", "type": "python", "request": "launch", "module": "meshtastic", "justMyCode": false, "args": ["--debug", "--ble-scan"] }, { "name": "meshtastic admin", "type": "python", "request": "launch", "module": "meshtastic", "justMyCode": true, "args": ["--debug", "--setch-longslow", "--port", "/dev/ttyUSB1"] }, { "name": "meshtastic tunnel", "type": "python", "request": "launch", "module": "meshtastic", "justMyCode": true, "args": ["--tunnel", "--debug"] }, { "name": "meshtastic set chan", "type": "python", "request": "launch", "module": "meshtastic", "justMyCode": true, "args": ["--setchan", "psk", "0x1a1a1a1a2b2b2b2b1a1a1a1a2b2b2b2b1a1a1a1a2b2b2b2b1a1a1a1a2b2b2b2b", "--debug"] }, { "name": "meshtastic debug", "type": "python", "request": "launch", "module": "meshtastic", "justMyCode": true, "args": ["--debug"] }, { "name": "meshtastic listen", "type": "python", "request": "launch", "module": "meshtastic", "justMyCode": true, "args": ["--listen", "--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 BLE", "type": "python", "request": "launch", "module": "meshtastic", "justMyCode": true, "args": ["--debug", "--ble", "--info"] }, { "name": "meshtastic debug set region", "type": "python", "request": "launch", "module": "meshtastic", "justMyCode": true, "args": ["--debug", "--set", "lora.region", "TW"] }, { "name": "meshtastic debug set bluetooth fixed pin", "type": "python", "request": "launch", "module": "meshtastic", "justMyCode": true, "args": ["--debug", "--set", "bluetooth.fixed_pin", "555555"] }, { "name": "meshtastic debug get bluetooth fixed pin", "type": "python", "request": "launch", "module": "meshtastic", "justMyCode": true, "args": ["--debug", "--get", "bluetooth.fixed_pin"] }, { "name": "meshtastic debug setPref", "type": "python", "request": "launch", "module": "meshtastic", "justMyCode": true, "args": ["--debug", "--set", "power.is_power_saving", "1"] }, { "name": "meshtastic debug setPref telemetry.environment_measurement_enabled", "type": "python", "request": "launch", "module": "meshtastic", "justMyCode": true, "args": ["--debug", "--set", "telemetry.environment_measurement_enabled", "1"] }, { "name": "meshtastic debug setPref telemetry.environment_screen_enabled", "type": "python", "request": "launch", "module": "meshtastic", "justMyCode": true, "args": ["--debug", "--set", "telemetry.environment_screen_enabled", "1"] }, { "name": "meshtastic debug setPref telemetry", "type": "python", "request": "launch", "module": "meshtastic", "justMyCode": true, "args": ["--debug", "--set", "telemetry.environment_measurement_enabled", "1"] }, { "name": "meshtastic setpref", "type": "python", "request": "launch", "module": "meshtastic", "justMyCode": true, "args": ["--debug", "--setchan", "psk", ""] }, { "name": "meshtastic --ch-set", "type": "python", "request": "launch", "module": "meshtastic", "justMyCode": true, "args": ["--debug", "--ch-set", "channel_num", "0", "--ch-index", "0"] }, { "name": "meshtastic seturl", "type": "python", "request": "launch", "module": "meshtastic", "justMyCode": true, "args": ["--seturl", "https://www.meshtastic.org/d/#CgIYAw" ] }, { "name": "meshtastic shell", "type": "python", "request": "launch", "module": "meshtastic", "justMyCode": true, "args": ["--debug", "--seriallog"] }, { "name": "meshtastic test", "type": "python", "request": "launch", "module": "meshtastic", "justMyCode": true, "args": ["--debug", "--test"] }, { "name": "meshtastic settime", "type": "python", "request": "launch", "module": "meshtastic", "justMyCode": true, "args": ["--debug", "--settime"] }, { "name": "meshtastic sendtext", "type": "python", "request": "launch", "module": "meshtastic", "justMyCode": true, "args": ["--debug", "--sendtext", "pytest"] }, { "name": "meshtastic showNodes", "type": "python", "request": "launch", "module": "meshtastic", "justMyCode": true, "args": ["--debug", "--nodes"] } ] } python-2.3.14/.vscode/settings.json000066400000000000000000000002041464266072200172230ustar00rootroot00000000000000{ "cSpell.words": [ "Meshtastic", "TORADIO", "Vids" ], "python.pythonPath": "/usr/bin/python3" }python-2.3.14/LICENSES/000077500000000000000000000000001464266072200143405ustar00rootroot00000000000000python-2.3.14/LICENSES/GPL-3.0-only.txt000066400000000000000000001035621464266072200170070ustar00rootroot00000000000000GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright © 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. “This License” refers to version 3 of the GNU General Public License. “Copyright” also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. “The Program” refers to any copyrightable work licensed under this License. Each licensee is addressed as “you”. “Licensees” and “recipients” may be individuals or organizations. To “modify” a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a “modified version” of the earlier work or a work “based on” the earlier work. A “covered work” means either the unmodified Program or a work based on the Program. To “propagate” a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To “convey” a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays “Appropriate Legal Notices” to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The “source code” for a work means the preferred form of the work for making modifications to it. “Object code” means any non-source form of a work. A “Standard Interface” means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The “System Libraries” of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A “Major Component”, in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The “Corresponding Source” for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to “keep intact all notices”. c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an “aggregate” if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A “User Product” is either (1) a “consumer product”, which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, “normally used” refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. “Installation Information” for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. “Additional permissions” are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered “further restrictions” within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An “entity transaction” is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A “contributor” is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's “contributor version”. A contributor's “essential patent claims” are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, “control” includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a “patent license” is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To “grant” such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. “Knowingly relying” means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is “discriminatory” if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License “or any later version” applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the “copyright” line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an “about box”. You should also get your employer (if you work as a programmer) or school, if any, to sign a “copyright disclaimer” for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . python-2.3.14/MANIFEST.in000066400000000000000000000000221464266072200146630ustar00rootroot00000000000000include README.md python-2.3.14/Makefile000066400000000000000000000016741464266072200146030ustar00rootroot00000000000000# only run the fast unit tests test: pytest -m unit # only run the smoke tests against the virtual device 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 . # generate the docs (for local use) docs: pdoc3 --html -f --output-dir docs meshtastic # lint the codebase lint: pylint meshtastic examples # show the slowest unit tests slow: pytest -m unit --durations=5 protobufs: FORCE git submodule update --init --recursive git pull --rebase git submodule update --remote --merge ./bin/regen-protobufs.sh # run the coverage report and open results in a browser cov: pytest --cov-report html --cov=meshtastic # on mac, this will open the coverage report in a browser open htmlcov/index.html # run cli examples examples: FORCE pytest -mexamples # Makefile hack to get the examples to always run FORCE: ; python-2.3.14/README.md000066400000000000000000000061241464266072200144150ustar00rootroot00000000000000# Meshtastic Python [![codecov](https://codecov.io/gh/meshtastic/python/branch/master/graph/badge.svg?token=TIWPJL73KV)](https://codecov.io/gh/meshtastic/python) ![PyPI - Downloads](https://img.shields.io/pypi/dm/meshtastic) [![CI](https://img.shields.io/github/actions/workflow/status/meshtastic/python/ci.yml?branch=master&label=actions&logo=github&color=yellow)](https://github.com/meshtastic/python/actions/workflows/ci.yml) [![CLA assistant](https://cla-assistant.io/readme/badge/meshtastic/python)](https://cla-assistant.io/meshtastic/python) [![Fiscal Contributors](https://opencollective.com/meshtastic/tiers/badge.svg?label=Fiscal%20Contributors&color=deeppink)](https://opencollective.com/meshtastic/) ![GPL-3.0](https://img.shields.io/badge/License-GPL%20v3-blue.svg) ## Overview 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. **[Getting Started Guide](https://meshtastic.org/docs/software/python/cli/installation)** (Documentation/API Reference is currently offline) ## Call for Contributors This library and CLI has gone without a consistent maintainer for a while, and there's many improvements that could be made. We're all volunteers here and help is extremely appreciated, whether in implementing your own needs or helping maintain the library and CLI in general. If you're interested in contributing but don't have specific things you'd like to work on, look at the roadmap below! ## Roadmap This should always be considered a list in progress and flux -- inclusion doesn't guarantee implementation, and exclusion doesn't mean something's not wanted. GitHub issues are a great place to discuss ideas. * Types * type annotations throughout the codebase, and upgrading mypy running in CI to `--strict` * async-friendliness * CLI completeness & consistency * the CLI should support all features of the firmware * there should be a consistent output format available for shell scripting * CLI input validation & documentation * what arguments and options are compatible & incompatible with one another? * can the options be restructured in a way that is more self-documenting? * pubsub events should be documented clearly * helpers for third-party code * it should be easy to write a script that supports similar options to the CLI so many tools support the same ways of connecting to nodes * data storage & processing * there should be a standardized way of recording packets for later use, debugging, etc. * a persistence layer could also keep track of nodes beyond nodedb, as the apps do * a sqlite database schema and tools for writing to it may be a good starting point * enable maps, charts, visualizations ## Stats ![Alt](https://repobeats.axiom.co/api/embed/c71ee8fc4a79690402e5d2807a41eec5e96d9039.svg "Repobeats analytics image") python-2.3.14/TODO.md000066400000000000000000000026721464266072200142310ustar00rootroot00000000000000# TODO Basic functionality is complete now. ## Eventual tasks - Improve documentation on properties/fields - include more examples: textchat.py, replymessage.py all as one little demo - possibly use tk to make a multiwindow test console: https://stackoverflow.com/questions/12351786/how-to-redirect-print-statements-to-tkinter-text-widget ## MeshtasticShell todos - Possibly use multiple windows: https://stackoverflow.com/questions/12351786/how-to-redirect-print-statements-to-tkinter-text-widget - make pingpong test ## Bluetooth support - ./bin/run.sh --ble-scan # To look for Meshtastic devices - ./bin/run.sh --ble 24:62:AB:DD:DF:3A --info ## Done - DONE use port enumeration to find ports https://pyserial.readthedocs.io/en/latest/shortintro.html - DONE make serial debug output optional (by providing a null stream) - DONE make pubsub work - DONE make docs decent - DONE keep everything in dicts - DONE have device send a special packet at boot so the serial client can detect if it rebooted - DONE add fromId and toId to received messages dictionaries - make command line options for displaying/changing config - update nodedb as nodes change - 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 python-2.3.14/bin/000077500000000000000000000000001464266072200137035ustar00rootroot00000000000000python-2.3.14/bin/build-bin.sh000077500000000000000000000002741464266072200161120ustar00rootroot00000000000000#!/bin/bash set -e echo Building ubuntu binary poetry install source $(poetry env info --path)/bin/activate pyinstaller -F -n meshtastic --collect-all meshtastic meshtastic/__main__.py python-2.3.14/bin/git-resolve-poetry-lock.sh000077500000000000000000000011611464266072200207470ustar00rootroot00000000000000#!/usr/bin/env bash set -e # This is a little helper you can use to resolve git merge conflicts in poetry.lock # with minimal changes vs the requested lib versions # Based on this article with a good description of best practices: # https://www.peterbe.com/plog/how-to-resolve-a-git-conflict-in-poetry.lock echo "Resolving poetry.lock merge conflicts, you'll need to run git commit yourself..." # Get poetry.lock to look like it does in master git checkout --theirs poetry.lock # Rewrite the lock file poetry lock --no-update git add poetry.lock # Update your poetry env to match the new merged lock file poetry install python-2.3.14/bin/prerelease-tests.sh000077500000000000000000000014271464266072200175350ustar00rootroot00000000000000set -e # You may consider running: "pytest -m smoke1" instead of this test. echo "Running (crude) prerelease tests to verify sanity" # Use the python environment created by poetry source $(poetry env info --path)/bin/activate echo running hello python3 tests/hello_world.py # meshtastic --help echo toggling router meshtastic --set is_router true meshtastic --set is_router false # TODO: This does not seem to work. echo setting channel meshtastic --seturl "https://www.meshtastic.org/c/#GAMiENTxuzogKQdZ8Lz_q89Oab8qB0RlZmF1bHQ=" echo setting owner meshtastic --set-owner "Test Build" echo setting position meshtastic --setlat 32.7767 --setlon -96.7970 --setalt 1337 echo dumping info meshtastic run meshtastic --info echo sending closing message meshtastic --sendtext "Sanity complete" python-2.3.14/bin/regen-docs.sh000077500000000000000000000003201464266072200162630ustar00rootroot00000000000000# Note: Docs are generated from this command below, albeit from Vercel. # The docs/ dir is not used and is no longer committed. # see sachaw if you have questions pdoc3 --html -f --output-dir docs meshtastic python-2.3.14/bin/regen-protobufs.sh000077500000000000000000000031321464266072200173620ustar00rootroot00000000000000#!/bin/bash set -e #Uncomment to run hack #gsed -i 's/import "\//import ".\//g' ./protobufs/meshtastic/* #gsed -i 's/package meshtastic;//g' ./protobufs/meshtastic/* # protoc looks for mypy plugin in the python path source $(poetry env info --path)/bin/activate # Put our temp files in the poetry build directory TMPDIR=./build/meshtastic/protofixup echo "Fixing up protobuf paths in ${TMPDIR} temp directory" # Ensure a clean build [ -e "${TMPDIR}" ] && rm -r "${TMPDIR}" INDIR=${TMPDIR}/in/meshtastic/protobuf OUTDIR=${TMPDIR}/out PYIDIR=${TMPDIR}/out mkdir -p "${OUTDIR}" "${INDIR}" "${PYIDIR}" cp ./protobufs/meshtastic/*.proto "${INDIR}" # OS-X sed is apparently a little different and expects an arg for -i if [[ $OSTYPE == 'darwin'* ]]; then SEDCMD="sed -i '' -E" else SEDCMD="sed -i -E" fi # change the package names to meshtastic.protobuf $SEDCMD 's/^package meshtastic;/package meshtastic.protobuf;/' "${INDIR}/"*.proto # fix the imports to match $SEDCMD 's/^import "meshtastic\//import "meshtastic\/protobuf\//' "${INDIR}/"*.proto # Generate the python files ./nanopb-0.4.8/generator-bin/protoc -I=$TMPDIR/in --python_out "${OUTDIR}" "--mypy_out=${PYIDIR}" $INDIR/*.proto # Change "from meshtastic.protobuf import" to "from . import" $SEDCMD 's/^from meshtastic.protobuf import/from . import/' "${OUTDIR}"/meshtastic/protobuf/*pb2*.py[i] # Create a __init__.py in the out directory touch "${OUTDIR}/meshtastic/protobuf/__init__.py" # Copy to the source controlled tree mkdir -p meshtastic/protobuf rm -rf meshtastic/protobuf/*pb2*.py cp "${OUTDIR}/meshtastic/protobuf"/* meshtastic/protobuf exit 0 python-2.3.14/bin/run-ci-local.sh000077500000000000000000000006601464266072200165310ustar00rootroot00000000000000#!/bin/bash # This script lets you run github ci actions locally # You need to have act installed. You can get it at https://nektosact.com/ # by default it simulates a push event # other useful options # -j build-and-publish-ubuntu # also: we only run one of the 4 matrix tests, because otherwise it absolutely hammers the CPU (so many containers and threads) act -P ubuntu-latest=-self-hosted --matrix "python-version:3.8" "$@"python-2.3.14/bin/test-release.sh000077500000000000000000000003251464266072200166370ustar00rootroot00000000000000rm dist/* set -e bin/regen-docs.sh pandoc --from=markdown --to=rst --output=README README.md poetry publish -r test-pypi --build echo "view the upload at https://test.pypi.org/ it it looks good upload for real" python-2.3.14/bin/upload-release.sh000077500000000000000000000002051464266072200171410ustar00rootroot00000000000000rm dist/* set -e poetry build poetry run pytest poetry publish #python3 setup.py sdist bdist_wheel #python3 -m twine upload dist/* python-2.3.14/exampleConfig.yaml000066400000000000000000000004351464266072200166020ustar00rootroot00000000000000# example config using camelCase keys owner: Bob TBeam ownerShort: BOB channelUrl: https://www.meshtastic.org/d/#CgUYAyIBAQ location: lat: 35.88888 lon: -93.88888 alt: 304 userPrefs: region: 1 isAlwaysPowered: "true" screenOnSecs: 31536000 waitBluetoothSecs: 31536000 python-2.3.14/example_config.yaml000066400000000000000000000014601464266072200170000ustar00rootroot00000000000000# example configuration file with snake_case keys owner: Bob TBeam owner_short: BOB channel_url: https://www.meshtastic.org/e/#CgMSAQESCDgBQANIAVAe location: lat: 35.88888 lon: -93.88888 alt: 304 config: bluetooth: enabled: true fixedPin: 123456 device: serialEnabled: true display: screenOnSecs: 600 lora: region: US hopLimit: 3 txEnabled: true txPower: 30 network: ntpServer: 0.pool.ntp.org position: gpsAttemptTime: 900 gpsEnabled: true gpsUpdateInterval: 120 positionBroadcastSecs: 900 positionBroadcastSmartEnabled: true positionFlags: 3 power: lsSecs: 300 meshSdsTimeoutSecs: 7200 minWakeSecs: 10 sdsSecs: 4294967295 module_config: telemetry: deviceUpdateInterval: 900 environmentUpdateInterval: 900 python-2.3.14/examples/000077500000000000000000000000001464266072200147515ustar00rootroot00000000000000python-2.3.14/examples/get_hw.py000066400000000000000000000010151464266072200165750ustar00rootroot00000000000000"""Simple program to demo how to use meshtastic library. To run: python examples/get_hw.py """ import sys import meshtastic import meshtastic.serial_interface # simple arg check if len(sys.argv) != 1: print(f"usage: {sys.argv[0]}") print("Print the hardware model for the local node.") sys.exit(3) iface = meshtastic.serial_interface.SerialInterface() if iface.nodes: for n in iface.nodes.values(): if n["num"] == iface.myInfo.my_node_num: print(n["user"]["hwModel"]) iface.close() python-2.3.14/examples/hello_world_serial.py000066400000000000000000000007351464266072200212010ustar00rootroot00000000000000"""Simple program to demo how to use meshtastic library. To run: python examples/hello_world_serial.py """ import sys import meshtastic import meshtastic.serial_interface # simple arg check if len(sys.argv) < 2: print(f"usage: {sys.argv[0]} message") sys.exit(3) # By default will try to find a meshtastic device, # otherwise provide a device path like /dev/ttyUSB0 iface = meshtastic.serial_interface.SerialInterface() iface.sendText(sys.argv[1]) iface.close() python-2.3.14/examples/info_example.py000066400000000000000000000007151464266072200177740ustar00rootroot00000000000000"""Simple program to demo how to use meshtastic library. To run: python examples/info.py """ import meshtastic import meshtastic.serial_interface iface = meshtastic.serial_interface.SerialInterface() # call showInfo() just to ensure values are populated # info = iface.showInfo() if iface.nodes: for n in iface.nodes.values(): if n["num"] == iface.myInfo.my_node_num: print(n["user"]["hwModel"]) break iface.close() python-2.3.14/examples/pub_sub_example.py000066400000000000000000000013011464266072200204700ustar00rootroot00000000000000"""Simple program to demo how to use meshtastic library. To run: python examples/pub_sub_example.py """ import sys from pubsub import pub import meshtastic import meshtastic.tcp_interface # simple arg check if len(sys.argv) < 2: print(f"usage: {sys.argv[0]} host") sys.exit(1) def onConnection(interface, topic=pub.AUTO_TOPIC): # pylint: disable=unused-argument """This is called when we (re)connect to the radio.""" print(interface.myInfo) interface.close() pub.subscribe(onConnection, "meshtastic.connection.established") try: iface = meshtastic.tcp_interface.TCPInterface(sys.argv[1]) except: print(f"Error: Could not connect to {sys.argv[1]}") sys.exit(1) python-2.3.14/examples/pub_sub_example2.py000066400000000000000000000020201464266072200205510ustar00rootroot00000000000000"""Simple program to demo how to use meshtastic library. To run: python examples/pub_sub_example2.py """ import sys import time from pubsub import pub import meshtastic import meshtastic.tcp_interface # simple arg check if len(sys.argv) < 2: print(f"usage: {sys.argv[0]} host") sys.exit(1) def onReceive(packet, interface): # pylint: disable=unused-argument """called when a packet arrives""" print(f"Received: {packet}") def onConnection(interface, topic=pub.AUTO_TOPIC): # pylint: disable=unused-argument """called when we (re)connect to the radio""" # defaults to broadcast, specify a destination ID if you wish interface.sendText("hello mesh") pub.subscribe(onReceive, "meshtastic.receive") pub.subscribe(onConnection, "meshtastic.connection.established") try: iface = meshtastic.tcp_interface.TCPInterface(hostname=sys.argv[1]) while True: time.sleep(1000) iface.close() except Exception as ex: print(f"Error: Could not connect to {sys.argv[1]} {ex}") sys.exit(1) python-2.3.14/examples/scan_for_devices.py000066400000000000000000000013021464266072200206130ustar00rootroot00000000000000"""Program to scan for hardware To run: python examples/scan_for_devices.py """ import sys from meshtastic.util import ( active_ports_on_supported_devices, detect_supported_devices, get_unique_vendor_ids, ) # simple arg check if len(sys.argv) != 1: print(f"usage: {sys.argv[0]}") print("Detect which device we might have.") sys.exit(3) vids = get_unique_vendor_ids() print(f"Searching for all devices with these vendor ids {vids}") sds = detect_supported_devices() if len(sds) > 0: print("Detected possible devices:") for d in sds: print(f" name:{d.name}{d.version} firmware:{d.for_firmware}") ports = active_ports_on_supported_devices(sds) print(f"ports:{ports}") python-2.3.14/examples/set_owner.py000066400000000000000000000007671464266072200173420ustar00rootroot00000000000000"""Simple program to demo how to use meshtastic library. To run: python examples/set_owner.py Bobby 333 """ import sys import meshtastic import meshtastic.serial_interface # simple arg check if len(sys.argv) < 2: print(f"usage: {sys.argv[0]} long_name [short_name]") sys.exit(3) iface = meshtastic.serial_interface.SerialInterface() long_name = sys.argv[1] short_name = None if len(sys.argv) > 2: short_name = sys.argv[2] iface.localNode.setOwner(long_name, short_name) iface.close() python-2.3.14/examples/show_ports.py000066400000000000000000000001471464266072200175340ustar00rootroot00000000000000"""Simple program to show serial ports. """ from meshtastic.util import findPorts print(findPorts()) python-2.3.14/examples/tcp_gps_example.py000066400000000000000000000006751464266072200205050ustar00rootroot00000000000000"""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() python-2.3.14/extra/000077500000000000000000000000001464266072200142565ustar00rootroot00000000000000python-2.3.14/extra/meshtastic_tun.rules000066400000000000000000000004341464266072200203650ustar00rootroot00000000000000# for userspace access to the TUN network interface by meshtastic. Add your user to the netdev group. # gives access via /dev/net/tun permissions # install via sudo cp meshtastic_tun.rules /etc/udev/rules.d/ KERNEL=="tun", GROUP="netdev", MODE="0660", OPTIONS+="static_node=net/tun" python-2.3.14/info/000077500000000000000000000000001464266072200140665ustar00rootroot00000000000000python-2.3.14/info/mac/000077500000000000000000000000001464266072200146265ustar00rootroot00000000000000python-2.3.14/info/mac/heltec.txt000066400000000000000000000012261464266072200166340ustar00rootroot00000000000000ioreg -p IOUSB > /tmp/d > | +-o CP2102 USB to UART Bridge Controller@14400000 system_profiler SPUSBDataType > /tmp/b 37a38,50 > CP2102 USB to UART Bridge Controller: > > Product ID: 0xea60 > Vendor ID: 0x10c4 (Silicon Laboratories, Inc.) > Version: 1.00 > Serial Number: 0001 > Speed: Up to 12 Mb/s > Manufacturer: Silicon Labs > Location ID: 0x14400000 / 53 > Current Available (mA): 500 > Current Required (mA): 100 > Extra Operating Current (mA): 0 python-2.3.14/info/mac/nano_g1.txt000066400000000000000000000013261464266072200167130ustar00rootroot00000000000000meshtastic detected port: /dev/cu.wchusbserial53820208781 ioreg -p IOUSB shows this: | +-o USB Single Serial@14300000 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 python-2.3.14/info/mac/rak11200.txt000066400000000000000000000030621464266072200165310ustar00rootroot00000000000000 % 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 --- > +-o AppleUSBXHCI Root Hub Simulation@14000000 18d17 < +-o USB Serial@14300000 % diff /tmp/b /tmp/c 13c13 < +-o AppleUSBXHCI Root Hub Simulation@14000000 --- > +-o AppleUSBXHCI Root Hub Simulation@14000000 18d17 < +-o USB Serial@14300000 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 python-2.3.14/info/mac/rak4631_19003.txt000066400000000000000000000040471464266072200172230ustar00rootroot00000000000000 > | +-o WisCore RAK4631 Board@14400000 /dev/cu.usbmodem14401 % ls -al /dev/*modem* crw-rw-rw- 1 root wheel 0x9000005 Jan 29 15:32 /dev/cu.usbmodem14401 crw-rw-rw- 1 root wheel 0x9000004 Jan 29 15:31 /dev/tty.usbmodem14401 Note: On a Mac Air, output is: % system_profiler SPUSBDataType USB: USB 3.1 Bus: Host Controller Driver: AppleT8103USBXHCI USB 3.1 Bus: Host Controller Driver: AppleT8103USBXHCI WisCore RAK4631 Board: Product ID: 0x8029 Vendor ID: 0x239a Version: 1.00 Serial Number: E6CF9502B1D410D8 Speed: Up to 12 Mb/s Manufacturer: RAKwireless Location ID: 0x01100000 / 2 Current Available (mA): 500 Current Required (mA): 100 Extra Operating Current (mA): 0 However, in FTHR840BOOT mode, it shows this: % system_profiler SPUSBDataType USB: USB 3.1 Bus: Host Controller Driver: AppleT8103USBXHCI USB 3.1 Bus: Host Controller Driver: AppleT8103USBXHCI Feather nRF52840 Express: Product ID: 0x0029 Vendor ID: 0x239a Version: 1.00 Serial Number: E6CF9502B1D410D8 Speed: Up to 12 Mb/s Manufacturer: Adafruit Industries Location ID: 0x01100000 / 1 Current Available (mA): 500 Current Required (mA): 100 Extra Operating Current (mA): 0 Media: nRF UF2: Capacity: 33.7 MB (33,690,112 bytes) Removable Media: Yes BSD Name: disk4 Logical Unit: 0 Partition Map Type: Unknown S.M.A.R.T. status: Verified USB Interface: 2 $ cat /Volumes/FTHR840BOOT/INFO_UF2.TXT UF2 Bootloader 0.3.2-109-gd6b28e6-dirty lib/nrfx (v2.0.0) lib/tinyusb (0.6.0-272-g4e6aa0d8) lib/uf2 (heads/master) Model: Adafruit Feather nRF52840 Express Board-ID: nRF52840-Feather-revD SoftDevice: S140 version 6.1.1 Date: Jun 16 2020 python-2.3.14/info/mac/rak4631_5005.txt000066400000000000000000000011751464266072200171370ustar00rootroot00000000000000 no device plugged in % ioreg -p IOUSB > /tmp/a device plugged in, in "boot" mode % ioreg -p IOUSB > /tmp/b device plugged in, botted to Meshtastic firmware % ioreg -p IOUSB > /tmp/c (venv) sweet Meshtastic-python % diff /tmp/a /tmp/b (with most info removed) > | +-o Feather nRF52840 Express@14400000 diff /tmp/a /tmp/c (with most info removed) > | +-o WisCore RAK4631 Board@14400000 Meshtastic detected port on /dev/cu.usbmodem14401 python-2.3.14/info/mac/tbeam.txt000066400000000000000000000011411464266072200164540ustar00rootroot00000000000000 meshtastic detected port: /dev/cu.usbmodem53230050571 ioreg -p IOUSB > /tmp/c > | +-o USB Single Serial@14400000 system_profiler SPUSBDataType > /tmp/a > USB Single Serial: > > Product ID: 0x55d4 > Vendor ID: 0x1a86 > Version: 4.43 > Serial Number: 5323005057 > Speed: Up to 12 Mb/s > Location ID: 0x14400000 / 50 > Current Available (mA): 500 > Current Required (mA): 134 > Extra Operating Current (mA): 0 python-2.3.14/info/mac/techo.txt000066400000000000000000000023021464266072200164660ustar00rootroot00000000000000 in boot mode: % mount /dev/disk122 on /Volumes/TECHOBOOT (msdos, local, nodev, nosuid, noowners) % ls -al /Volumes/TECHOBOOT total 3735 drwxrwxrwx@ 1 bob staff 2048 Feb 1 16:47 . drwxr-xr-x 5 root wheel 160 Feb 1 16:47 .. drwxrwxrwx 1 bob staff 512 Feb 1 16:47 .fseventsd -rwxrwxrwx 1 bob staff 1908736 Oct 13 08:37 CURRENT.UF2 -rwxrwxrwx 1 bob staff 129 Oct 13 08:37 INDEX.HTM -rwxrwxrwx 1 bob staff 237 Oct 13 08:37 INFO_UF2.TXT # nothing plugged in % ioreg -p IOUSB > /tmp/a # not boot mode % ioreg -p IOUSB > /tmp/b # bootmode % ioreg -p IOUSB > /tmp/c % diff /tmp/a /tmp/b > | +-o TTGO_eink@14300000 % diff /tmp/a /tmp/c > | +-o T-Echo v1@14300000 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 python-2.3.14/info/mac/tlora.txt000066400000000000000000000010001464266072200164770ustar00rootroot00000000000000diff of ioreg -p IOUSB > | +-o USB Single Serial@14300000 Diff of system_profiler SPUSBDataType < USB Single Serial: < < Product ID: 0x55d4 < Vendor ID: 0x1a86 < Version: 4.43 < Speed: Up to 12 Mb/s < Location ID: 0x14300000 / 46 < Current Available (mA): 500 < Current Required (mA): 134 < Extra Operating Current (mA): 0 python-2.3.14/info/mac/tlora_2.1.6.txt000066400000000000000000000066511464266072200172440ustar00rootroot00000000000000lsusb Bus 001 Device 001: ID 0bda:2172 Realtek Semiconductor Corp. BillBoard Device Serial: 00000000000000000 Bus 000 Device 002: ID 2109:0817 VIA Labs, Inc. USB3.0 Hub Serial: 000000000 Bus 000 Device 003: ID 2109:0715 VIA Labs, Inc. VLI Product String Serial: 000000075003 Bus 000 Device 004: ID 0bda:0306 Realtek Semiconductor Corp. USB3.0-CRW Serial: 60000719201300000 Bus 000 Device 005: ID 2109:0817 VIA Labs, Inc. USB3.0 Hub Serial: 000000000 Bus 000 Device 001: ID 2109:2817 VIA Labs, Inc. USB2.0 Hub Serial: 000000000 Bus 000 Device 009: ID 1a86:55d4 1a86 USB Single Serial Serial: 533C005215 Bus 000 Device 006: ID 2109:2817 VIA Labs, Inc. USB2.0 Hub Serial: 000000000 Bus 000 Device 007: ID 2109:8817 VIA Labs, Inc. USB Billboard Device Serial: 0000000000000001 Bus 000 Device 008: ID 2109:8817 VIA Labs, Inc. USB Billboard Device Serial: 0000000000000001 Bus 002 Device 001: ID 1a40:0101 TERMINUS TECHNOLOGY INC. USB 2.0 Hub Bus 002 Device 003: ID 0922:001f Dymo Corporation DYMO LabelWriter 4XL Serial: 17032316350940 Bus 002 Device 002: ID 046d:082d Logitech Inc. HD Pro Webcam C920 Serial: A21C905F Bus 000 Device 000: ID 0bda:2172 Realtek Semiconductor Corp. USB 3.1 Bus Bus 000 Device 000: ID 2109:0817 VIA Labs, Inc. USB 3.1 Bus Bus 000 Device 001: ID 1d6b:1100 Linux Foundation USB 3.0 Bus % lsusb -v (with parts snipped) USB2.0 Hub : Product ID: 0x2817 Vendor ID: 0x2109 (VIA Labs, Inc.) Version: 6.03 Serial Number: 000000000 Speed: Up to 480 Mb/s Manufacturer: VIA Labs, Inc. Location ID: 0x00100000 / 1 Current Available (mA): 500 Current Required (mA): 0 Extra Operating Current (mA): 0 USB Single Serial: Product ID: 0x55d4 Vendor ID: 0x1a86 Version: 4.43 Serial Number: 533C005215 Speed: Up to 12 Mb/s Location ID: 0x00140000 / 9 Current Available (mA): 500 Current Required (mA): 134 Extra Operating Current (mA): 0 USB2.0 Hub : Product ID: 0x2817 Vendor ID: 0x2109 (VIA Labs, Inc.) Version: 6.03 Serial Number: 000000000 Speed: Up to 480 Mb/s Manufacturer: VIA Labs, Inc. Location ID: 0x00110000 / 6 Current Available (mA): 500 Current Required (mA): 0 Extra Operating Current (mA): 0 USB Billboard Device : Product ID: 0x8817 Vendor ID: 0x2109 (VIA Labs, Inc.) Version: 0.01 Serial Number: 0000000000000001 Speed: Up to 480 Mb/s Manufacturer: VIA Labs, Inc. Location ID: 0x00115000 / 7 Current Available (mA): 500 Current Required (mA): 100 Extra Operating Current (mA): 0 USB Billboard Device : Product ID: 0x8817 Vendor ID: 0x2109 (VIA Labs, Inc.) Version: 0.01 Serial Number: 0000000000000001 Speed: Up to 480 Mb/s Manufacturer: VIA Labs, Inc. Location ID: 0x00150000 / 8 Current Available (mA): 500 Current Required (mA): 100 Extra Operating Current (mA): 0 python-2.3.14/info/readme.txt000066400000000000000000000001561464266072200160660ustar00rootroot00000000000000Gathering OS-level info for devices. This info might be helpful for developers detecting info about devices. python-2.3.14/info/ubuntu/000077500000000000000000000000001464266072200154105ustar00rootroot00000000000000python-2.3.14/info/ubuntu/diy.txt000066400000000000000000000043631464266072200167440ustar00rootroot00000000000000lsusb Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub Bus 001 Device 004: ID 10c4:ea60 Silicon Labs CP210x UART Bridge Bus 001 Device 003: ID 0bda:2838 Realtek Semiconductor Corp. RTL2838 DVB-T Bus 001 Device 002: ID 2109:3431 VIA Labs, Inc. Hub Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub lsusb -d 10c4: -v Bus 001 Device 004: ID 10c4:ea60 Silicon Labs CP210x UART Bridge Couldn't open device, some information will be missing Device Descriptor: bLength 18 bDescriptorType 1 bcdUSB 2.00 bDeviceClass 0 bDeviceSubClass 0 bDeviceProtocol 0 bMaxPacketSize0 64 idVendor 0x10c4 Silicon Labs idProduct 0xea60 CP210x UART Bridge bcdDevice 1.00 iManufacturer 1 iProduct 2 iSerial 3 bNumConfigurations 1 Configuration Descriptor: bLength 9 bDescriptorType 2 wTotalLength 0x0020 bNumInterfaces 1 bConfigurationValue 1 iConfiguration 0 bmAttributes 0x80 (Bus Powered) MaxPower 100mA Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 0 bAlternateSetting 0 bNumEndpoints 2 bInterfaceClass 255 Vendor Specific Class bInterfaceSubClass 0 bInterfaceProtocol 0 iInterface 2 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x01 EP 1 OUT bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0040 1x 64 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 python-2.3.14/info/ubuntu/heltec_v2.txt000066400000000000000000000047111464266072200200270ustar00rootroot00000000000000Run on Ubuntu 20 Command: lsusb -d 10c4: -v Output: Bus 001 Device 091: ID 10c4:ea60 Silicon Labs CP210x UART Bridge Device Descriptor: bLength 18 bDescriptorType 1 bcdUSB 1.10 bDeviceClass 0 bDeviceSubClass 0 bDeviceProtocol 0 bMaxPacketSize0 64 idVendor 0x10c4 Silicon Labs idProduct 0xea60 CP210x UART Bridge bcdDevice 1.00 iManufacturer 1 iProduct 2 iSerial 3 bNumConfigurations 1 Configuration Descriptor: bLength 9 bDescriptorType 2 wTotalLength 0x0020 bNumInterfaces 1 bConfigurationValue 1 iConfiguration 0 bmAttributes 0x80 (Bus Powered) MaxPower 100mA Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 0 bAlternateSetting 0 bNumEndpoints 2 bInterfaceClass 255 Vendor Specific Class bInterfaceSubClass 0 bInterfaceProtocol 0 iInterface 2 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x81 EP 1 IN bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0040 1x 64 bytes bInterval 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x01 EP 1 OUT bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0040 1x 64 bytes bInterval 0 Command: esptool.py chip_id Output: esptool.py v3.2 Found 3 serial ports Serial port /dev/ttyUSB0 Connecting.... Detecting chip type... Unsupported detection protocol, switching and trying again... Connecting..... Detecting chip type... ESP32 Chip is ESP32-D0WDQ6 (revision 1) Features: WiFi, BT, Dual Core, 240MHz, VRef calibration in efuse, Coding Scheme None Crystal is 40MHz MAC: 24:0a:c4:fc:be:f0 Uploading stub... Running stub... Stub running... Warning: ESP32 has no Chip ID. Reading MAC instead. MAC: 24:0a:c4:fc:be:f0 Hard resetting via RTS pin... python-2.3.14/info/ubuntu/nano_g1.txt000066400000000000000000000057131464266072200175010ustar00rootroot00000000000000lsusb -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 python-2.3.14/info/ubuntu/rak4631_19003.txt000066400000000000000000000075521464266072200200110ustar00rootroot00000000000000 Note: Meshtastic firmware was installed when running these commands $ ls -al /dev/ttyACM* crw-rw---- 1 root dialout 166, 0 Jan 29 21:50 /dev/ttyACM0 lsusb -d 239a: -v Bus 001 Device 097: ID 239a:8029 Couldn't open device, some information will be missing Device Descriptor: bLength 18 bDescriptorType 1 bcdUSB 2.00 bDeviceClass 239 Miscellaneous Device bDeviceSubClass 2 bDeviceProtocol 1 Interface Association bMaxPacketSize0 64 idVendor 0x239a idProduct 0x8029 bcdDevice 1.00 iManufacturer 1 iProduct 2 iSerial 3 bNumConfigurations 1 Configuration Descriptor: bLength 9 bDescriptorType 2 wTotalLength 0x004b bNumInterfaces 2 bConfigurationValue 1 iConfiguration 0 bmAttributes 0xa0 (Bus Powered) Remote Wakeup MaxPower 100mA Interface Association: bLength 8 bDescriptorType 11 bFirstInterface 0 bInterfaceCount 2 bFunctionClass 2 Communications bFunctionSubClass 2 Abstract (modem) bFunctionProtocol 0 iFunction 0 Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 0 bAlternateSetting 0 bNumEndpoints 1 bInterfaceClass 2 Communications bInterfaceSubClass 2 Abstract (modem) bInterfaceProtocol 0 iInterface 4 CDC Header: bcdCDC 1.20 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 0x81 EP 1 IN bmAttributes 3 Transfer Type Interrupt Synch Type None Usage Type Data wMaxPacketSize 0x0008 1x 8 bytes bInterval 16 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 0x01 EP 1 OUT bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0040 1x 64 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 $ lsusb Bus 002 Device 005: ID 046d:c31c Logitech, Inc. Keyboard K120 Bus 002 Device 002: ID 8087:8000 Intel Corp. Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub Bus 001 Device 097: ID 239a:8029 Bus 001 Device 002: ID 8087:8008 Intel Corp. Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub Note: esptool.py chip_id does not detect device python-2.3.14/info/ubuntu/rak4631_5005.txt000066400000000000000000000116311464266072200177170ustar00rootroot00000000000000 Note: Device has Meshtastic firmware installed. $ ls -al /dev/ttyACM* crw-rw---- 1 root dialout 166, 0 Jan 29 21:44 /dev/ttyACM0 $ lsusb -d 239a: -v Bus 001 Device 098: ID 239a:0029 Couldn't open device, some information will be missing Device Descriptor: bLength 18 bDescriptorType 1 bcdUSB 2.00 bDeviceClass 239 Miscellaneous Device bDeviceSubClass 2 bDeviceProtocol 1 Interface Association bMaxPacketSize0 64 idVendor 0x239a idProduct 0x0029 bcdDevice 1.00 iManufacturer 1 iProduct 2 iSerial 3 bNumConfigurations 1 Configuration Descriptor: bLength 9 bDescriptorType 2 wTotalLength 0x0062 bNumInterfaces 3 bConfigurationValue 1 iConfiguration 0 bmAttributes 0xa0 (Bus Powered) Remote Wakeup MaxPower 100mA Interface Association: bLength 8 bDescriptorType 11 bFirstInterface 0 bInterfaceCount 2 bFunctionClass 2 Communications bFunctionSubClass 2 Abstract (modem) bFunctionProtocol 0 iFunction 0 Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 0 bAlternateSetting 0 bNumEndpoints 1 bInterfaceClass 2 Communications bInterfaceSubClass 2 Abstract (modem) bInterfaceProtocol 0 iInterface 4 CDC Header: bcdCDC 1.20 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 0x81 EP 1 IN bmAttributes 3 Transfer Type Interrupt Synch Type None Usage Type Data wMaxPacketSize 0x0008 1x 8 bytes bInterval 16 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 0x0040 1x 64 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 Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 2 bAlternateSetting 0 bNumEndpoints 2 bInterfaceClass 8 Mass Storage bInterfaceSubClass 6 SCSI bInterfaceProtocol 80 Bulk-Only iInterface 5 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x03 EP 3 OUT bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0040 1x 64 bytes bInterval 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x83 EP 3 IN bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0040 1x 64 bytes bInterval 0 $ lsusb Bus 002 Device 005: ID 046d:c31c Logitech, Inc. Keyboard K120 Bus 002 Device 002: ID 8087:8000 Intel Corp. Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub Bus 001 Device 098: ID 239a:0029 Bus 001 Device 002: ID 8087:8008 Intel Corp. Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub Note: esptool.py chip_id does not detect device python-2.3.14/info/ubuntu/readme.txt000066400000000000000000000000231464266072200174010ustar00rootroot00000000000000info run on ubuntu python-2.3.14/info/ubuntu/tbeam.txt000066400000000000000000000057051464266072200172500ustar00rootroot00000000000000Run on Ubuntu 20 Command: lsusb -d 1a86: -v Output: Bus 001 Device 096: ID 1a86:55d4 QinHeng Electronics 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 python-2.3.14/info/ubuntu/techo.txt000066400000000000000000000113451464266072200172570ustar00rootroot00000000000000 $ lsusb Bus 002 Device 005: ID 046d:c31c Logitech, Inc. Keyboard K120 Bus 002 Device 002: ID 8087:8000 Intel Corp. Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub Bus 001 Device 107: ID 239a:0029 Bus 001 Device 002: ID 8087:8008 Intel Corp. Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub $ lsusb -d 239a: -v Bus 001 Device 107: ID 239a:0029 Couldn't open device, some information will be missing Device Descriptor: bLength 18 bDescriptorType 1 bcdUSB 2.00 bDeviceClass 239 Miscellaneous Device bDeviceSubClass 2 bDeviceProtocol 1 Interface Association bMaxPacketSize0 64 idVendor 0x239a idProduct 0x0029 bcdDevice 1.00 iManufacturer 1 iProduct 2 iSerial 3 bNumConfigurations 1 Configuration Descriptor: bLength 9 bDescriptorType 2 wTotalLength 0x0062 bNumInterfaces 3 bConfigurationValue 1 iConfiguration 0 bmAttributes 0xa0 (Bus Powered) Remote Wakeup MaxPower 100mA Interface Association: bLength 8 bDescriptorType 11 bFirstInterface 0 bInterfaceCount 2 bFunctionClass 2 Communications bFunctionSubClass 2 Abstract (modem) bFunctionProtocol 0 iFunction 0 Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 0 bAlternateSetting 0 bNumEndpoints 1 bInterfaceClass 2 Communications bInterfaceSubClass 2 Abstract (modem) bInterfaceProtocol 0 iInterface 4 CDC Header: bcdCDC 1.20 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 0x81 EP 1 IN bmAttributes 3 Transfer Type Interrupt Synch Type None Usage Type Data wMaxPacketSize 0x0008 1x 8 bytes bInterval 16 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 0x0040 1x 64 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 Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 2 bAlternateSetting 0 bNumEndpoints 2 bInterfaceClass 8 Mass Storage bInterfaceSubClass 6 SCSI bInterfaceProtocol 80 Bulk-Only iInterface 5 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x03 EP 3 OUT bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0040 1x 64 bytes bInterval 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x83 EP 3 IN bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0040 1x 64 bytes bInterval 0 python-2.3.14/info/ubuntu/tlora.txt000066400000000000000000000067431464266072200173040ustar00rootroot00000000000000 Run on Ubuntu 20 Note: Device has Meshtastic firmware installed $ lsusb Bus 002 Device 005: ID 046d:c31c Logitech, Inc. Keyboard K120 Bus 002 Device 002: ID 8087:8000 Intel Corp. Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub Bus 001 Device 099: ID 1a86:55d4 QinHeng Electronics Bus 001 Device 002: ID 8087:8008 Intel Corp. Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub $ lsusb -d 1a86: -v Bus 001 Device 099: 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 0 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 python-2.3.14/info/windows/000077500000000000000000000000001464266072200155605ustar00rootroot00000000000000python-2.3.14/info/windows/heltec.txt000066400000000000000000000156341464266072200175760ustar00rootroot00000000000000Run from Windows 10 Might work... (nope) Get-PnpDevice -Class 'USB' -PresentOnly | Format-List > Get-PnpDevice -PresentOnly | Format-List > b > Compare-Object (get-content a) (Get-Content b) InputObject SideIndicator ----------- ------------- Caption : CP2102 USB to UART Bridge Controller <= Description : CP2102 USB to UART Bridge Controller <= Name : CP2102 USB to UART Bridge Controller <= Status : Error <= ConfigManagerErrorCode : CM_PROB_FAILED_INSTALL <= DeviceID : USB\VID_10C4&PID_EA60\0001 <= PNPDeviceID : USB\VID_10C4&PID_EA60\0001 <= CompatibleID : {USB\Class_FF&SubClass_00&Prot_00, USB\Class_FF&SubClass_00, USB\Class_FF} <= HardwareID : {USB\VID_10C4&PID_EA60&REV_0100, USB\VID_10C4&PID_EA60} <= FriendlyName : CP2102 USB to UART Bridge Controller <= InstanceId : USB\VID_10C4&PID_EA60\0001 <= Problem : CM_PROB_FAILED_INSTALL <= ClassGuid : <= Manufacturer : <= PNPClass : <= Class : <= Service : <= InstallDate : <= Availability : <= ConfigManagerUserConfig : False <= CreationClassName : Win32_PnPEntity <= ErrorCleared : <= ErrorDescription : <= LastErrorCode : <= PowerManagementCapabilities : <= PowerManagementSupported : <= StatusInfo : <= SystemCreationClassName : Win32_ComputerSystem <= SystemName : DESKTOP-FRFQN8H <= Present : True <= PSComputerName : <= ProblemDescription : <= <= > Get-PnpDevice -DeviceID 'USB\VID_10C4&PID_EA60\0001' Status Class FriendlyName InstanceId ------ ----- ------------ ---------- Error CP2102 USB to UART Bridge Controller USB\VID_... > Get-PnpDevice -PresentOnly -DeviceID 'USB\VID_10C4&PID_EA60\0001' Get-PnpDevice : No matching Win32_PnPEntity objects found by CIM query for instances of the ROOT\cimv2\Win32_PnPEntity class on the CIM server: SELECT * FROM Win32_PnPEntity WHERE ((DeviceId LIKE 'USB\\VID[_]10C4&PID[_]EA60\\0001')) AND ((Present = TRUE)). Verify query parameters and retry. At line:1 char:1 + Get-PnpDevice -PresentOnly -DeviceID 'USB\VID_10C4&PID_EA60\0001' + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : ObjectNotFound: (Win32_PnPEntity:String) [Get-PnpDevice], CimJobException + FullyQualifiedErrorId : CmdletizationQuery_NotFound,Get-PnpDevice > Get-PnpDevice -PresentOnly -DeviceID 'USB\VID_10C4&PID_EA60\0001' Status Class FriendlyName InstanceId ------ ----- ------------ ---------- Error CP2102 USB to UART Bridge Controller USB\VID_... If need to install driver Get-PnpDevice -DeviceID 'USB\VID_10C4&PID_EA60\0001' | Format-List Caption : CP2102 USB to UART Bridge Controller Description : CP2102 USB to UART Bridge Controller InstallDate : Name : CP2102 USB to UART Bridge Controller Status : Error Availability : ConfigManagerErrorCode : CM_PROB_FAILED_INSTALL ConfigManagerUserConfig : False CreationClassName : Win32_PnPEntity DeviceID : USB\VID_10C4&PID_EA60\0001 ErrorCleared : ErrorDescription : LastErrorCode : PNPDeviceID : USB\VID_10C4&PID_EA60\0001 PowerManagementCapabilities : PowerManagementSupported : StatusInfo : SystemCreationClassName : Win32_ComputerSystem SystemName : DESKTOP-FRFQN8H ClassGuid : CompatibleID : {USB\Class_FF&SubClass_00&Prot_00, USB\Class_FF&SubClass_00, USB\Class_FF} HardwareID : {USB\VID_10C4&PID_EA60&REV_0100, USB\VID_10C4&PID_EA60} Manufacturer : PNPClass : Present : True Service : PSComputerName : Class : FriendlyName : CP2102 USB to UART Bridge Controller InstanceId : USB\VID_10C4&PID_EA60\0001 Problem : CM_PROB_FAILED_INSTALL ProblemDescription : python-2.3.14/info/windows/nano_g1.txt000066400000000000000000000075421464266072200176530ustar00rootroot00000000000000Get-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 : => => python-2.3.14/info/windows/rak4631_19003.txt000066400000000000000000000207321464266072200201540ustar00rootroot00000000000000Run from Windows 10 > Get-PnpDevice -PresentOnly | Format-List >a > Get-PnpDevice -PresentOnly | Format-List >b > Compare-Object (get-content a) (Get-Content b) InputObject Side Indi cato r ----------- ---- Caption : USB Serial Device (COM4) => Description : USB Serial Device => Name : USB Serial Device (COM4) => DeviceID : USB\VID_239A&PID_8029&MI_00\6&E8876D1&0&0000 => PNPDeviceID : USB\VID_239A&PID_8029&MI_00\6&E8876D1&0&0000 => ClassGuid : {4d36e978-e325-11ce-bfc1-08002be10318} => CompatibleID : {USB\Class_02&SubClass_02&Prot_00, USB\Class_02&SubClass_02, USB\Class_02} => HardwareID : {USB\VID_239A&PID_8029&REV_0100&MI_00, USB\VID_239A&PID_8029&MI_00} => PNPClass : Ports => Service : usbser => Class : Ports => FriendlyName : USB Serial Device (COM4) => InstanceId : USB\VID_239A&PID_8029&MI_00\6&E8876D1&0&0000 => Caption : USB Composite Device => Description : USB Composite Device => Name : USB Composite Device => DeviceID : USB\VID_239A&PID_8029\E6CF9502B1D410D8 => PNPDeviceID : USB\VID_239A&PID_8029\E6CF9502B1D410D8 => ClassGuid : {36fc9e60-c465-11cf-8056-444553540000} => CompatibleID : {USB\DevClass_00&SubClass_00&Prot_00, USB\DevClass_00&SubClass_00, USB\DevClass_00, => USB\COMPOSITE} => HardwareID : {USB\VID_239A&PID_8029&REV_0100, USB\VID_239A&PID_8029} => Manufacturer : (Standard USB Host Controller) => PNPClass : USB => Service : usbccgp => Class : USB => FriendlyName : USB Composite Device => InstanceId : USB\VID_239A&PID_8029\E6CF9502B1D410D8 => 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 : => 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 => Manufacturer : Microsoft => Present : True => PSComputerName : => Problem : CM_PROB_NONE => ProblemDescription : => => => python-2.3.14/info/windows/rak4631_5005.txt000066400000000000000000000411441464266072200200710ustar00rootroot00000000000000Run from Windows 10 > Get-PnpDevice -PresentOnly | Format-List >a > Get-PnpDevice -PresentOnly | Format-List >b > Compare-Object (get-content a) (Get-Content b) In "boot" mode: InputObject ----------- Caption : FTHR840BOOT Description : nRF UF2 Name : FTHR840BOOT DeviceID : SWD\WPDBUSENUM\_??_USBSTOR#DISK&VEN_ADAFRUIT&PROD_NRF_UF2&REV_1.0#7&27E1626&0&D121BD1C90B 93EA2&0#{53F56307-B6BF-11D0-94F2-00A0C91EFB8B} PNPDeviceID : SWD\WPDBUSENUM\_??_USBSTOR#DISK&VEN_ADAFRUIT&PROD_NRF_UF2&REV_1.0#7&27E1626&0&D121BD1C90B 93EA2&0#{53F56307-B6BF-11D0-94F2-00A0C91EFB8B} ClassGuid : {eec5ad98-8080-425f-922a-dabf3de3f69a} CompatibleID : {wpdbusenum\fs, SWD\Generic} Manufacturer : Adafruit PNPClass : WPD Service : WUDFWpdFs Class : WPD FriendlyName : FTHR840BOOT InstanceId : SWD\WPDBUSENUM\_??_USBSTOR#DISK&VEN_ADAFRUIT&PROD_NRF_UF2&REV_1.0#7&27E1626&0&D121BD1C90B 93EA2&0#{53F56307-B6BF-11D0-94F2-00A0C91EFB8B} DeviceID : STORAGE\VOLUME\_??_USBSTOR#DISK&VEN_ADAFRUIT&PROD_NRF_UF2&REV_1.0#7&27E1626&0&D121BD1C90B 93EA2&0#{53F56307-B6BF-11D0-94F2-00A0C91EFB8B} PNPDeviceID : STORAGE\VOLUME\_??_USBSTOR#DISK&VEN_ADAFRUIT&PROD_NRF_UF2&REV_1.0#7&27E1626&0&D121BD1C90B 93EA2&0#{53F56307-B6BF-11D0-94F2-00A0C91EFB8B} InstanceId : STORAGE\VOLUME\_??_USBSTOR#DISK&VEN_ADAFRUIT&PROD_NRF_UF2&REV_1.0#7&27E1626&0&D121BD1C90B 93EA2&0#{53F56307-B6BF-11D0-94F2-00A0C91EFB8B} Caption : USB Mass Storage Device Description : USB Mass Storage Device Name : USB Mass Storage Device DeviceID : USB\VID_239A&PID_0029&MI_02\6&175793A&0&0002 PNPDeviceID : USB\VID_239A&PID_0029&MI_02\6&175793A&0&0002 CompatibleID : {USB\Class_08&SubClass_06&Prot_50, USB\Class_08&SubClass_06, USB\Class_08} HardwareID : {USB\VID_239A&PID_0029&REV_0100&MI_02, USB\VID_239A&PID_0029&MI_02} Manufacturer : Compatible USB storage device Service : USBSTOR FriendlyName : USB Mass Storage Device InstanceId : USB\VID_239A&PID_0029&MI_02\6&175793A&0&0002 Caption : USB Serial Device (COM5) Description : USB Serial Device Name : USB Serial Device (COM5) DeviceID : USB\VID_239A&PID_0029&MI_00\6&175793A&0&0000 PNPDeviceID : USB\VID_239A&PID_0029&MI_00\6&175793A&0&0000 ClassGuid : {4d36e978-e325-11ce-bfc1-08002be10318} CompatibleID : {USB\Class_02&SubClass_02&Prot_00, USB\Class_02&SubClass_02, USB\Class_02} HardwareID : {USB\VID_239A&PID_0029&REV_0100&MI_00, USB\VID_239A&PID_0029&MI_00} PNPClass : Ports Service : usbser Class : Ports FriendlyName : USB Serial Device (COM5) InstanceId : USB\VID_239A&PID_0029&MI_00\6&175793A&0&0000 DeviceID : USB\VID_239A&PID_0029\D121BD1C90B93EA2 PNPDeviceID : USB\VID_239A&PID_0029\D121BD1C90B93EA2 ClassGuid : {36fc9e60-c465-11cf-8056-444553540000} HardwareID : {USB\VID_239A&PID_0029&REV_0100, USB\VID_239A&PID_0029} PNPClass : USB Class : USB InstanceId : USB\VID_239A&PID_0029\D121BD1C90B93EA2 Caption : USB Composite Device Description : USB Composite Device Name : USB Composite Device ClassGuid : {36fc9e60-c465-11cf-8056-444553540000} CompatibleID : {USB\DevClass_00&SubClass_00&Prot_00, USB\DevClass_00&SubClass_00, USB\DevClass_00, USB\COMPOSITE} Manufacturer : (Standard USB Host Controller) PNPClass : USB Service : usbccgp Class : USB FriendlyName : USB Composite Device Caption : Volume Description : Volume Name : Volume ClassGuid : {71a27cdd-812a-11d0-bec7-08002be2092f} HardwareID : {STORAGE\Volume} PNPClass : Volume Service : volume Class : Volume FriendlyName : Volume HardwareID : Caption : Adafruit nRF UF2 USB Device Name : Adafruit nRF UF2 USB Device DeviceID : USBSTOR\DISK&VEN_ADAFRUIT&PROD_NRF_UF2&REV_1.0\7&27E1626&0&D121BD1C90B93EA2&0 PNPDeviceID : USBSTOR\DISK&VEN_ADAFRUIT&PROD_NRF_UF2&REV_1.0\7&27E1626&0&D121BD1C90B93EA2&0 CompatibleID : {USBSTOR\Disk, USBSTOR\RAW, GenDisk} HardwareID : {USBSTOR\DiskAdafruitnRF_UF2_________1.0_, USBSTOR\DiskAdafruitnRF_UF2_________, USBSTOR\DiskAdafruit, USBSTOR\AdafruitnRF_UF2_________1...} FriendlyName : Adafruit nRF UF2 USB Device InstanceId : USBSTOR\DISK&VEN_ADAFRUIT&PROD_NRF_UF2&REV_1.0\7&27E1626&0&D121BD1C90B93EA2&0 Description : Disk drive ClassGuid : {4d36e967-e325-11ce-bfc1-08002be10318} Manufacturer : (Standard disk drives) PNPClass : DiskDrive Service : disk Class : DiskDrive CompatibleID : 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 : 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 : 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 : 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 Manufacturer : Microsoft Present : True PSComputerName : Problem : CM_PROB_NONE ProblemDescription : When you press the RST to load Meshtastic: > Get-PnpDevice -PresentOnly | Format-List >b > Compare-Object (get-content a) (Get-Content b) InputObject Side Indi cato r ----------- ---- DeviceID : USB\VID_239A&PID_8029\D121BD1C90B93EA2 => PNPDeviceID : USB\VID_239A&PID_8029\D121BD1C90B93EA2 => HardwareID : {USB\VID_239A&PID_8029&REV_0100, USB\VID_239A&PID_8029} => InstanceId : USB\VID_239A&PID_8029\D121BD1C90B93EA2 => Caption : USB Composite Device => Description : USB Composite Device => Name : USB Composite Device => ClassGuid : {36fc9e60-c465-11cf-8056-444553540000} => CompatibleID : {USB\DevClass_00&SubClass_00&Prot_00, USB\DevClass_00&SubClass_00, USB\DevClass_00, => USB\COMPOSITE} => Manufacturer : (Standard USB Host Controller) => PNPClass : USB => Service : usbccgp => Class : USB => FriendlyName : USB Composite Device => Caption : USB Serial Device (COM6) => Description : USB Serial Device => Name : USB Serial Device (COM6) => DeviceID : USB\VID_239A&PID_8029&MI_00\6&39B279E2&0&0000 => PNPDeviceID : USB\VID_239A&PID_8029&MI_00\6&39B279E2&0&0000 => ClassGuid : {4d36e978-e325-11ce-bfc1-08002be10318} => CompatibleID : {USB\Class_02&SubClass_02&Prot_00, USB\Class_02&SubClass_02, USB\Class_02} => HardwareID : {USB\VID_239A&PID_8029&REV_0100&MI_00, USB\VID_239A&PID_8029&MI_00} => PNPClass : Ports => Service : usbser => Class : Ports => FriendlyName : USB Serial Device (COM6) => InstanceId : USB\VID_239A&PID_8029&MI_00\6&39B279E2&0&0000 => 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 : => 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 => Manufacturer : Microsoft => Present : True => PSComputerName : => Problem : CM_PROB_NONE => ProblemDescription : => => python-2.3.14/info/windows/tbeam.txt000066400000000000000000000075761464266072200174300ustar00rootroot00000000000000Run from Windows 10 > 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 (COM7) => Description : USB-Enhanced-SERIAL CH9102 => Name : USB-Enhanced-SERIAL CH9102 (COM7) => DeviceID : USB\VID_1A86&PID_55D4\5323005057 => PNPDeviceID : USB\VID_1A86&PID_55D4\5323005057 => 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 (COM7) => InstanceId : USB\VID_1A86&PID_55D4\5323005057 => 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 : => => python-2.3.14/info/windows/techo.txt000066400000000000000000000446061464266072200174350ustar00rootroot00000000000000PS > Get-PnpDevice -PresentOnly | Format-List >a PS > Get-PnpDevice -PresentOnly | Format-List >b PS > Compare-Object (get-content a) (get-content b) Note: Not in boot mode InputObject Side Indi cato r ----------- ---- DeviceID : USB\VID_239A&PID_4405\D02012062C578951 => PNPDeviceID : USB\VID_239A&PID_4405\D02012062C578951 => HardwareID : {USB\VID_239A&PID_4405&REV_0100, USB\VID_239A&PID_4405} => InstanceId : USB\VID_239A&PID_4405\D02012062C578951 => Caption : USB Composite Device => Description : USB Composite Device => Name : USB Composite Device => ClassGuid : {36fc9e60-c465-11cf-8056-444553540000} => CompatibleID : {USB\DevClass_00&SubClass_00&Prot_00, USB\DevClass_00&SubClass_00, USB\DevClass_00, => USB\COMPOSITE} => Manufacturer : (Standard USB Host Controller) => PNPClass : USB => Service : usbccgp => Class : USB => FriendlyName : USB Composite Device => Caption : USB Serial Device (COM10) => Description : USB Serial Device => Name : USB Serial Device (COM10) => DeviceID : USB\VID_239A&PID_4405&MI_00\6&1B68A3E6&0&0000 => PNPDeviceID : USB\VID_239A&PID_4405&MI_00\6&1B68A3E6&0&0000 => ClassGuid : {4d36e978-e325-11ce-bfc1-08002be10318} => CompatibleID : {USB\Class_02&SubClass_02&Prot_00, USB\Class_02&SubClass_02, USB\Class_02} => HardwareID : {USB\VID_239A&PID_4405&REV_0100&MI_00, USB\VID_239A&PID_4405&MI_00} => PNPClass : Ports => Service : usbser => Class : Ports => FriendlyName : USB Serial Device (COM10) => InstanceId : USB\VID_239A&PID_4405&MI_00\6&1B68A3E6&0&0000 => 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 : => 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 => Manufacturer : Microsoft => Present : True => PSComputerName : => Problem : CM_PROB_NONE => ProblemDescription : => => in boot mode PS > Get-PnpDevice -PresentOnly | Format-List >c PS > Compare-Object (get-content a) (get-content c) InputObject ----------- Caption : Adafruit nRF UF2 USB Device Name : Adafruit nRF UF2 USB Device DeviceID : USBSTOR\DISK&VEN_ADAFRUIT&PROD_NRF_UF2&REV_1.0\7&10304CB2&0&D02012062C578951&0 PNPDeviceID : USBSTOR\DISK&VEN_ADAFRUIT&PROD_NRF_UF2&REV_1.0\7&10304CB2&0&D02012062C578951&0 CompatibleID : {USBSTOR\Disk, USBSTOR\RAW, GenDisk} HardwareID : {USBSTOR\DiskAdafruitnRF_UF2_________1.0_, USBSTOR\DiskAdafruitnRF_UF2_________, USBSTOR\DiskAdafruit, USBSTOR\AdafruitnRF_UF2_________1...} FriendlyName : Adafruit nRF UF2 USB Device InstanceId : USBSTOR\DISK&VEN_ADAFRUIT&PROD_NRF_UF2&REV_1.0\7&10304CB2&0&D02012062C578951&0 Caption : USB Mass Storage Device Description : USB Mass Storage Device Name : USB Mass Storage Device DeviceID : USB\VID_239A&PID_0029&MI_02\6&2AE8D65&0&0002 PNPDeviceID : USB\VID_239A&PID_0029&MI_02\6&2AE8D65&0&0002 CompatibleID : {USB\Class_08&SubClass_06&Prot_50, USB\Class_08&SubClass_06, USB\Class_08} HardwareID : {USB\VID_239A&PID_0029&REV_0100&MI_02, USB\VID_239A&PID_0029&MI_02} Manufacturer : Compatible USB storage device Service : USBSTOR FriendlyName : USB Mass Storage Device InstanceId : USB\VID_239A&PID_0029&MI_02\6&2AE8D65&0&0002 Caption : USB Serial Device (COM11) Description : USB Serial Device Name : USB Serial Device (COM11) DeviceID : USB\VID_239A&PID_0029&MI_00\6&2AE8D65&0&0000 PNPDeviceID : USB\VID_239A&PID_0029&MI_00\6&2AE8D65&0&0000 ClassGuid : {4d36e978-e325-11ce-bfc1-08002be10318} CompatibleID : {USB\Class_02&SubClass_02&Prot_00, USB\Class_02&SubClass_02, USB\Class_02} HardwareID : {USB\VID_239A&PID_0029&REV_0100&MI_00, USB\VID_239A&PID_0029&MI_00} PNPClass : Ports Service : usbser Class : Ports FriendlyName : USB Serial Device (COM11) InstanceId : USB\VID_239A&PID_0029&MI_00\6&2AE8D65&0&0000 ClassGuid : {36fc9e60-c465-11cf-8056-444553540000} PNPClass : USB Class : USB Caption : USB Composite Device Description : USB Composite Device Name : USB Composite Device DeviceID : USB\VID_239A&PID_0029\D02012062C578951 PNPDeviceID : USB\VID_239A&PID_0029\D02012062C578951 ClassGuid : {36fc9e60-c465-11cf-8056-444553540000} CompatibleID : {USB\DevClass_00&SubClass_00&Prot_00, USB\DevClass_00&SubClass_00, USB\DevClass_00, USB\COMPOSITE} HardwareID : {USB\VID_239A&PID_0029&REV_0100, USB\VID_239A&PID_0029} Manufacturer : (Standard USB Host Controller) PNPClass : USB Service : usbccgp Class : USB FriendlyName : USB Composite Device InstanceId : USB\VID_239A&PID_0029\D02012062C578951 DeviceID : STORAGE\VOLUME\_??_USBSTOR#DISK&VEN_ADAFRUIT&PROD_NRF_UF2&REV_1.0#7&10304CB2&0&D02012062C 578951&0#{53F56307-B6BF-11D0-94F2-00A0C91EFB8B} PNPDeviceID : STORAGE\VOLUME\_??_USBSTOR#DISK&VEN_ADAFRUIT&PROD_NRF_UF2&REV_1.0#7&10304CB2&0&D02012062C 578951&0#{53F56307-B6BF-11D0-94F2-00A0C91EFB8B} InstanceId : STORAGE\VOLUME\_??_USBSTOR#DISK&VEN_ADAFRUIT&PROD_NRF_UF2&REV_1.0#7&10304CB2&0&D02012062C 578951&0#{53F56307-B6BF-11D0-94F2-00A0C91EFB8B} Caption : Volume Description : Volume Name : Volume ClassGuid : {71a27cdd-812a-11d0-bec7-08002be2092f} HardwareID : {STORAGE\Volume} PNPClass : Volume Service : volume Class : Volume FriendlyName : Volume Description : Disk drive ClassGuid : {4d36e967-e325-11ce-bfc1-08002be10318} Manufacturer : (Standard disk drives) PNPClass : DiskDrive Service : disk Class : DiskDrive Caption : TECHOBOOT Description : nRF UF2 Name : TECHOBOOT DeviceID : SWD\WPDBUSENUM\_??_USBSTOR#DISK&VEN_ADAFRUIT&PROD_NRF_UF2&REV_1.0#7&10304CB2&0&D02012062C 578951&0#{53F56307-B6BF-11D0-94F2-00A0C91EFB8B} PNPDeviceID : SWD\WPDBUSENUM\_??_USBSTOR#DISK&VEN_ADAFRUIT&PROD_NRF_UF2&REV_1.0#7&10304CB2&0&D02012062C 578951&0#{53F56307-B6BF-11D0-94F2-00A0C91EFB8B} ClassGuid : {eec5ad98-8080-425f-922a-dabf3de3f69a} CompatibleID : {wpdbusenum\fs, SWD\Generic} HardwareID : Manufacturer : Adafruit PNPClass : WPD Service : WUDFWpdFs Class : WPD FriendlyName : TECHOBOOT InstanceId : SWD\WPDBUSENUM\_??_USBSTOR#DISK&VEN_ADAFRUIT&PROD_NRF_UF2&REV_1.0#7&10304CB2&0&D02012062C 578951&0#{53F56307-B6BF-11D0-94F2-00A0C91EFB8B} CompatibleID : 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 : 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 : 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 : 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 Manufacturer : Microsoft Present : True PSComputerName : Problem : CM_PROB_NONE ProblemDescription : 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 : 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 Manufacturer : Microsoft Present : True PSComputerName : Problem : CM_PROB_NONE ProblemDescription : PS > Get-Volume DriveLetter FriendlyName FileSystemType DriveType HealthStatus OperationalStatus SizeRemaining Size ----------- ------------ -------------- --------- ------------ ----------------- ------------- ---- Recovery NTFS Fixed Healthy OK 301.99 MB 854 MB C NTFS Fixed Healthy OK 22.3 GB 56.67 GB D TECHOBOOT FAT Removable Healthy OK 30.05 MB 31.88 MB python-2.3.14/info/windows/tlora.txt000066400000000000000000000076101464266072200174460ustar00rootroot00000000000000Run from Windows 10 PS > Get-PnpDevice -PresentOnly | Format-List > a PS > Get-PnpDevice -PresentOnly | Format-List > b PS > Compare-Object (get-content a) (Get-Content b) InputObject SideIndicator ----------- ------------- Caption : USB-Enhanced-SERIAL CH9102 (COM3) <= Description : USB-Enhanced-SERIAL CH9102 <= Name : USB-Enhanced-SERIAL CH9102 (COM3) <= DeviceID : USB\VID_1A86&PID_55D4\5&27435A1F&0&1 <= PNPDeviceID : USB\VID_1A86&PID_55D4\5&27435A1F&0&1 <= 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 (COM3) <= InstanceId : USB\VID_1A86&PID_55D4\5&27435A1F&0&1 <= 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 : <= <= python-2.3.14/info/windows/tlora_v1.txt000066400000000000000000000172411464266072200200550ustar00rootroot00000000000000InputObject SideIn dicato r ----------- ------ Caption : Silicon Labs CP210x USB to UART Bridge (COM5) => Description : Silicon Labs CP210x USB to UART Bridge => Name : Silicon Labs CP210x USB to UART Bridge (COM5) => DeviceID : USB\VID_10C4&PID_EA60\0001 => PNPDeviceID : USB\VID_10C4&PID_EA60\0001 => HardwareID : {USB\VID_10C4&PID_EA60&REV_0100, USB\VID_10C4&PID_EA60} => Manufacturer : Silicon Laboratories => Service : silabser => FriendlyName : Silicon Labs CP210x USB to UART Bridge (COM5) => InstanceId : USB\VID_10C4&PID_EA60\0001 => CompatibleID : {USB\Class_ff&SubClass_00&Prot_00, USB\Class_ff&SubClass_00, USB\Class_ff} => ClassGuid : {4d36e978-e325-11ce-bfc1-08002be10318} => PNPClass : Ports => Class : Ports => InstallDate : => Status : OK => Availability : => ConfigManagerErrorCode : CM_PROB_NONE => ConfigManagerUserConfig : False => CreationClassName : Win32_PnPEntity => ErrorCleared : => ErrorDescription : => LastErrorCode : => PowerManagementCapabilities : => PowerManagementSupported : => StatusInfo : => SystemCreationClassName : Win32_ComputerSystem => SystemName : MTI-ATPLT1 => Present : True => PSComputerName : => Problem : CM_PROB_NONE => ProblemDescription : => Caption : Microsoft Serial Mouse => Description : Microsoft Serial Mouse => InstallDate : => Name : Microsoft Serial Mouse => Status : Error => Availability : => ConfigManagerErrorCode : CM_PROB_FAILED_START => ConfigManagerUserConfig : False => CreationClassName : Win32_PnPEntity => DeviceID : SILABENM\MOUSE\C&1EBF522&0&0000 => ErrorCleared : => ErrorDescription : => LastErrorCode : => PNPDeviceID : SILABENM\MOUSE\C&1EBF522&0&0000 => PowerManagementCapabilities : => PowerManagementSupported : => StatusInfo : => SystemCreationClassName : Win32_ComputerSystem => SystemName : MTI-ATPLT1 => ClassGuid : {4d36e96f-e325-11ce-bfc1-08002be10318} => CompatibleID : {SERIAL_MOUSE} => HardwareID : {*PNP0F01} => Manufacturer : Microsoft => PNPClass : Mouse => Present : True => Service : sermouse => PSComputerName : => Class : Mouse => FriendlyName : Microsoft Serial Mouse => InstanceId : SILABENM\MOUSE\C&1EBF522&0&0000 => Problem : CM_PROB_FAILED_START => ProblemDescription : => => => python-2.3.14/meshtastic/000077500000000000000000000000001464266072200152775ustar00rootroot00000000000000python-2.3.14/meshtastic/.gitignore000066400000000000000000000000141464266072200172620ustar00rootroot00000000000000__pycache__ python-2.3.14/meshtastic/__init__.py000066400000000000000000000224461464266072200174200ustar00rootroot00000000000000""" # A library for the Meshtastic Client API Primary interfaces: SerialInterface, TCPInterface, BLEInterface Install with pip: "[pip3 install meshtastic](https://pypi.org/project/meshtastic/)" Source code on [github](https://github.com/meshtastic/python) notable properties of interface classes: - 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. - nodesByNum - like "nodes" but keyed by nodeNum instead of nodeId - myInfo & metadata - Contain read-only information about the local radio device (software version, hardware version, etc) - localNode - Pointer to a node object for the local node notable properties of nodes: - localConfig - Current radio settings, can be written to the radio with the `writeConfig` method. - moduleConfig - Current module settings, can be written to the radio with the `writeConfig` method. - channels - The node's channels, keyed by index. # Published PubSub topics We use a [publish-subscribe](https://pypubsub.readthedocs.io/en/v4.0.3/) model to communicate asynchronous events. Available topics: - meshtastic.connection.established - published once we've successfully connected to the radio and downloaded the node DB - meshtastic.connection.lost - published once we've lost our link to the radio - meshtastic.receive.text(packet) - delivers a received packet as a dictionary, if you only care about a particular type of packet, you should subscribe to the full topic name. If you want to see all packets, simply subscribe to "meshtastic.receive". - meshtastic.receive.position(packet) - meshtastic.receive.user(packet) - meshtastic.receive.data.portnum(packet) (where portnum is an integer or well known PortNum enum) - meshtastic.node.updated(node = NodeInfo) - published when a node in the DB changes (appears, location changed, username changed, etc...) We receive position, user, or data packets from the mesh. You probably only care about meshtastic.receive.data. The first argument for that publish will be the packet. Text or binary data packets (from sendData or sendText) will both arrive this way. If you print packet you'll see the fields in the dictionary. decoded.data.payload will contain the raw bytes that were sent. If the packet was sent with sendText, decoded.data.text will **also** be populated with the decoded string. For ASCII these two strings will be the same, but for unicode scripts they can be different. # Example Usage ``` import meshtastic import meshtastic.serial_interface from pubsub import pub def onReceive(packet, interface): # called when a packet arrives print(f"Received: {packet}") def onConnection(interface, topic=pub.AUTO_TOPIC): # called when we (re)connect to the radio # defaults to broadcast, specify a destination ID if you wish interface.sendText("hello mesh") pub.subscribe(onReceive, "meshtastic.receive") pub.subscribe(onConnection, "meshtastic.connection.established") # By default will try to find a meshtastic device, otherwise provide a device path like /dev/ttyUSB0 interface = meshtastic.serial_interface.SerialInterface() ``` """ import base64 import logging import os import platform import random import socket import stat import sys import threading import time import traceback from datetime import datetime from typing import * import google.protobuf.json_format import serial # type: ignore[import-untyped] from dotmap import DotMap # type: ignore[import-untyped] from google.protobuf.json_format import MessageToJson from pubsub import pub # type: ignore[import-untyped] from tabulate import tabulate from meshtastic.node import Node from meshtastic.util import DeferredExecution, Timeout, catchAndIgnore, fixme, stripnl from .protobuf import ( admin_pb2, apponly_pb2, channel_pb2, config_pb2, mesh_pb2, mqtt_pb2, paxcount_pb2, portnums_pb2, remote_hardware_pb2, storeforward_pb2, telemetry_pb2, ) from . import ( util, ) # Note: To follow PEP224, comments should be after the module variable. LOCAL_ADDR = "^local" """A special ID that means the local node""" BROADCAST_NUM = 0xFFFFFFFF """if using 8 bit nodenums this will be shortened on the target""" BROADCAST_ADDR = "^all" """A special ID that means broadcast""" OUR_APP_VERSION = 20300 """The numeric buildnumber (shared with android apps) specifying the level of device code we are guaranteed to understand format is Mmmss (where M is 1+the numeric major number. i.e. 20120 means 1.1.20 """ NODELESS_WANT_CONFIG_ID = 69420 """A special thing to pass for want_config_id that instructs nodes to skip sending nodeinfos other than its own.""" publishingThread = DeferredExecution("publishing") class ResponseHandler(NamedTuple): """A pending response callback, waiting for a response to one of our messages""" # requestId: int - used only as a key callback: Callable ackPermitted: bool = False # FIXME, add timestamp and age out old requests class KnownProtocol(NamedTuple): """Used to automatically decode known protocol payloads""" name: str # portnum: int, now a key # If set, will be called to prase as a protocol buffer protobufFactory: Optional[Callable] = None # If set, invoked as onReceive(interface, packet) onReceive: Optional[Callable] = None def _onTextReceive(iface, asDict): """Special text auto parsing for received messages""" # We don't throw if the utf8 is invalid in the text message. Instead we just don't populate # the decoded.data.text and we log an error message. This at least allows some delivery to # the app and the app can deal with the missing decoded representation. # # Usually btw this problem is caused by apps sending binary data but setting the payload type to # text. logging.debug(f"in _onTextReceive() asDict:{asDict}") try: asBytes = asDict["decoded"]["payload"] asDict["decoded"]["text"] = asBytes.decode("utf-8") except Exception as ex: logging.error(f"Malformatted utf8 in text message: {ex}") _receiveInfoUpdate(iface, asDict) def _onPositionReceive(iface, asDict): """Special auto parsing for received messages""" logging.debug(f"in _onPositionReceive() asDict:{asDict}") if "decoded" in asDict: if "position" in asDict["decoded"] and "from" in asDict: p = asDict["decoded"]["position"] logging.debug(f"p:{p}") p = iface._fixupPosition(p) logging.debug(f"after fixup p:{p}") # update node DB as needed iface._getOrCreateByNum(asDict["from"])["position"] = p def _onNodeInfoReceive(iface, asDict): """Special auto parsing for received messages""" logging.debug(f"in _onNodeInfoReceive() asDict:{asDict}") if "decoded" in asDict: if "user" in asDict["decoded"] and "from" in asDict: p = asDict["decoded"]["user"] # decode user protobufs and update nodedb, provide decoded version as "position" in the published msg # update node DB as needed n = iface._getOrCreateByNum(asDict["from"]) n["user"] = p # We now have a node ID, make sure it is up-to-date in that table iface.nodes[p["id"]] = n _receiveInfoUpdate(iface, asDict) def _receiveInfoUpdate(iface, asDict): if "from" in asDict: iface._getOrCreateByNum(asDict["from"])["lastReceived"] = asDict iface._getOrCreateByNum(asDict["from"])["lastHeard"] = asDict.get("rxTime") iface._getOrCreateByNum(asDict["from"])["snr"] = asDict.get("rxSnr") iface._getOrCreateByNum(asDict["from"])["hopLimit"] = asDict.get("hopLimit") """Well known message payloads can register decoders for automatic protobuf parsing""" protocols = { portnums_pb2.PortNum.TEXT_MESSAGE_APP: KnownProtocol( "text", onReceive=_onTextReceive ), portnums_pb2.PortNum.RANGE_TEST_APP: KnownProtocol( "rangetest", onReceive=_onTextReceive ), portnums_pb2.PortNum.DETECTION_SENSOR_APP: KnownProtocol( "detectionsensor", onReceive=_onTextReceive ), portnums_pb2.PortNum.POSITION_APP: KnownProtocol( "position", mesh_pb2.Position, _onPositionReceive ), portnums_pb2.PortNum.NODEINFO_APP: KnownProtocol( "user", mesh_pb2.User, _onNodeInfoReceive ), portnums_pb2.PortNum.ADMIN_APP: KnownProtocol("admin", admin_pb2.AdminMessage), portnums_pb2.PortNum.ROUTING_APP: KnownProtocol("routing", mesh_pb2.Routing), portnums_pb2.PortNum.TELEMETRY_APP: KnownProtocol( "telemetry", telemetry_pb2.Telemetry ), portnums_pb2.PortNum.REMOTE_HARDWARE_APP: KnownProtocol( "remotehw", remote_hardware_pb2.HardwareMessage ), portnums_pb2.PortNum.SIMULATOR_APP: KnownProtocol("simulator", mesh_pb2.Compressed), portnums_pb2.PortNum.TRACEROUTE_APP: KnownProtocol( "traceroute", mesh_pb2.RouteDiscovery ), portnums_pb2.PortNum.WAYPOINT_APP: KnownProtocol("waypoint", mesh_pb2.Waypoint), portnums_pb2.PortNum.PAXCOUNTER_APP: KnownProtocol("paxcounter", paxcount_pb2.Paxcount), portnums_pb2.PortNum.STORE_FORWARD_APP: KnownProtocol("storeforward", storeforward_pb2.StoreAndForward), portnums_pb2.PortNum.NEIGHBORINFO_APP: KnownProtocol("neighborinfo", mesh_pb2.NeighborInfo), portnums_pb2.PortNum.MAP_REPORT_APP: KnownProtocol("mapreport", mqtt_pb2.MapReport), } python-2.3.14/meshtastic/__main__.py000066400000000000000000001637071464266072200174070ustar00rootroot00000000000000#!python3 """ Main Meshtastic """ import argparse import logging import os import platform import sys import time import pyqrcode # type: ignore[import-untyped] import yaml from google.protobuf.json_format import MessageToDict from pubsub import pub # type: ignore[import-untyped] import meshtastic.test import meshtastic.util from meshtastic import mt_config from meshtastic.protobuf import channel_pb2, config_pb2, portnums_pb2 from meshtastic import remote_hardware, BROADCAST_ADDR from meshtastic.version import get_active_version from meshtastic.ble_interface import BLEInterface from meshtastic.mesh_interface import MeshInterface def onReceive(packet, interface): """Callback invoked when a packet arrives""" args = mt_config.args try: d = packet.get("decoded") logging.debug(f"in onReceive() d:{d}") # Exit once we receive a reply if ( args and args.sendtext and packet["to"] == interface.myInfo.my_node_num and d["portnum"] == portnums_pb2.PortNum.TEXT_MESSAGE_APP ): interface.close() # after running command then exit # Reply to every received message with some stats if d is not None and args and args.reply: msg = d.get("text") if msg: rxSnr = packet["rxSnr"] hopLimit = packet["hopLimit"] print(f"message: {msg}") reply = f"got msg '{msg}' with rxSnr: {rxSnr} and hopLimit: {hopLimit}" print("Sending reply: ", reply) interface.sendText(reply) except Exception as ex: print(f"Warning: There is no field {ex} in the packet.") def onConnection(interface, topic=pub.AUTO_TOPIC): # pylint: disable=W0613 """Callback invoked when we connect/disconnect from a radio""" print(f"Connection changed: {topic.getName()}") def checkChannel(interface: MeshInterface, channelIndex: int) -> bool: """Given an interface and channel index, return True if that channel is non-disabled on the local node""" ch = interface.localNode.getChannelByChannelIndex(channelIndex) logging.debug(f"ch:{ch}") return (ch and ch.role != channel_pb2.Channel.Role.DISABLED) def getPref(node, comp_name): """Get a channel or preferences value""" name = splitCompoundName(comp_name) wholeField = name[0] == name[1] # We want the whole field 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[1]) logging.debug(f"snake_name:{snake_name} camel_name:{camel_name}") logging.debug(f"use camel:{mt_config.camel_case}") # First validate the input localConfig = node.localConfig moduleConfig = node.moduleConfig found = False for config in [localConfig, moduleConfig]: 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 pref or wholeField: found = True break if not found: if mt_config.camel_case: print( f"{localConfig.__class__.__name__} and {moduleConfig.__class__.__name__} do not have an attribute {snake_name}." ) else: print( f"{localConfig.__class__.__name__} and {moduleConfig.__class__.__name__} do not have attribute {snake_name}." ) print("Choices are...") printConfig(localConfig) printConfig(moduleConfig) return False # Check if we need to request the config if len(config.ListFields()) != 0: # read the value config_values = getattr(config, config_type.name) if not wholeField: pref_value = getattr(config_values, pref.name) if mt_config.camel_case: 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"{str(config_type.name)}.{snake_name}: {str(pref_value)}") logging.debug( f"{str(config_type.name)}.{snake_name}: {str(pref_value)}" ) else: print(f"{str(config_type.name)}:\n{str(config_values)}") logging.debug(f"{str(config_type.name)}: {str(config_values)}") else: # Always show whole field for remote node node.requestConfig(config_type) return True def splitCompoundName(comp_name): """Split compound (dot separated) preference name into parts""" name = comp_name.split(".") if len(name) < 2: name[0] = comp_name name.append(comp_name) return name def traverseConfig(config_root, config, interface_config): """Iterate through current config level preferences and either traverse deeper if preference is a dict or set preference""" snake_name = meshtastic.util.camel_to_snake(config_root) for pref in config: pref_name = f"{snake_name}.{pref}" if isinstance(config[pref], dict): traverseConfig(pref_name, config[pref], interface_config) else: setPref( interface_config, pref_name, str(config[pref]) ) return True def setPref(config, comp_name, valStr) -> bool: """Set a channel or preferences value""" 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 = config.DESCRIPTOR config_part = config config_type = objDesc.fields_by_name.get(name[0]) if config_type and config_type.message_type is not None: for name_part in name[1:-1]: part_snake_name = meshtastic.util.camel_to_snake((name_part)) config_part = getattr(config, config_type.name) config_type = config_type.message_type.fields_by_name.get(part_snake_name) pref = None if config_type and config_type.message_type is not None: pref = config_type.message_type.fields_by_name.get(snake_name) # Others like ChannelSettings are standalone elif config_type: pref = config_type if (not pref) or (not config_type): return False val = meshtastic.util.fromStr(valStr) logging.debug(f"valStr:{valStr} val:{val}") if snake_name == "wifi_psk" and len(valStr) < 8: print(f"Warning: network.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 e = enumType.values_by_name.get(val) if e: val = e.number else: if mt_config.camel_case: print( f"{name[0]}.{camel_name} does not have an enum called {val}, so you can not set it." ) else: 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: # Note: We must use the value of the enum (regardless if camel or snake case) names.append(f"{f.name}") for temp_name in sorted(names): print(f" {temp_name}") return False # note: 'ignore_incoming' is a repeating field if snake_name != "ignore_incoming": try: if config_type.message_type is not None: config_values = getattr(config_part, config_type.name) setattr(config_values, pref.name, val) else: setattr(config_part, snake_name, val) except TypeError: # The setter didn't like our arg type guess try again as a string config_values = getattr(config_part, config_type.name) setattr(config_values, pref.name, valStr) else: config_values = getattr(config, config_type.name) if val == 0: # clear values print("Clearing ignore_incoming list") del config_values.ignore_incoming[:] else: print(f"Adding '{val}' to the ignore_incoming list") config_values.ignore_incoming.extend([int(valStr)]) prefix = f"{'.'.join(name[0:-1])}." if config_type.message_type is not None else "" if mt_config.camel_case: print(f"Set {prefix}{camel_name} to {valStr}") else: print(f"Set {prefix}{snake_name} to {valStr}") return True def onConnected(interface): """Callback invoked when we connect to a radio""" closeNow = False # Should we drop the connection after we finish? waitForAckNak = ( False # Should we wait for an acknowledgment if we send to a remote node? ) try: args = mt_config.args # do not print this line if we are exporting the config if not args.export_config: print("Connected to radio") if args.remove_position: if args.dest != BROADCAST_ADDR: print("Setting positions of remote nodes is not supported.") return closeNow = True print("Removing fixed position and disabling fixed position setting") interface.localNode.removeFixedPosition() elif args.setlat or args.setlon or args.setalt: if args.dest != BROADCAST_ADDR: print("Setting latitude, longitude, and altitude of remote nodes is not supported.") return closeNow = True alt = 0 lat = 0 lon = 0 if args.setalt: alt = int(args.setalt) print(f"Fixing altitude at {alt} meters") if args.setlat: try: lat = int(args.setlat) except ValueError: lat = float(args.setlat) print(f"Fixing latitude at {lat} degrees") if args.setlon: try: lon = int(args.setlon) except ValueError: lon = float(args.setlon) print(f"Fixing longitude at {lon} degrees") print("Setting device position and enabling fixed position setting") # can include lat/long/alt etc: latitude = 37.5, longitude = -122.1 interface.localNode.setFixedPosition(lat, lon, alt) elif not args.no_time: # We normally provide a current time to the mesh when we connect if interface.localNode.nodeNum in interface.nodesByNum and "position" in interface.nodesByNum[interface.localNode.nodeNum]: # send the same position the node already knows, just to update time position = interface.nodesByNum[interface.localNode.nodeNum]["position"] interface.sendPosition(position.get("latitude", 0.0), position.get("longitude", 0.0), position.get("altitude", 0.0)) else: interface.sendPosition() if args.set_owner: closeNow = True waitForAckNak = True print(f"Setting device owner to {args.set_owner}") interface.getNode(args.dest, False).setOwner(args.set_owner) if args.set_owner_short: closeNow = True waitForAckNak = True print(f"Setting device owner short to {args.set_owner_short}") interface.getNode(args.dest, False).setOwner( long_name=None, short_name=args.set_owner_short ) # TODO: add to export-config and configure if args.set_canned_message: closeNow = True waitForAckNak = True print(f"Setting canned plugin message to {args.set_canned_message}") interface.getNode(args.dest, False).set_canned_message( args.set_canned_message ) # TODO: add to export-config and configure if args.set_ringtone: closeNow = True waitForAckNak = True print(f"Setting ringtone to {args.set_ringtone}") interface.getNode(args.dest, False).set_ringtone(args.set_ringtone) if args.pos_fields: # If --pos-fields invoked with args, set position fields closeNow = True positionConfig = interface.getNode(args.dest).localConfig.position allFields = 0 try: for field in args.pos_fields: v_field = positionConfig.PositionFlags.Value(field) allFields |= v_field except ValueError: print("ERROR: supported position fields are:") print(positionConfig.PositionFlags.keys()) print( "If no fields are specified, will read and display current value." ) else: print(f"Setting position fields to {allFields}") setPref(positionConfig, "position_flags", f"{allFields:d}") print("Writing modified preferences to device") interface.getNode(args.dest).writeConfig("position") elif args.pos_fields is not None: # If --pos-fields invoked without args, read and display current value closeNow = True positionConfig = interface.getNode(args.dest).localConfig.position fieldNames = [] for bit in positionConfig.PositionFlags.values(): if positionConfig.position_flags & bit: fieldNames.append(positionConfig.PositionFlags.Name(bit)) print(" ".join(fieldNames)) if args.set_ham: closeNow = True print(f"Setting Ham ID to {args.set_ham} and turning off encryption") interface.getNode(args.dest).setOwner(args.set_ham, is_licensed=True) # Must turn off encryption on primary channel interface.getNode(args.dest).turnOffEncryptionOnPrimaryChannel() if args.reboot: closeNow = True waitForAckNak = True interface.getNode(args.dest, False).reboot() if args.reboot_ota: closeNow = True waitForAckNak = True interface.getNode(args.dest, False).rebootOTA() if args.enter_dfu: closeNow = True waitForAckNak = True interface.getNode(args.dest, False).enterDFUMode() if args.shutdown: closeNow = True waitForAckNak = True interface.getNode(args.dest, False).shutdown() if args.device_metadata: closeNow = True interface.getNode(args.dest, False).getMetadata() if args.begin_edit: closeNow = True interface.getNode(args.dest, False).beginSettingsTransaction() if args.commit_edit: closeNow = True interface.getNode(args.dest, False).commitSettingsTransaction() if args.factory_reset: closeNow = True waitForAckNak = True interface.getNode(args.dest, False).factoryReset() if args.remove_node: closeNow = True waitForAckNak = True interface.getNode(args.dest, False).removeNode(args.remove_node) if args.reset_nodedb: closeNow = True waitForAckNak = True interface.getNode(args.dest, False).resetNodeDb() if args.sendtext: closeNow = True channelIndex = mt_config.channel_index or 0 if checkChannel(interface, channelIndex): print( f"Sending text message {args.sendtext} to {args.dest} on channelIndex:{channelIndex}" ) interface.sendText( args.sendtext, args.dest, wantAck=True, channelIndex=channelIndex, onResponse=interface.getNode(args.dest, False).onAckNak, ) else: meshtastic.util.our_exit( f"Warning: {channelIndex} is not a valid channel. Channel must not be DISABLED." ) if args.traceroute: loraConfig = getattr(interface.localNode.localConfig, "lora") hopLimit = getattr(loraConfig, "hop_limit") dest = str(args.traceroute) channelIndex = mt_config.channel_index or 0 if checkChannel(interface, channelIndex): print(f"Sending traceroute request to {dest} on channelIndex:{channelIndex} (this could take a while)") interface.sendTraceRoute(dest, hopLimit, channelIndex=channelIndex) if args.request_telemetry: if args.dest == BROADCAST_ADDR: meshtastic.util.our_exit("Warning: Must use a destination node ID.") else: channelIndex = mt_config.channel_index or 0 if checkChannel(interface, channelIndex): print(f"Sending telemetry request to {args.dest} on channelIndex:{channelIndex} (this could take a while)") interface.sendTelemetry(destinationId=args.dest, wantResponse=True, channelIndex=channelIndex) if args.request_position: if args.dest == BROADCAST_ADDR: meshtastic.util.our_exit("Warning: Must use a destination node ID.") else: channelIndex = mt_config.channel_index or 0 if checkChannel(interface, channelIndex): print(f"Sending position request to {args.dest} on channelIndex:{channelIndex} (this could take a while)") interface.sendPosition(destinationId=args.dest, wantResponse=True, channelIndex=channelIndex) if args.gpio_wrb or args.gpio_rd or args.gpio_watch: if args.dest == BROADCAST_ADDR: meshtastic.util.our_exit("Warning: Must use a destination node ID.") else: rhc = remote_hardware.RemoteHardwareClient(interface) if args.gpio_wrb: bitmask = 0 bitval = 0 for wrpair in args.gpio_wrb or []: bitmask |= 1 << int(wrpair[0]) bitval |= int(wrpair[1]) << int(wrpair[0]) print( f"Writing GPIO mask 0x{bitmask:x} with value 0x{bitval:x} to {args.dest}" ) rhc.writeGPIOs(args.dest, bitmask, bitval) closeNow = True if args.gpio_rd: bitmask = int(args.gpio_rd, 16) print(f"Reading GPIO mask 0x{bitmask:x} from {args.dest}") interface.mask = bitmask rhc.readGPIOs(args.dest, bitmask, None) # wait up to X seconds for a response for _ in range(10): time.sleep(1) if interface.gotResponse: break logging.debug(f"end of gpio_rd") if args.gpio_watch: bitmask = int(args.gpio_watch, 16) print( f"Watching GPIO mask 0x{bitmask:x} from {args.dest}. Press ctrl-c to exit" ) while True: rhc.watchGPIOs(args.dest, bitmask) time.sleep(1) # handle settings if args.set: closeNow = True waitForAckNak = True node = interface.getNode(args.dest, False) # Handle the int/float/bool arguments pref = None for pref in args.set: found = False field = splitCompoundName(pref[0].lower())[0] for config in [node.localConfig, node.moduleConfig]: config_type = config.DESCRIPTOR.fields_by_name.get(field) if config_type: if len(config.ListFields()) == 0: node.requestConfig( config.DESCRIPTOR.fields_by_name.get(field) ) found = setPref(config, pref[0], pref[1]) if found: break if found: print("Writing modified preferences to device") node.writeConfig(field) else: if mt_config.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]}." ) print("Choices are...") printConfig(node.localConfig) printConfig(node.moduleConfig) if args.configure: with open(args.configure[0], encoding="utf8") as file: configuration = yaml.safe_load(file) closeNow = True interface.getNode(args.dest, False).beginSettingsTransaction() if "owner" in configuration: print(f"Setting device owner to {configuration['owner']}") waitForAckNak = True interface.getNode(args.dest, False).setOwner(configuration["owner"]) if "owner_short" in configuration: print( f"Setting device owner short to {configuration['owner_short']}" ) waitForAckNak = True interface.getNode(args.dest, False).setOwner( long_name=None, short_name=configuration["owner_short"] ) if "ownerShort" in configuration: print( f"Setting device owner short to {configuration['ownerShort']}" ) waitForAckNak = True interface.getNode(args.dest, False).setOwner( long_name=None, short_name=configuration["ownerShort"] ) if "channel_url" in configuration: print("Setting channel url to", configuration["channel_url"]) interface.getNode(args.dest).setURL(configuration["channel_url"]) if "channelUrl" in configuration: print("Setting channel url to", configuration["channelUrl"]) interface.getNode(args.dest).setURL(configuration["channelUrl"]) if "location" in configuration: alt = 0 lat = 0.0 lon = 0.0 localConfig = interface.localNode.localConfig if "alt" in configuration["location"]: alt = int(configuration["location"]["alt"] or 0) localConfig.position.fixed_position = True print(f"Fixing altitude at {alt} meters") if "lat" in configuration["location"]: lat = float(configuration["location"]["lat"] or 0) localConfig.position.fixed_position = True print(f"Fixing latitude at {lat} degrees") if "lon" in configuration["location"]: lon = float(configuration["location"]["lon"] or 0) localConfig.position.fixed_position = True print(f"Fixing longitude at {lon} degrees") print("Setting device position") interface.sendPosition(lat, lon, alt) interface.localNode.writeConfig("position") if "config" in configuration: localConfig = interface.getNode(args.dest).localConfig for section in configuration["config"]: traverseConfig(section, configuration["config"][section], localConfig) interface.getNode(args.dest).writeConfig( meshtastic.util.camel_to_snake(section) ) if "module_config" in configuration: moduleConfig = interface.getNode(args.dest).moduleConfig for section in configuration["module_config"]: traverseConfig(section, configuration["module_config"][section], moduleConfig) interface.getNode(args.dest).writeConfig( meshtastic.util.camel_to_snake(section) ) interface.getNode(args.dest, False).commitSettingsTransaction() print("Writing modified configuration to device") if args.export_config: if args.dest != BROADCAST_ADDR: print("Exporting configuration of remote nodes is not supported.") return # export the configuration (the opposite of '--configure') closeNow = True export_config(interface) if args.seturl: closeNow = True interface.getNode(args.dest).setURL(args.seturl) # handle changing channels if args.ch_add: channelIndex = mt_config.channel_index if channelIndex is not None: # Since we set the channel index after adding a channel, don't allow --ch-index meshtastic.util.our_exit( "Warning: '--ch-add' and '--ch-index' are incompatible. Channel not added." ) closeNow = True if len(args.ch_add) > 10: meshtastic.util.our_exit( "Warning: Channel name must be shorter. Channel not added." ) n = interface.getNode(args.dest) ch = n.getChannelByName(args.ch_add) if ch: meshtastic.util.our_exit( f"Warning: This node already has a '{args.ch_add}' channel. No changes were made." ) else: # get the first channel that is disabled (i.e., available) ch = n.getDisabledChannel() if not ch: meshtastic.util.our_exit("Warning: No free channels were found") chs = channel_pb2.ChannelSettings() chs.psk = meshtastic.util.genPSK256() chs.name = args.ch_add ch.settings.CopyFrom(chs) ch.role = channel_pb2.Channel.Role.SECONDARY print(f"Writing modified channels to device") n.writeChannel(ch.index) if channelIndex is None: print(f"Setting newly-added channel's {ch.index} as '--ch-index' for further modifications") mt_config.channel_index = ch.index if args.ch_del: closeNow = True channelIndex = mt_config.channel_index if channelIndex is None: meshtastic.util.our_exit( "Warning: Need to specify '--ch-index' for '--ch-del'.", 1 ) else: if channelIndex == 0: meshtastic.util.our_exit( "Warning: Cannot delete primary channel.", 1 ) else: print(f"Deleting channel {channelIndex}") ch = interface.getNode(args.dest).deleteChannel(channelIndex) def setSimpleConfig(modem_preset): """Set one of the simple modem_config""" channelIndex = mt_config.channel_index if channelIndex is not None and channelIndex > 0: meshtastic.util.our_exit( "Warning: Cannot set modem preset for non-primary channel", 1 ) # Overwrite modem_preset prefs = interface.getNode(args.dest).localConfig prefs.lora.modem_preset = modem_preset interface.getNode(args.dest).writeConfig("lora") # handle the simple radio set commands if args.ch_vlongslow: setSimpleConfig(config_pb2.Config.LoRaConfig.ModemPreset.VERY_LONG_SLOW) if args.ch_longslow: setSimpleConfig(config_pb2.Config.LoRaConfig.ModemPreset.LONG_SLOW) if args.ch_longfast: setSimpleConfig(config_pb2.Config.LoRaConfig.ModemPreset.LONG_FAST) if args.ch_medslow: setSimpleConfig(config_pb2.Config.LoRaConfig.ModemPreset.MEDIUM_SLOW) if args.ch_medfast: setSimpleConfig(config_pb2.Config.LoRaConfig.ModemPreset.MEDIUM_FAST) if args.ch_shortslow: setSimpleConfig(config_pb2.Config.LoRaConfig.ModemPreset.SHORT_SLOW) if args.ch_shortfast: setSimpleConfig(config_pb2.Config.LoRaConfig.ModemPreset.SHORT_FAST) if args.ch_set or args.ch_enable or args.ch_disable: closeNow = True channelIndex = mt_config.channel_index if channelIndex is None: meshtastic.util.our_exit("Warning: Need to specify '--ch-index'.", 1) ch = interface.getNode(args.dest).channels[channelIndex] if args.ch_enable or args.ch_disable: print( "Warning: --ch-enable and --ch-disable can produce noncontiguous channels, " "which can cause errors in some clients. Whenever possible, use --ch-add and --ch-del instead." ) if channelIndex == 0: meshtastic.util.our_exit( "Warning: Cannot enable/disable PRIMARY channel." ) enable = True # default to enable if args.ch_enable: enable = True if args.ch_disable: enable = False # Handle the channel settings for pref in args.ch_set or []: if pref[0] == "psk": found = True ch.settings.psk = meshtastic.util.fromPSK(pref[1]) else: found = setPref(ch.settings, pref[0], pref[1]) if not found: category_settings = ['module_settings'] print( f"{ch.settings.__class__.__name__} does not have an attribute {pref[0]}." ) print("Choices are...") for field in ch.settings.DESCRIPTOR.fields: if field.name not in category_settings: print(f"{field.name}") else: print(f"{field.name}:") config = ch.settings.DESCRIPTOR.fields_by_name.get(field.name) names = [] for sub_field in config.message_type.fields: tmp_name = f"{field.name}.{sub_field.name}" names.append(tmp_name) for temp_name in sorted(names): print(f" {temp_name}") enable = True # If we set any pref, assume the user wants to enable the channel if enable: ch.role = ( channel_pb2.Channel.Role.PRIMARY if (channelIndex == 0) else channel_pb2.Channel.Role.SECONDARY ) else: ch.role = channel_pb2.Channel.Role.DISABLED 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.get_ringtone: closeNow = True print("") interface.getNode(args.dest).get_ringtone() if args.info: print("") # If we aren't trying to talk to our local node, don't show it if args.dest == BROADCAST_ADDR: interface.showInfo() print("") interface.getNode(args.dest).showInfo() closeNow = True print("") pypi_version = meshtastic.util.check_if_newer_version() if pypi_version: print( f"*** A newer version v{pypi_version} is available!" ' Consider running "pip install --upgrade meshtastic" ***\n' ) if sys.version_info[0] == 3 and sys.version_info[1] < 9: print(" *** this version of the CLI is the last that supports python 3.8 ***") print(" *** please update your python installation ***") else: print("Showing info of remote node is not supported.") print( "Use the '--get' command for a specific configuration (e.g. 'lora') instead." ) if args.get: closeNow = True node = interface.getNode(args.dest, False) for pref in args.get: found = getPref(node, pref[0]) if found: print("Completed getting preferences") if args.nodes: closeNow = True if args.dest != BROADCAST_ADDR: print("Showing node list of a remote node is not supported.") return interface.showNodes() if args.qr or args.qr_all: closeNow = True url = interface.getNode(args.dest, True).getURL(includeAll=args.qr_all) if args.qr_all: urldesc = "Complete URL (includes all channels)" else: urldesc = "Primary channel URL" print(f"{urldesc}: {url}") qr = pyqrcode.create(url) print(qr.terminal()) if args.listen: closeNow = False have_tunnel = platform.system() == "Linux" if have_tunnel and args.tunnel: if args.dest != BROADCAST_ADDR: print("A tunnel can only be created using the local node.") return # pylint: disable=C0415 from . import tunnel # Even if others said we could close, stay open if the user asked for a tunnel closeNow = False if interface.noProto: logging.warning(f"Not starting Tunnel - disabled by noProto") else: if args.tunnel_net: tunnel.Tunnel(interface, subnet=args.tunnel_net) else: tunnel.Tunnel(interface) if args.ack or (args.dest != BROADCAST_ADDR and waitForAckNak): print( f"Waiting for an acknowledgment from remote node (this could take a while)" ) interface.getNode(args.dest, False).iface.waitForAckNak() if args.wait_to_disconnect: print(f"Waiting {args.wait_to_disconnect} seconds before disconnecting" ) time.sleep(int(args.wait_to_disconnect)) # if the user didn't ask for serial debugging output, we might want to exit after we've done our operation if (not args.seriallog) and closeNow: interface.close() # after running command then exit except Exception as ex: print(f"Aborting due to: {ex}") interface.close() # close the connection now, so that our app exits sys.exit(1) def printConfig(config): """print configuration""" objDesc = config.DESCRIPTOR for config_section in objDesc.fields: if config_section.name != "version": config = objDesc.fields_by_name.get(config_section.name) print(f"{config_section.name}:") names = [] for field in config.message_type.fields: tmp_name = f"{config_section.name}.{field.name}" if mt_config.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}") def onNode(node): """Callback invoked when the node DB changes""" print(f"Node changed: {node}") def subscribe(): """Subscribe to the topics the user probably wants to see, prints output to stdout""" pub.subscribe(onReceive, "meshtastic.receive") # pub.subscribe(onConnection, "meshtastic.connection") # We now call onConnected from main # pub.subscribe(onConnected, "meshtastic.connection.established") # pub.subscribe(onNode, "meshtastic.node") def export_config(interface): """used in --export-config""" configObj = {} owner = interface.getLongName() owner_short = interface.getShortName() channel_url = interface.localNode.getURL() myinfo = interface.getMyNodeInfo() pos = myinfo.get("position") lat = None lon = None alt = None if pos: lat = pos.get("latitude") lon = pos.get("longitude") alt = pos.get("altitude") if owner: configObj["owner"] = owner if owner_short: configObj["owner_short"] = owner_short if channel_url: if mt_config.camel_case: configObj["channelUrl"] = channel_url else: configObj["channel_url"] = channel_url # lat and lon don't make much sense without the other (so fill with 0s), and alt isn't meaningful without both if lat or lon: configObj["location"] = {"lat": lat or float(0), "lon": lon or float(0)} if alt: configObj["location"]["alt"] = alt config = MessageToDict(interface.localNode.localConfig) if config: # Convert inner keys to correct snake/camelCase prefs = {} for pref in config: if mt_config.camel_case: prefs[meshtastic.util.snake_to_camel(pref)] = config[pref] else: prefs[pref] = config[pref] if mt_config.camel_case: configObj["config"] = config else: configObj["config"] = config module_config = MessageToDict(interface.localNode.moduleConfig) if module_config: # Convert inner keys to correct snake/camelCase prefs = {} for pref in module_config: if len(module_config[pref]) > 0: prefs[pref] = module_config[pref] if mt_config.camel_case: configObj["module_config"] = prefs else: configObj["module_config"] = prefs config = "# start of Meshtastic configure yaml\n" config += yaml.dump(configObj) print(config) return config def common(): """Shared code for all of our command line wrappers""" logfile = None args = mt_config.args parser = mt_config.parser logging.basicConfig( level=logging.DEBUG if (args.debug or args.listen) else logging.INFO, format="%(levelname)s file:%(filename)s %(funcName)s line:%(lineno)s %(message)s", ) if len(sys.argv) == 1: parser.print_help(sys.stderr) meshtastic.util.our_exit("", 1) else: if args.support: meshtastic.util.support_info() meshtastic.util.our_exit("", 0) if args.ch_index is not None: channelIndex = int(args.ch_index) mt_config.channel_index = channelIndex if not args.dest: args.dest = BROADCAST_ADDR if not args.seriallog: if args.noproto: args.seriallog = "stdout" else: args.seriallog = "none" # assume no debug output in this case if args.deprecated is not None: logging.error( "This option has been deprecated, see help below for the correct replacement..." ) parser.print_help(sys.stderr) meshtastic.util.our_exit("", 1) elif args.test: result = meshtastic.test.testAll() if not result: meshtastic.util.our_exit("Warning: Test was not successful.") else: meshtastic.util.our_exit("Test was a success.", 0) else: if args.seriallog == "stdout": logfile = sys.stdout elif args.seriallog == "none": args.seriallog = None logging.debug("Not logging serial output") logfile = None else: logging.info(f"Logging serial output to {args.seriallog}") # Note: using "line buffering" # pylint: disable=R1732 logfile = open(args.seriallog, "w+", buffering=1, encoding="utf8") mt_config.logfile = logfile subscribe() if args.ble_scan: logging.debug("BLE scan starting") for x in BLEInterface.scan(): print(f"Found: name='{x.name}' address='{x.address}'") meshtastic.util.our_exit("BLE scan finished", 0) elif args.ble: client = BLEInterface(args.ble if args.ble != "any" else None, debugOut=logfile, noProto=args.noproto, noNodes=args.no_nodes) elif args.host: try: client = meshtastic.tcp_interface.TCPInterface( args.host, debugOut=logfile, noProto=args.noproto, noNodes=args.no_nodes ) except Exception as ex: meshtastic.util.our_exit( f"Error connecting to {args.host}:{ex}", 1 ) else: try: client = meshtastic.serial_interface.SerialInterface( args.port, debugOut=logfile, noProto=args.noproto, noNodes=args.no_nodes ) except PermissionError as ex: username = os.getlogin() message = "Permission Error:\n" message += ( " Need to add yourself to the 'dialout' group by running:\n" ) message += f" sudo usermod -a -G dialout {username}\n" message += " After running that command, log out and re-login for it to take effect.\n" message += f"Error was:{ex}" meshtastic.util.our_exit(message) if client.devPath is None: try: client = meshtastic.tcp_interface.TCPInterface( "localhost", debugOut=logfile, noProto=args.noproto, noNodes=args.no_nodes ) except Exception as ex: meshtastic.util.our_exit( f"Error connecting to localhost:{ex}", 1 ) # We assume client is fully connected now onConnected(client) have_tunnel = platform.system() == "Linux" if ( args.noproto or args.reply or (have_tunnel and args.tunnel) or args.listen ): # loop until someone presses ctrlc while True: time.sleep(1000) # don't call exit, background threads might be running still # sys.exit(0) def addConnectionArgs(parser: argparse.ArgumentParser) -> argparse.ArgumentParser: """Add connection specifiation arguments""" outer = parser.add_argument_group('Connection', 'Optional arguments that specify how to connect to a Meshtastic device.') group = outer.add_mutually_exclusive_group() group.add_argument( "--port", "--serial", "-s", help="The port of the device to connect to using serial, e.g. /dev/ttyUSB0. (defaults to trying to detect a port)", nargs="?", const=None, default=None, ) group.add_argument( "--host", "--tcp", "-t", help="Connect to a device using TCP, optionally passing hostname or IP address to use. (defaults to '%(const)s')", nargs="?", default=None, const="localhost" ) group.add_argument( "--ble", "-b", help="Connect to a BLE device, optionally specifying a device name (defaults to '%(const)s')", nargs="?", default=None, const="any" ) return parser def initParser(): """Initialize the command line argument parsing.""" parser = mt_config.parser args = mt_config.args # The "Help" group includes the help option and other informational stuff about the CLI itself outerHelpGroup = parser.add_argument_group('Help') helpGroup = outerHelpGroup.add_mutually_exclusive_group() helpGroup.add_argument("-h", "--help", action="help", help="show this help message and exit") the_version = get_active_version() helpGroup.add_argument("--version", action="version", version=f"{the_version}") helpGroup.add_argument( "--support", action="store_true", help="Show support info (useful when troubleshooting an issue)", ) # Connection arguments to indicate a device to connect to parser = addConnectionArgs(parser) # Arguments concerning viewing and setting configuration # Arguments for sending or requesting things from the local device # Arguments for sending or requesting things from the mesh # All the rest of the arguments group = parser.add_argument_group("optional arguments") group.add_argument( "--configure", help="Specify a path to a yaml(.yml) file containing the desired settings for the connected device.", action="append", ) group.add_argument( "--export-config", help="Export the configuration in yaml(.yml) format.", action="store_true", ) group.add_argument( "--seriallog", help="Log device serial output to either 'none' or a filename to append to. Defaults to 'stdout' if no filename specified.", nargs='?', const="stdout", default=None ) group.add_argument( "--info", help="Read and display the radio config information", action="store_true", ) group.add_argument( "--get-canned-message", help="Show the canned message plugin message", action="store_true", ) group.add_argument( "--get-ringtone", help="Show the stored ringtone", action="store_true" ) group.add_argument( "--nodes", help="Print Node List in a pretty formatted table", action="store_true", ) group.add_argument( "--qr", help=( "Display a QR code for the node's primary channel (or all channels with --qr-all). " "Also shows the shareable channel URL." ), action="store_true", ) group.add_argument( "--qr-all", help="Display a QR code and URL for all of the node's channels.", action="store_true", ) group.add_argument( "--get", help=( "Get a preferences field. Use an invalid field such as '0' to get a list of all fields." " Can use either snake_case or camelCase format. (ex: 'ls_secs' or 'lsSecs')" ), nargs=1, action="append", ) group.add_argument( "--set", help="Set a preferences field. Can use either snake_case or camelCase format. (ex: 'ls_secs' or 'lsSecs')", nargs=2, action="append", ) group.add_argument("--seturl", help="Set a channel URL", action="store") group.add_argument( "--ch-index", help="Set the specified channel index. Channels start at 0 (0 is the PRIMARY channel).", action="store", ) group.add_argument( "--ch-add", help="Add a secondary channel, you must specify a channel name", default=None, ) group.add_argument( "--ch-del", help="Delete the ch-index channel", action="store_true" ) group.add_argument( "--ch-enable", help="Enable the specified channel", action="store_true", dest="ch_enable", default=False, ) # Note: We are doing a double negative here (Do we want to disable? If ch_disable==True, then disable.) group.add_argument( "--ch-disable", help="Disable the specified channel", action="store_true", dest="ch_disable", default=False, ) group.add_argument( "--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 psk default --ch-index 0'. To set encryption with your " "own key: '--ch-set psk 0x1a1a1a1a2b2b2b2b1a1a1a1a2b2b2b2b1a1a1a1a2b2b2b2b1a1a1a1a2b2b2b2b --ch-index 0'." ), nargs=2, action="append", ) group.add_argument( "--ch-vlongslow", help="Change to the very long-range and slow channel", action="store_true", ) group.add_argument( "--ch-longslow", help="Change to the long-range and slow channel", action="store_true", ) group.add_argument( "--ch-longfast", help="Change to the long-range and fast channel", action="store_true", ) group.add_argument( "--ch-medslow", help="Change to the med-range and slow channel", action="store_true", ) group.add_argument( "--ch-medfast", help="Change to the med-range and fast channel", action="store_true", ) group.add_argument( "--ch-shortslow", help="Change to the short-range and slow channel", action="store_true", ) group.add_argument( "--ch-shortfast", help="Change to the short-range and fast channel", action="store_true", ) group.add_argument("--set-owner", help="Set device owner name", action="store") group.add_argument( "--set-canned-message", help="Set the canned messages plugin message (up to 200 characters).", action="store", ) group.add_argument( "--set-ringtone", help="Set the Notification Ringtone (up to 230 characters).", action="store", ) group.add_argument( "--set-owner-short", help="Set device owner short name", action="store" ) group.add_argument( "--set-ham", help="Set licensed Ham ID and turn off encryption", action="store" ) group.add_argument( "--dest", help="The destination node id for any sent commands, if not set '^all' or '^local' is assumed as appropriate", default=None, ) group.add_argument( "--sendtext", help="Send a text message. Can specify a destination '--dest' and/or channel index '--ch-index'.", ) group.add_argument( "--traceroute", help="Traceroute from connected node to a destination. " "You need pass the destination ID as argument, like " "this: '--traceroute !ba4bf9d0' " "Only nodes that have the encryption key can be traced.", ) group.add_argument( "--request-telemetry", help="Request telemetry from a node. " "You need to pass the destination ID as argument with '--dest'. " "For repeaters, the nodeNum is required.", action="store_true", ) group.add_argument( "--request-position", help="Request the position from a node. " "You need to pass the destination ID as an argument with '--dest'. " "For repeaters, the nodeNum is required.", action="store_true", ) group.add_argument( "--ack", help="Use in combination with --sendtext to wait for an acknowledgment.", action="store_true", ) group.add_argument( "--reboot", help="Tell the destination node to reboot", action="store_true" ) group.add_argument( "--reboot-ota", help="Tell the destination node to reboot into factory firmware (ESP32)", action="store_true", ) group.add_argument( "--enter-dfu", help="Tell the destination node to enter DFU mode (NRF52)", action="store_true", ) group.add_argument( "--shutdown", help="Tell the destination node to shutdown", action="store_true" ) group.add_argument( "--device-metadata", help="Get the device metadata from the node", action="store_true", ) group.add_argument( "--begin-edit", help="Tell the node to open a transaction to edit settings", action="store_true", ) group.add_argument( "--commit-edit", help="Tell the node to commit open settings transaction", action="store_true", ) group.add_argument( "--factory-reset", help="Tell the destination node to install the default config", action="store_true", ) group.add_argument( "--remove-node", help="Tell the destination node to remove a specific node from its DB, by node number or ID" ) group.add_argument( "--reset-nodedb", help="Tell the destination node to clear its list of nodes", action="store_true", ) group.add_argument( "--reply", help="Reply to received messages", action="store_true" ) group.add_argument( "--no-time", help="Suppress sending the current time to the mesh", action="store_true", ) group.add_argument( "--no-nodes", help="Request that the node not send node info to the client. " "Will break things that depend on the nodedb, but will speed up startup. Requires 2.3.11+ firmware.", action="store_true", ) group.add_argument( "--setalt", help="Set device altitude in meters (allows use without GPS), and enable fixed position.", ) group.add_argument( "--setlat", help="Set device latitude (allows use without GPS), and enable fixed position. Accepts a decimal value or an integer premultiplied by 1e7.", ) group.add_argument( "--setlon", help="Set device longitude (allows use without GPS), and enable fixed position. Accepts a decimal value or an integer premultiplied by 1e7.", ) group.add_argument( "--remove-position", help="Clear any existing fixed position and disable fixed position.", action="store_true", ) group.add_argument( "--pos-fields", help="Specify fields to send when sending a position. Use no argument for a list of valid values. " "Can pass multiple values as a space separated list like " "this: '--pos-fields ALTITUDE HEADING SPEED'", nargs="*", action="store", ) group.add_argument( "--debug", help="Show API library debug log messages", action="store_true" ) group.add_argument( "--test", help="Run stress test against all connected Meshtastic devices", action="store_true", ) group.add_argument( "--ble-scan", help="Scan for Meshtastic BLE devices", action="store_true", ) group.add_argument( "--wait-to-disconnect", help="How many seconds to wait before disconnecting from the device.", const="5", nargs="?", action="store", ) group.add_argument( "--noproto", help="Don't start the API, just function as a dumb serial terminal.", action="store_true", ) group.add_argument( "--listen", help="Just stay open and listen to the protobuf stream. Enables debug logging.", action="store_true", ) remoteHardwareArgs = parser.add_argument_group('Remote Hardware', 'Arguments related to the Remote Hardware module') remoteHardwareArgs.add_argument( "--gpio-wrb", nargs=2, help="Set a particular GPIO # to 1 or 0", action="append" ) remoteHardwareArgs.add_argument( "--gpio-rd", help="Read from a GPIO mask (ex: '0x10')" ) remoteHardwareArgs.add_argument( "--gpio-watch", help="Start watching a GPIO mask for changes (ex: '0x10')" ) have_tunnel = platform.system() == "Linux" if have_tunnel: tunnelArgs = parser.add_argument_group('Tunnel', 'Arguments related to establishing a tunnel device over the mesh.') tunnelArgs.add_argument( "--tunnel", action="store_true", help="Create a TUN tunnel device for forwarding IP packets over the mesh", ) tunnelArgs.add_argument( "--subnet", dest="tunnel_net", help="Sets the local-end subnet address for the TUN IP bridge. (ex: 10.115' which is the default)", default=None, ) parser.set_defaults(deprecated=None) args = parser.parse_args() mt_config.args = args mt_config.parser = parser def main(): """Perform command line meshtastic operations""" parser = argparse.ArgumentParser( add_help=False, epilog="If no connection arguments are specified, we search for a compatible serial device, " "and if none is found, then attempt a TCP connection to localhost.") mt_config.parser = parser initParser() common() logfile = mt_config.logfile if logfile: logfile.close() def tunnelMain(): """Run a meshtastic IP tunnel""" parser = argparse.ArgumentParser(add_help=False) mt_config.parser = parser initParser() args = mt_config.args args.tunnel = True mt_config.args = args common() if __name__ == "__main__": main() python-2.3.14/meshtastic/ble_interface.py000066400000000000000000000272311464266072200204400ustar00rootroot00000000000000"""Bluetooth interface """ import asyncio import atexit import logging import struct import time from threading import Thread from typing import List, Optional import print_color # type: ignore[import-untyped] from bleak import BleakClient, BleakScanner, BLEDevice from bleak.exc import BleakDBusError, BleakError import google.protobuf from meshtastic.mesh_interface import MeshInterface from .protobuf import ( mesh_pb2, ) SERVICE_UUID = "6ba1b218-15a8-461f-9fa8-5dcae273eafd" TORADIO_UUID = "f75c76d2-129e-4dad-a1dd-7866124401e7" FROMRADIO_UUID = "2c55e69e-4993-11ed-b878-0242ac120002" FROMNUM_UUID = "ed9da18c-a800-4f66-a670-aa7547e34453" LEGACY_LOGRADIO_UUID = "6c6fd238-78fa-436b-aacf-15c5be1ef2e2" LOGRADIO_UUID = "5a3d6e49-06e6-4423-9944-e9de8cdf9547" class BLEInterface(MeshInterface): """MeshInterface using BLE to connect to devices.""" class BLEError(Exception): """An exception class for BLE errors.""" def __init__( self, address: Optional[str], noProto: bool = False, debugOut=None, noNodes: bool = False, ): MeshInterface.__init__( self, debugOut=debugOut, noProto=noProto, noNodes=noNodes ) self.should_read = False logging.debug("Threads starting") self._want_receive = True self._receiveThread: Optional[Thread] = Thread( target=self._receiveFromRadioImpl, name="BLEReceive", daemon=True ) self._receiveThread.start() logging.debug("Threads running") try: logging.debug(f"BLE connecting to: {address if address else 'any'}") self.client: Optional[BLEClient] = self.connect(address) logging.debug("BLE connected") except BLEInterface.BLEError as e: self.close() raise e if self.client.has_characteristic(LEGACY_LOGRADIO_UUID): self.client.start_notify(LEGACY_LOGRADIO_UUID, self.legacy_log_radio_handler) if self.client.has_characteristic(LOGRADIO_UUID): self.client.start_notify(LOGRADIO_UUID, self.log_radio_handler) logging.debug("Mesh configure starting") self._startConfig() if not self.noProto: self._waitConnected(timeout=60.0) self.waitForConfig() logging.debug("Register FROMNUM notify callback") self.client.start_notify(FROMNUM_UUID, self.from_num_handler) # We MUST run atexit (if we can) because otherwise (at least on linux) the BLE device is not disconnected # and future connection attempts will fail. (BlueZ kinda sucks) # Note: the on disconnected callback will call our self.close which will make us nicely wait for threads to exit self._exit_handler = atexit.register(self.client.disconnect) def from_num_handler(self, _, b): # pylint: disable=C0116 """Handle callbacks for fromnum notify. Note: this method does not need to be async because it is just setting a bool. """ from_num = struct.unpack(" List[BLEDevice]: """Scan for available BLE devices.""" with BLEClient() as client: logging.info("Scanning for BLE devices (takes 10 seconds)...") response = client.discover( timeout=10, return_adv=True, service_uuids=[SERVICE_UUID] ) devices = response.values() # bleak sometimes returns devices we didn't ask for, so filter the response # to only return true meshtastic devices # d[0] is the device. d[1] is the advertisement data devices = list( filter(lambda d: SERVICE_UUID in d[1].service_uuids, devices) ) return list(map(lambda d: d[0], devices)) def find_device(self, address: Optional[str]) -> BLEDevice: """Find a device by address.""" addressed_devices = BLEInterface.scan() if address: addressed_devices = list( filter( lambda x: address in (x.name, x.address), addressed_devices, ) ) if len(addressed_devices) == 0: raise BLEInterface.BLEError( f"No Meshtastic BLE peripheral with identifier or address '{address}' found. Try --ble-scan to find it." ) if len(addressed_devices) > 1: raise BLEInterface.BLEError( f"More than one Meshtastic BLE peripheral with identifier or address '{address}' found." ) return addressed_devices[0] def _sanitize_address(address): # pylint: disable=E0213 "Standardize BLE address by removing extraneous characters and lowercasing." return address.replace("-", "").replace("_", "").replace(":", "").lower() def connect(self, address: Optional[str] = None) -> "BLEClient": "Connect to a device by address." # Bleak docs recommend always doing a scan before connecting (even if we know addr) device = self.find_device(address) client = BLEClient(device.address, disconnected_callback=lambda _: self.close) client.connect() client.discover() return client def _receiveFromRadioImpl(self): while self._want_receive: if self.should_read: self.should_read = False retries = 0 while self._want_receive: try: b = bytes(self.client.read_gatt_char(FROMRADIO_UUID)) except BleakDBusError as e: # Device disconnected probably, so end our read loop immediately logging.debug(f"Device disconnected, shutting down {e}") self._want_receive = False except BleakError as e: # We were definitely disconnected if "Not connected" in str(e): logging.debug(f"Device disconnected, shutting down {e}") self._want_receive = False else: raise BLEInterface.BLEError("Error reading BLE") from e if not b: if retries < 5: time.sleep(0.1) retries += 1 continue break logging.debug(f"FROMRADIO read: {b.hex()}") self._handleFromRadio(b) else: time.sleep(0.01) def _sendToRadioImpl(self, toRadio): b = toRadio.SerializeToString() if b: logging.debug(f"TORADIO write: {b.hex()}") try: self.client.write_gatt_char( TORADIO_UUID, b, response=True ) # FIXME: or False? # search Bleak src for org.bluez.Error.InProgress except Exception as e: raise BLEInterface.BLEError( "Error writing BLE (are you in the 'bluetooth' user group? did you enter the pairing PIN on your computer?)" ) from e # Allow to propagate and then make sure we read time.sleep(0.01) self.should_read = True def close(self): atexit.unregister(self._exit_handler) try: MeshInterface.close(self) except Exception as e: logging.error(f"Error closing mesh interface: {e}") if self._want_receive: self.want_receive = False # Tell the thread we want it to stop self._receiveThread.join(timeout=2) # If bleak is hung, don't wait for the thread to exit (it is critical we disconnect) self._receiveThread = None if self.client: self.client.disconnect() self.client.close() self.client = None class BLEClient: """Client for managing connection to a BLE device""" def __init__(self, address=None, **kwargs): self._eventLoop = asyncio.new_event_loop() self._eventThread = Thread( target=self._run_event_loop, name="BLEClient", daemon=True ) self._eventThread.start() if not address: logging.debug("No address provided - only discover method will work.") return self.bleak_client = BleakClient(address, **kwargs) def discover(self, **kwargs): # pylint: disable=C0116 return self.async_await(BleakScanner.discover(**kwargs)) def pair(self, **kwargs): # pylint: disable=C0116 return self.async_await(self.bleak_client.pair(**kwargs)) def connect(self, **kwargs): # pylint: disable=C0116 return self.async_await(self.bleak_client.connect(**kwargs)) def disconnect(self, **kwargs): # pylint: disable=C0116 self.async_await(self.bleak_client.disconnect(**kwargs)) def read_gatt_char(self, *args, **kwargs): # pylint: disable=C0116 return self.async_await(self.bleak_client.read_gatt_char(*args, **kwargs)) def write_gatt_char(self, *args, **kwargs): # pylint: disable=C0116 self.async_await(self.bleak_client.write_gatt_char(*args, **kwargs)) def has_characteristic(self, specifier): """Check if the connected node supports a specified characteristic.""" return bool(self.bleak_client.services.get_characteristic(specifier)) def start_notify(self, *args, **kwargs): # pylint: disable=C0116 self.async_await(self.bleak_client.start_notify(*args, **kwargs)) def close(self): # pylint: disable=C0116 self.async_run(self._stop_event_loop()) self._eventThread.join() def __enter__(self): return self def __exit__(self, _type, _value, _traceback): self.close() def async_await(self, coro, timeout=None): # pylint: disable=C0116 return self.async_run(coro).result(timeout) def async_run(self, coro): # pylint: disable=C0116 return asyncio.run_coroutine_threadsafe(coro, self._eventLoop) def _run_event_loop(self): try: self._eventLoop.run_forever() finally: self._eventLoop.close() async def _stop_event_loop(self): self._eventLoop.stop() python-2.3.14/meshtastic/mesh_interface.py000066400000000000000000001406221464266072200206320ustar00rootroot00000000000000"""Mesh Interface class """ import collections import json import logging import random import sys import threading import time from datetime import datetime from decimal import Decimal from typing import Any, Callable, Dict, List, Optional, Union import google.protobuf.json_format from pubsub import pub # type: ignore[import-untyped] from tabulate import tabulate import meshtastic.node from meshtastic.protobuf import ( mesh_pb2, portnums_pb2, telemetry_pb2, ) from meshtastic import ( BROADCAST_ADDR, BROADCAST_NUM, LOCAL_ADDR, NODELESS_WANT_CONFIG_ID, ResponseHandler, protocols, publishingThread, ) from meshtastic.util import ( Acknowledgment, Timeout, convert_mac_addr, our_exit, remove_keys_from_dict, stripnl, message_to_json, ) def _timeago(delta_secs: int) -> str: """Convert a number of seconds in the past into a short, friendly string e.g. "now", "30 sec ago", "1 hour ago" Zero or negative intervals simply return "now" """ intervals = ( ("year", 60 * 60 * 24 * 365), ("month", 60 * 60 * 24 * 30), ("day", 60 * 60 * 24), ("hour", 60 * 60), ("min", 60), ("sec", 1), ) for name, interval_duration in intervals: if delta_secs < interval_duration: continue x = delta_secs // interval_duration plur = "s" if x > 1 else "" return f"{x} {name}{plur} ago" return "now" class MeshInterface: # pylint: disable=R0902 """Interface class for meshtastic devices Properties: isConnected nodes debugOut """ class MeshInterfaceError(Exception): """An exception class for general mesh interface errors""" def __init__(self, message): self.message = message super().__init__(self.message) def __init__(self, debugOut=None, noProto: bool=False, noNodes: bool=False) -> None: """Constructor Keyword Arguments: noProto -- If True, don't try to run our protocol on the link - just be a dumb serial client. noNodes -- If True, instruct the node to not send its nodedb on startup, just other configuration information. """ self.debugOut = debugOut self.nodes: Optional[Dict[str,Dict]] = None # FIXME self.isConnected: threading.Event = threading.Event() self.noProto: bool = noProto self.localNode: meshtastic.node.Node = meshtastic.node.Node(self, -1) # We fixup nodenum later self.myInfo: Optional[mesh_pb2.MyNodeInfo] = None # We don't have device info yet self.metadata: Optional[mesh_pb2.DeviceMetadata] = None # We don't have device metadata yet self.responseHandlers: Dict[int,ResponseHandler] = {} # A map from request ID to the handler self.failure = ( None # If we've encountered a fatal exception it will be kept here ) self._timeout: Timeout = Timeout() self._acknowledgment: Acknowledgment = Acknowledgment() self.heartbeatTimer: Optional[threading.Timer] = None random.seed() # FIXME, we should not clobber the random seedval here, instead tell user they must call it self.currentPacketId: int = random.randint(0, 0xFFFFFFFF) self.nodesByNum: Optional[Dict[int, Dict]] = None self.noNodes: bool = noNodes self.configId: Optional[int] = NODELESS_WANT_CONFIG_ID if noNodes else None self.gotResponse: bool = False # used in gpio read self.mask: Optional[int] = None # used in gpio read and gpio watch self.queueStatus: Optional[mesh_pb2.QueueStatus] = None self.queue: collections.OrderedDict = collections.OrderedDict() self._localChannels = None def close(self): """Shutdown this interface""" if self.heartbeatTimer: self.heartbeatTimer.cancel() self._sendDisconnect() def __enter__(self): return self def __exit__(self, exc_type, exc_value, traceback): if exc_type is not None and exc_value is not None: logging.error( f"An exception of type {exc_type} with value {exc_value} has occurred" ) if traceback is not None: logging.error(f"Traceback: {traceback}") self.close() def showInfo(self, file=sys.stdout) -> str: # pylint: disable=W0613 """Show human readable summary about this object""" owner = f"Owner: {self.getLongName()} ({self.getShortName()})" myinfo = "" if self.myInfo: myinfo = f"\nMy info: {message_to_json(self.myInfo)}" metadata = "" if self.metadata: metadata = f"\nMetadata: {message_to_json(self.metadata)}" mesh = "\n\nNodes in mesh: " nodes = {} if self.nodes: for n in self.nodes.values(): # when the TBeam is first booted, it sometimes shows the raw data # so, we will just remove any raw keys keys_to_remove = ("raw", "decoded", "payload") n2 = remove_keys_from_dict(keys_to_remove, n) # if we have 'macaddr', re-format it if "macaddr" in n2["user"]: val = n2["user"]["macaddr"] # decode the base64 value addr = convert_mac_addr(val) n2["user"]["macaddr"] = addr # use id as dictionary key for correct json format in list of nodes nodeid = n2["user"]["id"] nodes[nodeid] = n2 infos = owner + myinfo + metadata + mesh + json.dumps(nodes, indent=2) print(infos) return infos def showNodes(self, includeSelf: bool=True, file=sys.stdout) -> str: # pylint: disable=W0613 """Show table summary of nodes in mesh""" def formatFloat(value, precision=2, unit="") -> Optional[str]: """Format a float value with precision.""" return f"{value:.{precision}f}{unit}" if value else None def getLH(ts) -> Optional[str]: """Format last heard""" return ( datetime.fromtimestamp(ts).strftime("%Y-%m-%d %H:%M:%S") if ts else None ) def getTimeAgo(ts) -> Optional[str]: """Format how long ago have we heard from this node (aka timeago).""" if ts is None: return None delta = datetime.now() - datetime.fromtimestamp(ts) delta_secs = int(delta.total_seconds()) if delta_secs < 0: return None # not handling a timestamp from the future return _timeago(delta_secs) rows: List[Dict[str, Any]] = [] if self.nodesByNum: logging.debug(f"self.nodes:{self.nodes}") for node in self.nodesByNum.values(): if not includeSelf and node["num"] == self.localNode.nodeNum: continue presumptive_id = f"!{node['num']:08x}" row = {"N": 0, "User": f"Meshtastic {presumptive_id[-4:]}", "ID": presumptive_id} user = node.get("user") if user: row.update( { "User": user.get("longName", "N/A"), "AKA": user.get("shortName", "N/A"), "ID": user["id"], "Hardware": user.get("hwModel", "UNSET") } ) pos = node.get("position") if pos: row.update( { "Latitude": formatFloat(pos.get("latitude"), 4, "°"), "Longitude": formatFloat(pos.get("longitude"), 4, "°"), "Altitude": formatFloat(pos.get("altitude"), 0, " m"), } ) metrics = node.get("deviceMetrics") if metrics: batteryLevel = metrics.get("batteryLevel") if batteryLevel is not None: if batteryLevel == 0: batteryString = "Powered" else: batteryString = str(batteryLevel) + "%" row.update({"Battery": batteryString}) row.update( { "Channel util.": formatFloat( metrics.get("channelUtilization"), 2, "%" ), "Tx air util.": formatFloat( metrics.get("airUtilTx"), 2, "%" ), } ) row.update( { "SNR": formatFloat(node.get("snr"), 2, " dB"), "Hops Away": node.get("hopsAway", "0/unknown"), "Channel": node.get("channel", 0), "LastHeard": getLH(node.get("lastHeard")), "Since": getTimeAgo(node.get("lastHeard")), } ) rows.append(row) rows.sort(key=lambda r: r.get("LastHeard") or "0000", reverse=True) for i, row in enumerate(rows): row["N"] = i + 1 table = tabulate(rows, headers="keys", missingval="N/A", tablefmt="fancy_grid") print(table) return table def getNode(self, nodeId: str, requestChannels: bool=True) -> meshtastic.node.Node: """Return a node object which contains device settings and channel info""" if nodeId in (LOCAL_ADDR, BROADCAST_ADDR): return self.localNode else: n = meshtastic.node.Node(self, nodeId) # Only request device settings and channel info when necessary if requestChannels: logging.debug("About to requestChannels") n.requestChannels() if not n.waitForConfig(): our_exit("Error: Timed out waiting for channels") return n def sendText( self, text: str, destinationId: Union[int, str]=BROADCAST_ADDR, wantAck: bool=False, wantResponse: bool=False, onResponse: Optional[Callable[[dict], Any]]=None, channelIndex: int=0, ): """Send a utf8 string to some other node, if the node has a display it will also be shown on the device. Arguments: text {string} -- The text to send Keyword Arguments: destinationId {nodeId or nodeNum} -- where to send this message (default: {BROADCAST_ADDR}) portNum -- the application portnum (similar to IP port numbers) of the destination, see portnums.proto for a list wantAck -- True if you want the message sent in a reliable manner (with retries and ack/nak provided for delivery) wantResponse -- True if you want the service on the other side to send an application layer response Returns the sent packet. The id field will be populated in this packet and can be used to track future message acks/naks. """ return self.sendData( text.encode("utf-8"), destinationId, portNum=portnums_pb2.PortNum.TEXT_MESSAGE_APP, wantAck=wantAck, wantResponse=wantResponse, onResponse=onResponse, channelIndex=channelIndex, ) def sendData( self, data, destinationId: Union[int, str]=BROADCAST_ADDR, portNum: portnums_pb2.PortNum.ValueType=portnums_pb2.PortNum.PRIVATE_APP, wantAck: bool=False, wantResponse: bool=False, onResponse: Optional[Callable[[dict], Any]]=None, onResponseAckPermitted: bool=False, channelIndex: int=0, hopLimit: Optional[int]=None, ): """Send a data packet to some other node Keyword Arguments: data -- the data to send, either as an array of bytes or as a protobuf (which will be automatically serialized to bytes) destinationId {nodeId or nodeNum} -- where to send this message (default: {BROADCAST_ADDR}) portNum -- the application portnum (similar to IP port numbers) of the destination, see portnums.proto for a list wantAck -- True if you want the message sent in a reliable manner (with retries and ack/nak provided for delivery) wantResponse -- True if you want the service on the other side to send an application layer response onResponse -- A closure of the form funct(packet), that will be called when a response packet arrives (or the transaction is NAKed due to non receipt) onResponseAckPermitted -- should the onResponse callback be called for regular ACKs (True) or just data responses & NAKs (False) Note that if the onResponse callback is called 'onAckNak' this will implicitly be true. channelIndex -- channel number to use hopLimit -- hop limit to use Returns the sent packet. The id field will be populated in this packet and can be used to track future message acks/naks. """ if getattr(data, "SerializeToString", None): logging.debug(f"Serializing protobuf as data: {stripnl(data)}") data = data.SerializeToString() logging.debug(f"len(data): {len(data)}") logging.debug( f"mesh_pb2.Constants.DATA_PAYLOAD_LEN: {mesh_pb2.Constants.DATA_PAYLOAD_LEN}" ) if len(data) > mesh_pb2.Constants.DATA_PAYLOAD_LEN: raise MeshInterface.MeshInterfaceError("Data payload too big") if ( portNum == portnums_pb2.PortNum.UNKNOWN_APP ): # we are now more strict wrt port numbers our_exit("Warning: A non-zero port number must be specified") meshPacket = mesh_pb2.MeshPacket() meshPacket.channel = channelIndex meshPacket.decoded.payload = data meshPacket.decoded.portnum = portNum meshPacket.decoded.want_response = wantResponse meshPacket.id = self._generatePacketId() if onResponse is not None: logging.debug(f"Setting a response handler for requestId {meshPacket.id}") self._addResponseHandler(meshPacket.id, onResponse, ackPermitted=onResponseAckPermitted) p = self._sendPacket(meshPacket, destinationId, wantAck=wantAck, hopLimit=hopLimit) return p def sendPosition( self, latitude: float=0.0, longitude: float=0.0, altitude: int=0, timeSec: int=0, destinationId: Union[int, str]=BROADCAST_ADDR, wantAck: bool=False, wantResponse: bool=False, channelIndex: int=0, ): """ Send a position packet to some other node (normally a broadcast) Also, the device software will notice this packet and use it to automatically set its notion of the local position. If timeSec is not specified (recommended), we will use the local machine time. Returns the sent packet. The id field will be populated in this packet and can be used to track future message acks/naks. """ p = mesh_pb2.Position() if latitude != 0.0: p.latitude_i = int(latitude / 1e-7) logging.debug(f"p.latitude_i:{p.latitude_i}") if longitude != 0.0: p.longitude_i = int(longitude / 1e-7) logging.debug(f"p.longitude_i:{p.longitude_i}") if altitude != 0: p.altitude = int(altitude) logging.debug(f"p.altitude:{p.altitude}") if timeSec == 0: timeSec = int(time.time()) # returns unix timestamp in seconds p.time = timeSec logging.debug(f"p.time:{p.time}") if wantResponse: onResponse = self.onResponsePosition else: onResponse = None d = self.sendData( p, destinationId, portNum=portnums_pb2.PortNum.POSITION_APP, wantAck=wantAck, wantResponse=wantResponse, onResponse=onResponse, channelIndex=channelIndex, ) if wantResponse: self.waitForPosition() return d def onResponsePosition(self, p): """on response for position""" if p["decoded"]["portnum"] == 'POSITION_APP': self._acknowledgment.receivedPosition = True position = mesh_pb2.Position() position.ParseFromString(p["decoded"]["payload"]) ret = "Position received: " if position.latitude_i != 0 and position.longitude_i != 0: ret += f"({position.latitude_i * 10**-7}, {position.longitude_i * 10**-7})" else: ret += "(unknown)" if position.altitude != 0: ret += f" {position.altitude}m" if position.precision_bits not in [0,32]: ret += f" precision:{position.precision_bits}" elif position.precision_bits == 32: ret += " full precision" elif position.precision_bits == 0: ret += " position disabled" print(ret) elif p["decoded"]["portnum"] == 'ROUTING_APP': if p["decoded"]["routing"]["errorReason"] == 'NO_RESPONSE': our_exit("No response from node. At least firmware 2.1.22 is required on the destination node.") def sendTraceRoute(self, dest: Union[int, str], hopLimit: int, channelIndex: int=0): """Send the trace route""" r = mesh_pb2.RouteDiscovery() self.sendData( r, destinationId=dest, portNum=portnums_pb2.PortNum.TRACEROUTE_APP, wantResponse=True, onResponse=self.onResponseTraceRoute, channelIndex=channelIndex, hopLimit=hopLimit, ) # extend timeout based on number of nodes, limit by configured hopLimit waitFactor = min(len(self.nodes) - 1 if self.nodes else 0, hopLimit) self.waitForTraceRoute(waitFactor) def onResponseTraceRoute(self, p: dict): """on response for trace route""" routeDiscovery = mesh_pb2.RouteDiscovery() routeDiscovery.ParseFromString(p["decoded"]["payload"]) asDict = google.protobuf.json_format.MessageToDict(routeDiscovery) print("Route traced:") routeStr = self._nodeNumToId(p["to"]) or f"{p['to']:08x}" if "route" in asDict: for nodeNum in asDict["route"]: routeStr += " --> " + (self._nodeNumToId(nodeNum) or f"{nodeNum:08x}") routeStr += " --> " + (self._nodeNumToId(p["from"]) or f"{p['from']:08x}") print(routeStr) self._acknowledgment.receivedTraceRoute = True def sendTelemetry(self, destinationId: Union[int,str]=BROADCAST_ADDR, wantResponse: bool=False, channelIndex: int=0): """Send telemetry and optionally ask for a response""" r = telemetry_pb2.Telemetry() if self.nodes is not None: node = next(n for n in self.nodes.values() if n["num"] == self.localNode.nodeNum) if node is not None: metrics = node.get("deviceMetrics") if metrics: batteryLevel = metrics.get("batteryLevel") if batteryLevel is not None: r.device_metrics.battery_level = batteryLevel voltage = metrics.get("voltage") if voltage is not None: r.device_metrics.voltage = voltage channel_utilization = metrics.get("channelUtilization") if channel_utilization is not None: r.device_metrics.channel_utilization = channel_utilization air_util_tx = metrics.get("airUtilTx") if air_util_tx is not None: r.device_metrics.air_util_tx = air_util_tx if wantResponse: onResponse = self.onResponseTelemetry else: onResponse = None self.sendData( r, destinationId=destinationId, portNum=portnums_pb2.PortNum.TELEMETRY_APP, wantResponse=wantResponse, onResponse=onResponse, channelIndex=channelIndex, ) if wantResponse: self.waitForTelemetry() def onResponseTelemetry(self, p: dict): """on response for telemetry""" if p["decoded"]["portnum"] == 'TELEMETRY_APP': self._acknowledgment.receivedTelemetry = True telemetry = telemetry_pb2.Telemetry() telemetry.ParseFromString(p["decoded"]["payload"]) print("Telemetry received:") if telemetry.device_metrics.battery_level is not None: print(f"Battery level: {telemetry.device_metrics.battery_level:.2f}%") if telemetry.device_metrics.voltage is not None: print(f"Voltage: {telemetry.device_metrics.voltage:.2f} V") if telemetry.device_metrics.channel_utilization is not None: print( f"Total channel utilization: {telemetry.device_metrics.channel_utilization:.2f}%" ) if telemetry.device_metrics.air_util_tx is not None: print(f"Transmit air utilization: {telemetry.device_metrics.air_util_tx:.2f}%") elif p["decoded"]["portnum"] == 'ROUTING_APP': if p["decoded"]["routing"]["errorReason"] == 'NO_RESPONSE': our_exit("No response from node. At least firmware 2.1.22 is required on the destination node.") def _addResponseHandler(self, requestId: int, callback: Callable[[dict], Any], ackPermitted: bool=False): self.responseHandlers[requestId] = ResponseHandler(callback=callback, ackPermitted=ackPermitted) def _sendPacket( self, meshPacket: mesh_pb2.MeshPacket, destinationId: Union[int,str]=BROADCAST_ADDR, wantAck: bool=False, hopLimit: Optional[int]=None ): """Send a MeshPacket to the specified node (or if unspecified, broadcast). You probably don't want this - use sendData instead. Returns the sent packet. The id field will be populated in this packet and can be used to track future message acks/naks. """ # We allow users to talk to the local node before we've completed the full connection flow... if self.myInfo is not None and destinationId != self.myInfo.my_node_num: self._waitConnected() toRadio = mesh_pb2.ToRadio() nodeNum: int = 0 if destinationId is None: our_exit("Warning: destinationId must not be None") elif isinstance(destinationId, int): nodeNum = destinationId elif destinationId == BROADCAST_ADDR: nodeNum = BROADCAST_NUM elif destinationId == LOCAL_ADDR: if self.myInfo: nodeNum = self.myInfo.my_node_num else: our_exit("Warning: No myInfo found.") # A simple hex style nodeid - we can parse this without needing the DB elif destinationId.startswith("!"): nodeNum = int(destinationId[1:], 16) else: if self.nodes: node = self.nodes.get(destinationId) if node is None: our_exit(f"Warning: NodeId {destinationId} not found in DB") else: nodeNum = node["num"] else: logging.warning("Warning: There were no self.nodes.") meshPacket.to = nodeNum meshPacket.want_ack = wantAck if hopLimit is not None: meshPacket.hop_limit = hopLimit else: loraConfig = getattr(self.localNode.localConfig, "lora") meshPacket.hop_limit = getattr(loraConfig, "hop_limit") # if the user hasn't set an ID for this packet (likely and recommended), # we should pick a new unique ID so the message can be tracked. if meshPacket.id == 0: meshPacket.id = self._generatePacketId() toRadio.packet.CopyFrom(meshPacket) if self.noProto: logging.warning( f"Not sending packet because protocol use is disabled by noProto" ) else: logging.debug(f"Sending packet: {stripnl(meshPacket)}") self._sendToRadio(toRadio) return meshPacket def waitForConfig(self): """Block until radio config is received. Returns True if config has been received.""" success = ( self._timeout.waitForSet(self, attrs=("myInfo", "nodes")) and self.localNode.waitForConfig() ) if not success: raise MeshInterface.MeshInterfaceError("Timed out waiting for interface config") def waitForAckNak(self): """Wait for the ack/nak""" success = self._timeout.waitForAckNak(self._acknowledgment) if not success: raise MeshInterface.MeshInterfaceError("Timed out waiting for an acknowledgment") def waitForTraceRoute(self, waitFactor): """Wait for trace route""" success = self._timeout.waitForTraceRoute(waitFactor, self._acknowledgment) if not success: raise MeshInterface.MeshInterfaceError("Timed out waiting for traceroute") def waitForTelemetry(self): """Wait for telemetry""" success = self._timeout.waitForTelemetry(self._acknowledgment) if not success: raise MeshInterface.MeshInterfaceError("Timed out waiting for telemetry") def waitForPosition(self): """Wait for position""" success = self._timeout.waitForPosition(self._acknowledgment) if not success: raise MeshInterface.MeshInterfaceError("Timed out waiting for position") def getMyNodeInfo(self) -> Optional[Dict]: """Get info about my node.""" if self.myInfo is None or self.nodesByNum is None: return None logging.debug(f"self.nodesByNum:{self.nodesByNum}") return self.nodesByNum.get(self.myInfo.my_node_num) def getMyUser(self): """Get user""" nodeInfo = self.getMyNodeInfo() if nodeInfo is not None: return nodeInfo.get("user") return None def getLongName(self): """Get long name""" user = self.getMyUser() if user is not None: return user.get("longName", None) return None def getShortName(self): """Get short name""" user = self.getMyUser() if user is not None: return user.get("shortName", None) return None def _waitConnected(self, timeout=30.0): """Block until the initial node db download is complete, or timeout and raise an exception""" if not self.noProto: if not self.isConnected.wait(timeout): # timeout after x seconds raise MeshInterface.MeshInterfaceError("Timed out waiting for connection completion") # If we failed while connecting, raise the connection to the client if self.failure: raise self.failure def _generatePacketId(self) -> int: """Get a new unique packet ID""" if self.currentPacketId is None: raise MeshInterface.MeshInterfaceError("Not connected yet, can not generate packet") else: self.currentPacketId = (self.currentPacketId + 1) & 0xFFFFFFFF return self.currentPacketId def _disconnected(self): """Called by subclasses to tell clients this interface has disconnected""" self.isConnected.clear() publishingThread.queueWork( lambda: pub.sendMessage("meshtastic.connection.lost", interface=self) ) def _startHeartbeat(self): """We need to send a heartbeat message to the device every X seconds""" def callback(): self.heartbeatTimer = None i = 300 logging.debug(f"Sending heartbeat, interval {i} seconds") if i != 0: self.heartbeatTimer = threading.Timer(i, callback) self.heartbeatTimer.start() p = mesh_pb2.ToRadio() p.heartbeat.CopyFrom(mesh_pb2.Heartbeat()) self._sendToRadio(p) callback() # run our periodic callback now, it will make another timer if necessary def _connected(self): """Called by this class to tell clients we are now fully connected to a node""" # (because I'm lazy) _connected might be called when remote Node # objects complete their config reads, don't generate redundant isConnected # for the local interface if not self.isConnected.is_set(): self.isConnected.set() self._startHeartbeat() publishingThread.queueWork( lambda: pub.sendMessage( "meshtastic.connection.established", interface=self ) ) def _startConfig(self): """Start device packets flowing""" self.myInfo = None self.nodes = {} # nodes keyed by ID self.nodesByNum = {} # nodes keyed by nodenum self._localChannels = [] # empty until we start getting channels pushed from the device (during config) startConfig = mesh_pb2.ToRadio() if self.configId is None or not self.noNodes: self.configId = random.randint(0, 0xFFFFFFFF) startConfig.want_config_id = self.configId self._sendToRadio(startConfig) def _sendDisconnect(self): """Tell device we are done using it""" m = mesh_pb2.ToRadio() m.disconnect = True self._sendToRadio(m) def _queueHasFreeSpace(self) -> bool: # We never got queueStatus, maybe the firmware is old if self.queueStatus is None: return True return self.queueStatus.free > 0 def _queueClaim(self) -> None: if self.queueStatus is None: return self.queueStatus.free -= 1 def _sendToRadio(self, toRadio: mesh_pb2.ToRadio) -> None: """Send a ToRadio protobuf to the device""" if self.noProto: logging.warning( f"Not sending packet because protocol use is disabled by noProto" ) else: # logging.debug(f"Sending toRadio: {stripnl(toRadio)}") if not toRadio.HasField("packet"): # not a meshpacket -- send immediately, give queue a chance, # this makes heartbeat trigger queue self._sendToRadioImpl(toRadio) else: # meshpacket -- queue self.queue[toRadio.packet.id] = toRadio resentQueue = collections.OrderedDict() while self.queue: # logging.warn("queue: " + " ".join(f'{k:08x}' for k in self.queue)) while not self._queueHasFreeSpace(): logging.debug("Waiting for free space in TX Queue") time.sleep(0.5) try: toResend = self.queue.popitem(last=False) except KeyError: break packetId, packet = toResend # logging.warn(f"packet: {packetId:08x} {packet}") resentQueue[packetId] = packet if packet is False: continue self._queueClaim() if packet != toRadio: logging.debug(f"Resending packet ID {packetId:08x} {packet}") self._sendToRadioImpl(packet) # logging.warn("resentQueue: " + " ".join(f'{k:08x}' for k in resentQueue)) for packetId, packet in resentQueue.items(): if ( self.queue.pop(packetId, False) is False ): # Packet got acked under us logging.debug(f"packet {packetId:08x} got acked under us") continue if packet: self.queue[packetId] = packet # logging.warn("queue + resentQueue: " + " ".join(f'{k:08x}' for k in self.queue)) def _sendToRadioImpl(self, toRadio: mesh_pb2.ToRadio) -> None: """Send a ToRadio protobuf to the device""" logging.error(f"Subclass must provide toradio: {toRadio}") def _handleConfigComplete(self) -> None: """ Done with initial config messages, now send regular MeshPackets to ask for settings and channels """ # This is no longer necessary because the current protocol statemachine has already proactively sent us the locally visible channels # self.localNode.requestChannels() self.localNode.setChannels(self._localChannels) # the following should only be called after we have settings and channels self._connected() # Tell everyone else we are ready to go def _handleQueueStatusFromRadio(self, queueStatus) -> None: self.queueStatus = queueStatus logging.debug( f"TX QUEUE free {queueStatus.free} of {queueStatus.maxlen}, res = {queueStatus.res}, id = {queueStatus.mesh_packet_id:08x} " ) if queueStatus.res: return # logging.warn("queue: " + " ".join(f'{k:08x}' for k in self.queue)) justQueued = self.queue.pop(queueStatus.mesh_packet_id, None) if justQueued is None and queueStatus.mesh_packet_id != 0: self.queue[queueStatus.mesh_packet_id] = False logging.debug( f"Reply for unexpected packet ID {queueStatus.mesh_packet_id:08x}" ) # logging.warn("queue: " + " ".join(f'{k:08x}' for k in self.queue)) def _handleFromRadio(self, fromRadioBytes): """ Handle a packet that arrived from the radio(update model and publish events) Called by subclasses.""" fromRadio = mesh_pb2.FromRadio() fromRadio.ParseFromString(fromRadioBytes) logging.debug( f"in mesh_interface.py _handleFromRadio() fromRadioBytes: {fromRadioBytes}" ) asDict = google.protobuf.json_format.MessageToDict(fromRadio) logging.debug(f"Received from radio: {fromRadio}") if fromRadio.HasField("my_info"): self.myInfo = fromRadio.my_info self.localNode.nodeNum = self.myInfo.my_node_num logging.debug(f"Received myinfo: {stripnl(fromRadio.my_info)}") failmsg = None if failmsg: self.failure = MeshInterface.MeshInterfaceError(failmsg) self.isConnected.set() # let waitConnected return this exception self.close() elif fromRadio.HasField("metadata"): self.metadata = fromRadio.metadata logging.debug(f"Received device metadata: {stripnl(fromRadio.metadata)}") elif fromRadio.HasField("node_info"): logging.debug(f"Received nodeinfo: {asDict['nodeInfo']}") node = self._getOrCreateByNum(asDict["nodeInfo"]["num"]) node.update(asDict["nodeInfo"]) try: newpos = self._fixupPosition(node["position"]) node["position"] = newpos except: logging.debug("Node without position") # no longer necessary since we're mutating directly in nodesByNum via _getOrCreateByNum #self.nodesByNum[node["num"]] = node if "user" in node: # Some nodes might not have user/ids assigned yet if "id" in node["user"]: self.nodes[node["user"]["id"]] = node publishingThread.queueWork( lambda: pub.sendMessage( "meshtastic.node.updated", node=node, interface=self ) ) elif fromRadio.config_complete_id == self.configId: # we ignore the config_complete_id, it is unneeded for our # stream API fromRadio.config_complete_id logging.debug(f"Config complete ID {self.configId}") self._handleConfigComplete() elif fromRadio.HasField("channel"): self._handleChannel(fromRadio.channel) elif fromRadio.HasField("packet"): self._handlePacketFromRadio(fromRadio.packet) elif fromRadio.HasField("queueStatus"): self._handleQueueStatusFromRadio(fromRadio.queueStatus) elif fromRadio.HasField("mqttClientProxyMessage"): publishingThread.queueWork( lambda: pub.sendMessage( "meshtastic.mqttclientproxymessage", proxymessage=fromRadio.mqttClientProxyMessage, interface=self ) ) elif fromRadio.HasField("xmodemPacket"): publishingThread.queueWork( lambda: pub.sendMessage( "meshtastic.xmodempacket", packet=fromRadio.xmodemPacket, interface=self ) ) elif fromRadio.HasField("rebooted") and 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.HasField("config") or fromRadio.HasField("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("network"): self.localNode.localConfig.network.CopyFrom(fromRadio.config.network) 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.config.HasField("bluetooth"): self.localNode.localConfig.bluetooth.CopyFrom( fromRadio.config.bluetooth ) 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("store_forward"): self.localNode.moduleConfig.store_forward.CopyFrom( fromRadio.moduleConfig.store_forward ) 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 ) elif fromRadio.moduleConfig.HasField("audio"): self.localNode.moduleConfig.audio.CopyFrom(fromRadio.moduleConfig.audio) elif fromRadio.moduleConfig.HasField("remote_hardware"): self.localNode.moduleConfig.remote_hardware.CopyFrom( fromRadio.moduleConfig.remote_hardware ) elif fromRadio.moduleConfig.HasField("neighbor_info"): self.localNode.moduleConfig.neighbor_info.CopyFrom( fromRadio.moduleConfig.neighbor_info ) elif fromRadio.moduleConfig.HasField("detection_sensor"): self.localNode.moduleConfig.detection_sensor.CopyFrom( fromRadio.moduleConfig.detection_sensor ) elif fromRadio.moduleConfig.HasField("ambient_lighting"): self.localNode.moduleConfig.ambient_lighting.CopyFrom( fromRadio.moduleConfig.ambient_lighting ) elif fromRadio.moduleConfig.HasField("paxcounter"): self.localNode.moduleConfig.paxcounter.CopyFrom( fromRadio.moduleConfig.paxcounter ) else: logging.debug("Unexpected FromRadio payload") def _fixupPosition(self, position: Dict) -> Dict: """Convert integer lat/lon into floats Arguments: position {Position dictionary} -- object to fix up Returns the position with the updated keys """ if "latitudeI" in position: position["latitude"] = float(position["latitudeI"] * Decimal("1e-7")) if "longitudeI" in position: position["longitude"] = float(position["longitudeI"] * Decimal("1e-7")) return position def _nodeNumToId(self, num: int) -> Optional[str]: """Map a node node number to a node ID Arguments: num {int} -- Node number Returns: string -- Node ID """ if num == BROADCAST_NUM: return BROADCAST_ADDR try: return self.nodesByNum[num]["user"]["id"] #type: ignore[index] except: logging.debug(f"Node {num} not found for fromId") return None def _getOrCreateByNum(self, nodeNum): """Given a nodenum find the NodeInfo in the DB (or create if necessary)""" if nodeNum == BROADCAST_NUM: raise MeshInterface.MeshInterfaceError("Can not create/find nodenum by the broadcast num") if nodeNum in self.nodesByNum: return self.nodesByNum[nodeNum] else: presumptive_id = f"!{nodeNum:08x}" n = { "num": nodeNum, "user": { "id": presumptive_id, "longName": f"Meshtastic {presumptive_id[-4:]}", "shortName": f"{presumptive_id[-4:]}", "hwModel": "UNSET" } } # Create a minimal node db entry self.nodesByNum[nodeNum] = n return n def _handleChannel(self, channel): """During initial config the local node will proactively send all N (8) channels it knows""" self._localChannels.append(channel) def _handlePacketFromRadio(self, meshPacket, hack=False): """Handle a MeshPacket that just arrived from the radio hack - well, since we used 'from', which is a python keyword, as an attribute to MeshPacket in protobufs, there really is no way to do something like this: meshPacket = mesh_pb2.MeshPacket() meshPacket.from = 123 If hack is True, we can unit test this code. Will publish one of the following events: - meshtastic.receive.text(packet = MeshPacket dictionary) - meshtastic.receive.position(packet = MeshPacket dictionary) - meshtastic.receive.user(packet = MeshPacket dictionary) - meshtastic.receive.data(packet = MeshPacket dictionary) """ asDict = google.protobuf.json_format.MessageToDict(meshPacket) # We normally decompose the payload into a dictionary so that the client # doesn't need to understand protobufs. But advanced clients might # want the raw protobuf, so we provide it in "raw" asDict["raw"] = meshPacket # from might be missing if the nodenum was zero. if not hack and "from" not in asDict: asDict["from"] = 0 logging.error( f"Device returned a packet we sent, ignoring: {stripnl(asDict)}" ) print( f"Error: Device returned a packet we sent, ignoring: {stripnl(asDict)}" ) return if "to" not in asDict: asDict["to"] = 0 # /add fromId and toId fields based on the node ID try: asDict["fromId"] = self._nodeNumToId(asDict["from"]) except Exception as ex: logging.warning(f"Not populating fromId {ex}") try: asDict["toId"] = self._nodeNumToId(asDict["to"]) except Exception as ex: logging.warning(f"Not populating toId {ex}") # We could provide our objects as DotMaps - which work with . notation or as dictionaries # asObj = DotMap(asDict) topic = "meshtastic.receive" # Generic unknown packet type decoded = None portnum = portnums_pb2.PortNum.Name(portnums_pb2.PortNum.UNKNOWN_APP) 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 # it to prevent confusion if "portnum" not in decoded: decoded["portnum"] = portnum logging.warning(f"portnum was not in decoded. Setting to:{portnum}") else: portnum = decoded["portnum"] topic = f"meshtastic.receive.data.{portnum}" # decode position protobufs and update nodedb, provide decoded version # as "position" in the published msg move the following into a 'decoders' # API that clients could register? portNumInt = meshPacket.decoded.portnum # we want portnum as an int handler = protocols.get(portNumInt) # The decoded protobuf as a dictionary (if we understand this message) p = None if handler is not None: topic = f"meshtastic.receive.{handler.name}" # Convert to protobuf if possible if handler.protobufFactory is not None: pb = handler.protobufFactory() pb.ParseFromString(meshPacket.decoded.payload) p = google.protobuf.json_format.MessageToDict(pb) asDict["decoded"][handler.name] = p # Also provide the protobuf raw asDict["decoded"][handler.name]["raw"] = pb # Call specialized onReceive if necessary if handler.onReceive is not None: handler.onReceive(self, asDict) # Is this message in response to a request, if so, look for a handler requestId = decoded.get("requestId") if requestId is not None: logging.debug(f"Got a response for requestId {requestId}") # We ignore ACK packets unless the callback is named `onAckNak` # or the handler is set as ackPermitted, but send NAKs and # other, data-containing responses to the handlers routing = decoded.get("routing") isAck = routing is not None and ("errorReason" not in routing or routing["errorReason"] == "NONE") # we keep the responseHandler in dict until we actually call it handler = self.responseHandlers.get(requestId, None) if handler is not None: if (not isAck) or handler.callback.__name__ == "onAckNak" or handler.ackPermitted: handler = self.responseHandlers.pop(requestId, None) logging.debug(f"Calling response handler for requestId {requestId}") handler.callback(asDict) logging.debug(f"Publishing {topic}: packet={stripnl(asDict)} ") publishingThread.queueWork( lambda: pub.sendMessage(topic, packet=asDict, interface=self) ) python-2.3.14/meshtastic/mt_config.py000066400000000000000000000022121464266072200176130ustar00rootroot00000000000000""" Globals singleton class. The Global object is gone, as are all its setters and getters. Instead the module itself is the singleton namespace, which can be imported into whichever module is used. The associated tests have also been removed, since we now rely on built in Python mechanisms. This is intended to make the Python read more naturally, and to make the intention of the code clearer and more compact. It is merely a sticking plaster over the use of shared mt_config, but the coupling issues wil be dealt with rather more easily once the code is simplified by this change. """ def reset(): """ Restore the namespace to pristine condition. """ # pylint: disable=W0603 global args, parser, channel_index, logfile, tunnelInstance, camel_case args = None parser = None channel_index = None logfile = None tunnelInstance = None # TODO: to migrate to camel_case for v1.3 change this value to True camel_case = False # These assignments are used instead of calling reset() # purely to shut pylint up. args = None parser = None channel_index = None logfile = None tunnelInstance = None camel_case = False python-2.3.14/meshtastic/node.py000066400000000000000000001014761464266072200166070ustar00rootroot00000000000000"""Node class """ import base64 import logging import time from typing import Union from meshtastic.protobuf import admin_pb2, apponly_pb2, channel_pb2, localonly_pb2, mesh_pb2, portnums_pb2 from meshtastic.util import ( Timeout, camel_to_snake, fromPSK, our_exit, pskToString, stripnl, message_to_json, ) class Node: """A model of a (local or remote) node in the mesh Includes methods for localConfig, moduleConfig and channels """ def __init__(self, iface, nodeNum, noProto=False): """Constructor""" self.iface = iface self.nodeNum = nodeNum 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.cannedPluginMessageMessages = None self.ringtone = None self.ringtonePart = None self.gotResponse = None def showChannels(self): """Show human readable description of our channels.""" print("Channels:") if self.channels: logging.debug(f"self.channels:{self.channels}") for c in self.channels: cStr = message_to_json(c.settings) # don't show disabled channels if channel_pb2.Channel.Role.Name(c.role) != "DISABLED": print( f" Index {c.index}: {channel_pb2.Channel.Role.Name(c.role)} psk={pskToString(c.settings.psk)} {cStr}" ) publicURL = self.getURL(includeAll=False) adminURL = self.getURL(includeAll=True) print(f"\nPrimary channel URL: {publicURL}") if adminURL != publicURL: print(f"Complete URL (includes all channels): {adminURL}") def showInfo(self): """Show human readable description of our node""" prefs = "" if self.localConfig: prefs = message_to_json(self.localConfig, multiline=True) print(f"Preferences: {prefs}\n") prefs = "" if self.moduleConfig: prefs = message_to_json(self.moduleConfig, multiline=True) print(f"Module preferences: {prefs}\n") self.showChannels() def setChannels(self, channels): """Set the channels for this node""" self.channels = channels self._fixupChannels() def requestChannels(self): """Send regular MeshPackets to ask channels.""" logging.debug(f"requestChannels for nodeNum:{self.nodeNum}") self.channels = None self.partialChannels = [] # We keep our channels in a temp array until finished self._requestChannel(0) def onResponseRequestSettings(self, p): """Handle the response packets for requesting settings _requestSettings()""" logging.debug(f"onResponseRequestSetting() p:{p}") if "routing" in p["decoded"]: if p["decoded"]["routing"]["errorReason"] != "NONE": print(f'Error on response: {p["decoded"]["routing"]["errorReason"]}') self.iface._acknowledgment.receivedNak = True else: self.iface._acknowledgment.receivedAck = True print("") adminMessage = p["decoded"]["admin"] if "getConfigResponse" in adminMessage: resp = adminMessage["getConfigResponse"] field = list(resp.keys())[0] config_type = self.localConfig.DESCRIPTOR.fields_by_name.get( camel_to_snake(field) ) config_values = getattr(self.localConfig, config_type.name) elif "getModuleConfigResponse" in adminMessage: resp = adminMessage["getModuleConfigResponse"] field = list(resp.keys())[0] config_type = self.moduleConfig.DESCRIPTOR.fields_by_name.get( camel_to_snake(field) ) config_values = getattr(self.moduleConfig, config_type.name) else: print( "Did not receive a valid response. Make sure to have a shared channel named 'admin'." ) return for key, value in resp[field].items(): setattr(config_values, camel_to_snake(key), value) print(f"{str(camel_to_snake(field))}:\n{str(config_values)}") def requestConfig(self, configType): """Request the config from the node via admin message""" if self == self.iface.localNode: onResponse = None else: onResponse = self.onResponseRequestSettings print("Requesting current config from remote node (this can take a while).") msgIndex = configType.index if configType.containing_type.name == "LocalConfig": p = admin_pb2.AdminMessage() p.get_config_request = msgIndex self._sendAdmin(p, wantResponse=True, onResponse=onResponse) else: p = admin_pb2.AdminMessage() p.get_module_config_request = msgIndex self._sendAdmin(p, wantResponse=True, onResponse=onResponse) if onResponse: self.iface.waitForAckNak() def turnOffEncryptionOnPrimaryChannel(self): """Turn off encryption on primary channel.""" self.channels[0].settings.psk = fromPSK("none") print("Writing modified channels to device") self.writeChannel(0) def waitForConfig(self, attribute="channels"): """Block until radio config is received. Returns True if config has been received.""" return self._timeout.waitForSet(self, attrs=("localConfig", attribute)) def writeConfig(self, config_name): """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() if config_name == "device": p.set_config.device.CopyFrom(self.localConfig.device) elif config_name == "position": p.set_config.position.CopyFrom(self.localConfig.position) elif config_name == "power": p.set_config.power.CopyFrom(self.localConfig.power) elif config_name == "network": p.set_config.network.CopyFrom(self.localConfig.network) elif config_name == "display": p.set_config.display.CopyFrom(self.localConfig.display) elif config_name == "lora": p.set_config.lora.CopyFrom(self.localConfig.lora) elif config_name == "bluetooth": p.set_config.bluetooth.CopyFrom(self.localConfig.bluetooth) elif config_name == "mqtt": p.set_module_config.mqtt.CopyFrom(self.moduleConfig.mqtt) elif config_name == "serial": p.set_module_config.serial.CopyFrom(self.moduleConfig.serial) elif config_name == "external_notification": p.set_module_config.external_notification.CopyFrom( self.moduleConfig.external_notification ) elif config_name == "store_forward": p.set_module_config.store_forward.CopyFrom(self.moduleConfig.store_forward) elif config_name == "range_test": p.set_module_config.range_test.CopyFrom(self.moduleConfig.range_test) elif config_name == "telemetry": p.set_module_config.telemetry.CopyFrom(self.moduleConfig.telemetry) elif config_name == "canned_message": p.set_module_config.canned_message.CopyFrom( self.moduleConfig.canned_message ) elif config_name == "audio": p.set_module_config.audio.CopyFrom(self.moduleConfig.audio) elif config_name == "remote_hardware": p.set_module_config.remote_hardware.CopyFrom( self.moduleConfig.remote_hardware ) elif config_name == "neighbor_info": p.set_module_config.neighbor_info.CopyFrom(self.moduleConfig.neighbor_info) elif config_name == "detection_sensor": p.set_module_config.detection_sensor.CopyFrom(self.moduleConfig.detection_sensor) elif config_name == "ambient_lighting": p.set_module_config.ambient_lighting.CopyFrom(self.moduleConfig.ambient_lighting) elif config_name == "paxcounter": p.set_module_config.paxcounter.CopyFrom(self.moduleConfig.paxcounter) else: our_exit(f"Error: No valid config with name {config_name}") logging.debug(f"Wrote: {config_name}") if self == self.iface.localNode: onResponse = None else: onResponse = self.onAckNak self._sendAdmin(p, onResponse=onResponse) def writeChannel(self, channelIndex, adminIndex=0): """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}") def getChannelByChannelIndex(self, channelIndex): """Get channel by channelIndex channelIndex: number, typically 0-7; based on max number channels returns: None if there is no channel found """ ch = None if self.channels and 0 <= channelIndex < len(self.channels): ch = self.channels[channelIndex] return ch def deleteChannel(self, channelIndex): """Delete the specified channelIndex and shift other channels up""" ch = self.channels[channelIndex] if ch.role not in ( channel_pb2.Channel.Role.SECONDARY, channel_pb2.Channel.Role.DISABLED, ): our_exit("Warning: Only SECONDARY channels can be deleted") # we are careful here because if we move the "admin" channel the channelIndex we need to use # for sending admin channels will also change adminIndex = self.iface.localNode._getAdminChannelIndex() self.channels.pop(channelIndex) self._fixupChannels() # expand back to 8 channels index = channelIndex while index < 8: self.writeChannel(index, adminIndex=adminIndex) index += 1 # if we are updating the local node, we might end up # *moving* the admin channel index as we are writing if (self.iface.localNode == self) and index >= adminIndex: # We've now passed the old location for admin index # (and written it), so we can start finding it by name again adminIndex = 0 def getChannelByName(self, name): """Try to find the named channel or return None""" for c in self.channels or []: if c.settings and c.settings.name == name: return c return None def getDisabledChannel(self): """Return the first channel that is disabled (i.e. available for some new use)""" for c in self.channels: if c.role == channel_pb2.Channel.Role.DISABLED: return c return None def _getAdminChannelIndex(self): """Return the channel number of the admin channel, or 0 if no reserved channel""" for c in self.channels or []: if c.settings and c.settings.name.lower() == "admin": return c.index return 0 def setOwner(self, long_name=None, short_name=None, is_licensed=False): """Set device owner name""" logging.debug(f"in setOwner nodeNum:{self.nodeNum}") p = admin_pb2.AdminMessage() nChars = 4 if long_name is not None: long_name = long_name.strip() p.set_owner.long_name = long_name p.set_owner.is_licensed = is_licensed if short_name is not None: short_name = short_name.strip() if len(short_name) > nChars: short_name = short_name[:nChars] print(f"Maximum is 4 characters, truncated to {short_name}") p.set_owner.short_name = short_name # 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}") # If sending to a remote node, wait for ACK/NAK if self == self.iface.localNode: onResponse = None else: onResponse = self.onAckNak return self._sendAdmin(p, onResponse=onResponse) def getURL(self, includeAll: bool = True): """The sharable URL that describes the current channel""" # Only keep the primary/secondary channels, assume primary is first channelSet = apponly_pb2.ChannelSet() if self.channels: for c in self.channels: if c.role == channel_pb2.Channel.Role.PRIMARY or ( includeAll and c.role == channel_pb2.Channel.Role.SECONDARY ): channelSet.settings.append(c.settings) if len(self.localConfig.ListFields()) == 0: self.requestConfig(self.localConfig.DESCRIPTOR.fields_by_name.get('lora')) channelSet.lora_config.CopyFrom(self.localConfig.lora) some_bytes = channelSet.SerializeToString() s = base64.urlsafe_b64encode(some_bytes).decode("ascii") s = s.replace("=", "").replace("+", "-").replace("/", "_") return f"https://meshtastic.org/e/#{s}" def setURL(self, url): """Set mesh network URL""" if self.localConfig is None: our_exit("Warning: No Config has been read") # URLs are of the form https://meshtastic.org/d/#{base64_channel_set} # Split on '/#' to find the base64 encoded channel settings splitURL = url.split("/#") b64 = splitURL[-1] # We normally strip padding to make for a shorter URL, but the python parser doesn't like # that. So add back any missing padding # per https://stackoverflow.com/a/9807138 missing_padding = len(b64) % 4 if missing_padding: b64 += "=" * (4 - missing_padding) decodedURL = base64.urlsafe_b64decode(b64) channelSet = apponly_pb2.ChannelSet() channelSet.ParseFromString(decodedURL) if len(channelSet.settings) == 0: our_exit("Warning: There were no settings.") i = 0 for chs in channelSet.settings: ch = channel_pb2.Channel() ch.role = ( channel_pb2.Channel.Role.PRIMARY if i == 0 else channel_pb2.Channel.Role.SECONDARY ) ch.index = i ch.settings.CopyFrom(chs) self.channels[ch.index] = ch logging.debug(f"Channel i:{i} ch:{ch}") self.writeChannel(ch.index) i = i + 1 p = admin_pb2.AdminMessage() p.set_config.lora.CopyFrom(channelSet.lora_config) self._sendAdmin(p) def onResponseRequestRingtone(self, p): """Handle the response packet for requesting ringtone part 1""" logging.debug(f"onResponseRequestRingtone() p:{p}") errorFound = False if "routing" in p["decoded"]: if p["decoded"]["routing"]["errorReason"] != "NONE": 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.ringtonePart = p["decoded"]["admin"][ "raw" ].get_ringtone_response logging.debug(f"self.ringtonePart:{self.ringtonePart}") self.gotResponse = True def get_ringtone(self): """Get the ringtone. Concatenate all pieces together and return a single string.""" logging.debug(f"in get_ringtone()") if not self.ringtone: p1 = admin_pb2.AdminMessage() p1.get_ringtone_request = True self.gotResponse = False self._sendAdmin( p1, wantResponse=True, onResponse=self.onResponseRequestRingtone ) while self.gotResponse is False: time.sleep(0.1) logging.debug(f"self.ringtone:{self.ringtone}") self.ringtone = "" if self.ringtonePart: self.ringtone += self.ringtonePart print(f"ringtone:{self.ringtone}") logging.debug(f"ringtone:{self.ringtone}") return self.ringtone def set_ringtone(self, ringtone): """Set the ringtone. The ringtone length must be less than 230 character.""" if len(ringtone) > 230: our_exit("Warning: The ringtone must be less than 230 characters.") # split into chunks chunks = [] chunks_size = 230 for i in range(0, len(ringtone), chunks_size): chunks.append(ringtone[i : i + chunks_size]) # for each chunk, send a message to set the values # for i in range(0, len(chunks)): for i, chunk in enumerate(chunks): p = admin_pb2.AdminMessage() # TODO: should be a way to improve this if i == 0: p.set_ringtone_message = chunk logging.debug(f"Setting ringtone '{chunk}' part {i+1}") # If sending to a remote node, wait for ACK/NAK if self == self.iface.localNode: onResponse = None else: onResponse = self.onAckNak return self._sendAdmin(p, onResponse=onResponse) def onResponseRequestCannedMessagePluginMessageMessages(self, p): """Handle the response packet for requesting canned message plugin message part 1""" logging.debug(f"onResponseRequestCannedMessagePluginMessageMessages() p:{p}") errorFound = False if "routing" in p["decoded"]: if p["decoded"]["routing"]["errorReason"] != "NONE": errorFound = True print(f'Error on response: {p["decoded"]["routing"]["errorReason"]}') if errorFound is False: if "decoded" in p: if "admin" in p["decoded"]: if "raw" in p["decoded"]["admin"]: self.cannedPluginMessageMessages = p["decoded"]["admin"][ "raw" ].get_canned_message_module_messages_response logging.debug( f"self.cannedPluginMessageMessages:{self.cannedPluginMessageMessages}" ) self.gotResponse = True def get_canned_message(self): """Get the canned message string. Concatenate all pieces together and return a single string.""" logging.debug(f"in get_canned_message()") if not self.cannedPluginMessage: p1 = admin_pb2.AdminMessage() p1.get_canned_message_module_messages_request = True self.gotResponse = False self._sendAdmin( p1, wantResponse=True, onResponse=self.onResponseRequestCannedMessagePluginMessageMessages, ) while self.gotResponse is False: time.sleep(0.1) logging.debug( f"self.cannedPluginMessageMessages:{self.cannedPluginMessageMessages}" ) self.cannedPluginMessage = "" if self.cannedPluginMessageMessages: self.cannedPluginMessage += self.cannedPluginMessageMessages print(f"canned_plugin_message:{self.cannedPluginMessage}") logging.debug(f"canned_plugin_message:{self.cannedPluginMessage}") return self.cannedPluginMessage def set_canned_message(self, message): """Set the canned message. The canned messages length must be less than 200 character.""" if len(message) > 200: our_exit("Warning: The canned message must be less than 200 characters.") # split into chunks chunks = [] chunks_size = 200 for i in range(0, len(message), chunks_size): chunks.append(message[i : i + chunks_size]) # for each chunk, send a message to set the values # for i in range(0, len(chunks)): for i, chunk in enumerate(chunks): p = admin_pb2.AdminMessage() # TODO: should be a way to improve this if i == 0: p.set_canned_message_module_messages = chunk logging.debug(f"Setting canned message '{chunk}' part {i+1}") # If sending to a remote node, wait for ACK/NAK if self == self.iface.localNode: onResponse = None else: onResponse = self.onAckNak return self._sendAdmin(p, onResponse=onResponse) def exitSimulator(self): """Tell a simulator node to exit (this message is ignored for other nodes)""" p = admin_pb2.AdminMessage() p.exit_simulator = True logging.debug("in exitSimulator()") return self._sendAdmin(p) def reboot(self, secs: int = 10): """Tell the node to reboot.""" p = admin_pb2.AdminMessage() p.reboot_seconds = secs logging.info(f"Telling node to reboot in {secs} seconds") # If sending to a remote node, wait for ACK/NAK if self == self.iface.localNode: onResponse = None else: onResponse = self.onAckNak return self._sendAdmin(p, onResponse=onResponse) def beginSettingsTransaction(self): """Tell the node to open a transaction to edit settings.""" p = admin_pb2.AdminMessage() p.begin_edit_settings = True logging.info(f"Telling open a transaction to edit settings") # If sending to a remote node, wait for ACK/NAK if self == self.iface.localNode: onResponse = None else: onResponse = self.onAckNak return self._sendAdmin(p, onResponse=onResponse) def commitSettingsTransaction(self): """Tell the node to commit the open transaction for editing settings.""" p = admin_pb2.AdminMessage() p.commit_edit_settings = True logging.info(f"Telling node to commit open transaction for editing settings") # If sending to a remote node, wait for ACK/NAK if self == self.iface.localNode: onResponse = None else: onResponse = self.onAckNak return self._sendAdmin(p, onResponse=onResponse) def rebootOTA(self, secs: int = 10): """Tell the node to reboot into factory firmware.""" p = admin_pb2.AdminMessage() p.reboot_ota_seconds = secs logging.info(f"Telling node to reboot to OTA in {secs} seconds") # If sending to a remote node, wait for ACK/NAK if self == self.iface.localNode: onResponse = None else: onResponse = self.onAckNak return self._sendAdmin(p, onResponse=onResponse) def enterDFUMode(self): """Tell the node to enter DFU mode (NRF52).""" p = admin_pb2.AdminMessage() p.enter_dfu_mode_request = True logging.info(f"Telling node to enable DFU mode") # If sending to a remote node, wait for ACK/NAK if self == self.iface.localNode: onResponse = None else: onResponse = self.onAckNak return self._sendAdmin(p, onResponse=onResponse) def shutdown(self, secs: int = 10): """Tell the node to shutdown.""" p = admin_pb2.AdminMessage() p.shutdown_seconds = secs logging.info(f"Telling node to shutdown in {secs} seconds") # If sending to a remote node, wait for ACK/NAK if self == self.iface.localNode: onResponse = None else: onResponse = self.onAckNak return self._sendAdmin(p, onResponse=onResponse) def getMetadata(self): """Get the node's metadata.""" p = admin_pb2.AdminMessage() p.get_device_metadata_request = True logging.info(f"Requesting device metadata") self._sendAdmin( p, wantResponse=True, onResponse=self.onRequestGetMetadata ) self.iface.waitForAckNak() def factoryReset(self): """Tell the node to factory reset.""" p = admin_pb2.AdminMessage() p.factory_reset = True logging.info(f"Telling node to factory reset") # If sending to a remote node, wait for ACK/NAK if self == self.iface.localNode: onResponse = None else: onResponse = self.onAckNak return self._sendAdmin(p, onResponse=onResponse) def removeNode(self, nodeId: Union[int, str]): """Tell the node to remove a specific node by ID""" if isinstance(nodeId, str): if nodeId.startswith("!"): nodeId = int(nodeId[1:], 16) else: nodeId = int(nodeId) p = admin_pb2.AdminMessage() p.remove_by_nodenum = nodeId if self == self.iface.localNode: onResponse = None else: onResponse = self.onAckNak return self._sendAdmin(p, onResponse=onResponse) def resetNodeDb(self): """Tell the node to reset its list of nodes.""" p = admin_pb2.AdminMessage() p.nodedb_reset = True logging.info(f"Telling node to reset the NodeDB") # If sending to a remote node, wait for ACK/NAK if self == self.iface.localNode: onResponse = None else: onResponse = self.onAckNak return self._sendAdmin(p, onResponse=onResponse) def setFixedPosition(self, lat: Union[int, float], lon: Union[int, float], alt: int): """Tell the node to set fixed position to the provided value and enable the fixed position setting""" if self != self.iface.localNode: logging.error("Setting position of remote nodes is not supported.") return None p = mesh_pb2.Position() if isinstance(lat, float) and lat != 0.0: p.latitude_i = int(lat / 1e-7) elif isinstance(lat, int) and lat != 0: p.latitude_i = lat if isinstance(lon, float) and lon != 0.0: p.longitude_i = int(lon / 1e-7) elif isinstance(lon, int) and lon != 0: p.longitude_i = lon if alt != 0: p.altitude = alt a = admin_pb2.AdminMessage() a.set_fixed_position.CopyFrom(p) return self._sendAdmin(a) def removeFixedPosition(self): """Tell the node to remove the fixed position and set the fixed position setting to false""" p = admin_pb2.AdminMessage() p.remove_fixed_position = True logging.info(f"Telling node to remove fixed position") return self._sendAdmin(p) def _fixupChannels(self): """Fixup indexes and add disabled channels as needed""" # Add extra disabled channels as needed # This is needed because the protobufs will have index **missing** if the channel number is zero for index, ch in enumerate(self.channels): ch.index = index # fixup indexes self._fillChannels() def _fillChannels(self): """Mark unused channels as disabled""" # Add extra disabled channels as needed index = len(self.channels) while index < 8: ch = channel_pb2.Channel() ch.role = channel_pb2.Channel.Role.DISABLED ch.index = index self.channels.append(ch) index += 1 def onRequestGetMetadata(self, p): """Handle the response packet for requesting device metadata getMetadata()""" logging.debug(f"onRequestGetMetadata() p:{p}") if "routing" in p["decoded"]: if p["decoded"]["routing"]["errorReason"] != "NONE": print(f'Error on response: {p["decoded"]["routing"]["errorReason"]}') self.iface._acknowledgment.receivedNak = True else: self.iface._acknowledgment.receivedAck = True if p["decoded"]["portnum"] == portnums_pb2.PortNum.Name( portnums_pb2.PortNum.ROUTING_APP ): if p["decoded"]["routing"]["errorReason"] != "NONE": logging.warning( f'Metadata request failed, error reason: {p["decoded"]["routing"]["errorReason"]}' ) self._timeout.expireTime = time.time() # Do not wait any longer return # Don't try to parse this routing message logging.debug(f"Retrying metadata request.") self.getMetadata() return c = p["decoded"]["admin"]["raw"].get_device_metadata_response self._timeout.reset() # We made forward progress logging.debug(f"Received metadata {stripnl(c)}") print(f"\nfirmware_version: {c.firmware_version}") print(f"device_state_version: {c.device_state_version}") def onResponseRequestChannel(self, p): """Handle the response packet for requesting a channel _requestChannel()""" logging.debug(f"onResponseRequestChannel() p:{p}") if p["decoded"]["portnum"] == portnums_pb2.PortNum.Name( portnums_pb2.PortNum.ROUTING_APP ): if p["decoded"]["routing"]["errorReason"] != "NONE": logging.warning( f'Channel request failed, error reason: {p["decoded"]["routing"]["errorReason"]}' ) self._timeout.expireTime = time.time() # Do not wait any longer return # Don't try to parse this routing message lastTried = 0 if len(self.partialChannels) > 0: lastTried = self.partialChannels[-1].index logging.debug(f"Retrying previous channel request.") self._requestChannel(lastTried) return c = p["decoded"]["admin"]["raw"].get_channel_response self.partialChannels.append(c) self._timeout.reset() # We made forward progress logging.debug(f"Received channel {stripnl(c)}") index = c.index if index >= 8 - 1: logging.debug("Finished downloading channels") self.channels = self.partialChannels self._fixupChannels() else: self._requestChannel(index + 1) def onAckNak(self, p): """Informative handler for ACK/NAK responses""" if p["decoded"]["routing"]["errorReason"] != "NONE": print( f'Received a NAK, error reason: {p["decoded"]["routing"]["errorReason"]}' ) self.iface._acknowledgment.receivedNak = True else: if int(p["from"]) == self.iface.localNode.nodeNum: print( f"Received an implicit ACK. Packet will likely arrive, but cannot be guaranteed." ) self.iface._acknowledgment.receivedImplAck = True else: print(f"Received an ACK.") self.iface._acknowledgment.receivedAck = True def _requestChannel(self, channelNum: int): """Done with initial config messages, now send regular MeshPackets to ask for settings""" p = admin_pb2.AdminMessage() p.get_channel_request = channelNum + 1 # Show progress message for super slow operations if self != self.iface.localNode: print( f"Requesting channel {channelNum} info from remote node (this could take a while)" ) logging.debug( f"Requesting channel {channelNum} info from remote node (this could take a while)" ) else: logging.debug(f"Requesting channel {channelNum}") return self._sendAdmin( p, wantResponse=True, onResponse=self.onResponseRequestChannel ) # pylint: disable=R1710 def _sendAdmin( self, p: admin_pb2.AdminMessage, wantResponse: bool=True, onResponse=None, adminIndex: int=0, ): """Send an admin message to the specified node (or the local node if destNodeNum is zero)""" if self.noProto: logging.warning( f"Not sending packet because protocol use is disabled by noProto" ) else: if ( adminIndex == 0 ): # unless a special channel index was used, we want to use the admin index adminIndex = self.iface.localNode._getAdminChannelIndex() logging.debug(f"adminIndex:{adminIndex}") return self.iface.sendData( p, self.nodeNum, portNum=portnums_pb2.PortNum.ADMIN_APP, wantAck=False, wantResponse=wantResponse, onResponse=onResponse, channelIndex=adminIndex, ) python-2.3.14/meshtastic/protobuf/000077500000000000000000000000001464266072200171375ustar00rootroot00000000000000python-2.3.14/meshtastic/protobuf/__init__.py000066400000000000000000000000001464266072200212360ustar00rootroot00000000000000python-2.3.14/meshtastic/protobuf/admin_pb2.py000066400000000000000000000146461464266072200213570ustar00rootroot00000000000000# -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # source: meshtastic/protobuf/admin.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 symbol_database as _symbol_database from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() from meshtastic.protobuf import channel_pb2 as meshtastic_dot_protobuf_dot_channel__pb2 from meshtastic.protobuf import config_pb2 as meshtastic_dot_protobuf_dot_config__pb2 from meshtastic.protobuf import connection_status_pb2 as meshtastic_dot_protobuf_dot_connection__status__pb2 from meshtastic.protobuf import mesh_pb2 as meshtastic_dot_protobuf_dot_mesh__pb2 from meshtastic.protobuf import module_config_pb2 as meshtastic_dot_protobuf_dot_module__config__pb2 DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1fmeshtastic/protobuf/admin.proto\x12\x13meshtastic.protobuf\x1a!meshtastic/protobuf/channel.proto\x1a meshtastic/protobuf/config.proto\x1a+meshtastic/protobuf/connection_status.proto\x1a\x1emeshtastic/protobuf/mesh.proto\x1a\'meshtastic/protobuf/module_config.proto\"\xea\x12\n\x0c\x41\x64minMessage\x12\x1d\n\x13get_channel_request\x18\x01 \x01(\rH\x00\x12<\n\x14get_channel_response\x18\x02 \x01(\x0b\x32\x1c.meshtastic.protobuf.ChannelH\x00\x12\x1b\n\x11get_owner_request\x18\x03 \x01(\x08H\x00\x12\x37\n\x12get_owner_response\x18\x04 \x01(\x0b\x32\x19.meshtastic.protobuf.UserH\x00\x12J\n\x12get_config_request\x18\x05 \x01(\x0e\x32,.meshtastic.protobuf.AdminMessage.ConfigTypeH\x00\x12:\n\x13get_config_response\x18\x06 \x01(\x0b\x32\x1b.meshtastic.protobuf.ConfigH\x00\x12W\n\x19get_module_config_request\x18\x07 \x01(\x0e\x32\x32.meshtastic.protobuf.AdminMessage.ModuleConfigTypeH\x00\x12G\n\x1aget_module_config_response\x18\x08 \x01(\x0b\x32!.meshtastic.protobuf.ModuleConfigH\x00\x12\x34\n*get_canned_message_module_messages_request\x18\n \x01(\x08H\x00\x12\x35\n+get_canned_message_module_messages_response\x18\x0b \x01(\tH\x00\x12%\n\x1bget_device_metadata_request\x18\x0c \x01(\x08H\x00\x12K\n\x1cget_device_metadata_response\x18\r \x01(\x0b\x32#.meshtastic.protobuf.DeviceMetadataH\x00\x12\x1e\n\x14get_ringtone_request\x18\x0e \x01(\x08H\x00\x12\x1f\n\x15get_ringtone_response\x18\x0f \x01(\tH\x00\x12.\n$get_device_connection_status_request\x18\x10 \x01(\x08H\x00\x12\\\n%get_device_connection_status_response\x18\x11 \x01(\x0b\x32+.meshtastic.protobuf.DeviceConnectionStatusH\x00\x12:\n\x0cset_ham_mode\x18\x12 \x01(\x0b\x32\".meshtastic.protobuf.HamParametersH\x00\x12/\n%get_node_remote_hardware_pins_request\x18\x13 \x01(\x08H\x00\x12\x65\n&get_node_remote_hardware_pins_response\x18\x14 \x01(\x0b\x32\x33.meshtastic.protobuf.NodeRemoteHardwarePinsResponseH\x00\x12 \n\x16\x65nter_dfu_mode_request\x18\x15 \x01(\x08H\x00\x12\x1d\n\x13\x64\x65lete_file_request\x18\x16 \x01(\tH\x00\x12\x13\n\tset_scale\x18\x17 \x01(\rH\x00\x12.\n\tset_owner\x18 \x01(\x0b\x32\x19.meshtastic.protobuf.UserH\x00\x12\x33\n\x0bset_channel\x18! \x01(\x0b\x32\x1c.meshtastic.protobuf.ChannelH\x00\x12\x31\n\nset_config\x18\" \x01(\x0b\x32\x1b.meshtastic.protobuf.ConfigH\x00\x12>\n\x11set_module_config\x18# \x01(\x0b\x32!.meshtastic.protobuf.ModuleConfigH\x00\x12,\n\"set_canned_message_module_messages\x18$ \x01(\tH\x00\x12\x1e\n\x14set_ringtone_message\x18% \x01(\tH\x00\x12\x1b\n\x11remove_by_nodenum\x18& \x01(\rH\x00\x12\x1b\n\x11set_favorite_node\x18\' \x01(\rH\x00\x12\x1e\n\x14remove_favorite_node\x18( \x01(\rH\x00\x12;\n\x12set_fixed_position\x18) \x01(\x0b\x32\x1d.meshtastic.protobuf.PositionH\x00\x12\x1f\n\x15remove_fixed_position\x18* \x01(\x08H\x00\x12\x1d\n\x13\x62\x65gin_edit_settings\x18@ \x01(\x08H\x00\x12\x1e\n\x14\x63ommit_edit_settings\x18\x41 \x01(\x08H\x00\x12\x1c\n\x12reboot_ota_seconds\x18_ \x01(\x05H\x00\x12\x18\n\x0e\x65xit_simulator\x18` \x01(\x08H\x00\x12\x18\n\x0ereboot_seconds\x18\x61 \x01(\x05H\x00\x12\x1a\n\x10shutdown_seconds\x18\x62 \x01(\x05H\x00\x12\x17\n\rfactory_reset\x18\x63 \x01(\x05H\x00\x12\x16\n\x0cnodedb_reset\x18\x64 \x01(\x05H\x00\"\x95\x01\n\nConfigType\x12\x11\n\rDEVICE_CONFIG\x10\x00\x12\x13\n\x0fPOSITION_CONFIG\x10\x01\x12\x10\n\x0cPOWER_CONFIG\x10\x02\x12\x12\n\x0eNETWORK_CONFIG\x10\x03\x12\x12\n\x0e\x44ISPLAY_CONFIG\x10\x04\x12\x0f\n\x0bLORA_CONFIG\x10\x05\x12\x14\n\x10\x42LUETOOTH_CONFIG\x10\x06\"\xbb\x02\n\x10ModuleConfigType\x12\x0f\n\x0bMQTT_CONFIG\x10\x00\x12\x11\n\rSERIAL_CONFIG\x10\x01\x12\x13\n\x0f\x45XTNOTIF_CONFIG\x10\x02\x12\x17\n\x13STOREFORWARD_CONFIG\x10\x03\x12\x14\n\x10RANGETEST_CONFIG\x10\x04\x12\x14\n\x10TELEMETRY_CONFIG\x10\x05\x12\x14\n\x10\x43\x41NNEDMSG_CONFIG\x10\x06\x12\x10\n\x0c\x41UDIO_CONFIG\x10\x07\x12\x19\n\x15REMOTEHARDWARE_CONFIG\x10\x08\x12\x17\n\x13NEIGHBORINFO_CONFIG\x10\t\x12\x1a\n\x16\x41MBIENTLIGHTING_CONFIG\x10\n\x12\x1a\n\x16\x44\x45TECTIONSENSOR_CONFIG\x10\x0b\x12\x15\n\x11PAXCOUNTER_CONFIG\x10\x0c\x42\x11\n\x0fpayload_variant\"[\n\rHamParameters\x12\x11\n\tcall_sign\x18\x01 \x01(\t\x12\x10\n\x08tx_power\x18\x02 \x01(\x05\x12\x11\n\tfrequency\x18\x03 \x01(\x02\x12\x12\n\nshort_name\x18\x04 \x01(\t\"o\n\x1eNodeRemoteHardwarePinsResponse\x12M\n\x19node_remote_hardware_pins\x18\x01 \x03(\x0b\x32*.meshtastic.protobuf.NodeRemoteHardwarePinB`\n\x13\x63om.geeksville.meshB\x0b\x41\x64minProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.admin_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\013AdminProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' _globals['_ADMINMESSAGE']._serialized_start=244 _globals['_ADMINMESSAGE']._serialized_end=2654 _globals['_ADMINMESSAGE_CONFIGTYPE']._serialized_start=2168 _globals['_ADMINMESSAGE_CONFIGTYPE']._serialized_end=2317 _globals['_ADMINMESSAGE_MODULECONFIGTYPE']._serialized_start=2320 _globals['_ADMINMESSAGE_MODULECONFIGTYPE']._serialized_end=2635 _globals['_HAMPARAMETERS']._serialized_start=2656 _globals['_HAMPARAMETERS']._serialized_end=2747 _globals['_NODEREMOTEHARDWAREPINSRESPONSE']._serialized_start=2749 _globals['_NODEREMOTEHARDWAREPINSRESPONSE']._serialized_end=2860 # @@protoc_insertion_point(module_scope) python-2.3.14/meshtastic/protobuf/admin_pb2.pyi000066400000000000000000000620441464266072200215230ustar00rootroot00000000000000""" @generated by mypy-protobuf. Do not edit manually! isort:skip_file """ import builtins import collections.abc import google.protobuf.descriptor import google.protobuf.internal.containers import google.protobuf.internal.enum_type_wrapper import google.protobuf.message import meshtastic.protobuf.channel_pb2 import meshtastic.protobuf.config_pb2 import meshtastic.protobuf.connection_status_pb2 import meshtastic.protobuf.mesh_pb2 import meshtastic.protobuf.module_config_pb2 import sys import typing if sys.version_info >= (3, 10): import typing as typing_extensions else: import typing_extensions DESCRIPTOR: google.protobuf.descriptor.FileDescriptor @typing.final class AdminMessage(google.protobuf.message.Message): """ This message is handled by the Admin module and is responsible for all settings/channel read/write operations. This message is used to do settings operations to both remote AND local nodes. (Prior to 1.2 these operations were done via special ToRadio operations) """ DESCRIPTOR: google.protobuf.descriptor.Descriptor class _ConfigType: ValueType = typing.NewType("ValueType", builtins.int) V: typing_extensions.TypeAlias = ValueType class _ConfigTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[AdminMessage._ConfigType.ValueType], builtins.type): DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor DEVICE_CONFIG: AdminMessage._ConfigType.ValueType # 0 """ TODO: REPLACE """ POSITION_CONFIG: AdminMessage._ConfigType.ValueType # 1 """ TODO: REPLACE """ POWER_CONFIG: AdminMessage._ConfigType.ValueType # 2 """ TODO: REPLACE """ NETWORK_CONFIG: AdminMessage._ConfigType.ValueType # 3 """ TODO: REPLACE """ DISPLAY_CONFIG: AdminMessage._ConfigType.ValueType # 4 """ TODO: REPLACE """ LORA_CONFIG: AdminMessage._ConfigType.ValueType # 5 """ TODO: REPLACE """ BLUETOOTH_CONFIG: AdminMessage._ConfigType.ValueType # 6 """ TODO: REPLACE """ class ConfigType(_ConfigType, metaclass=_ConfigTypeEnumTypeWrapper): """ TODO: REPLACE """ DEVICE_CONFIG: AdminMessage.ConfigType.ValueType # 0 """ TODO: REPLACE """ POSITION_CONFIG: AdminMessage.ConfigType.ValueType # 1 """ TODO: REPLACE """ POWER_CONFIG: AdminMessage.ConfigType.ValueType # 2 """ TODO: REPLACE """ NETWORK_CONFIG: AdminMessage.ConfigType.ValueType # 3 """ TODO: REPLACE """ DISPLAY_CONFIG: AdminMessage.ConfigType.ValueType # 4 """ TODO: REPLACE """ LORA_CONFIG: AdminMessage.ConfigType.ValueType # 5 """ TODO: REPLACE """ BLUETOOTH_CONFIG: AdminMessage.ConfigType.ValueType # 6 """ TODO: REPLACE """ class _ModuleConfigType: ValueType = typing.NewType("ValueType", builtins.int) V: typing_extensions.TypeAlias = ValueType class _ModuleConfigTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[AdminMessage._ModuleConfigType.ValueType], builtins.type): DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor MQTT_CONFIG: AdminMessage._ModuleConfigType.ValueType # 0 """ TODO: REPLACE """ SERIAL_CONFIG: AdminMessage._ModuleConfigType.ValueType # 1 """ TODO: REPLACE """ EXTNOTIF_CONFIG: AdminMessage._ModuleConfigType.ValueType # 2 """ TODO: REPLACE """ STOREFORWARD_CONFIG: AdminMessage._ModuleConfigType.ValueType # 3 """ TODO: REPLACE """ RANGETEST_CONFIG: AdminMessage._ModuleConfigType.ValueType # 4 """ TODO: REPLACE """ TELEMETRY_CONFIG: AdminMessage._ModuleConfigType.ValueType # 5 """ TODO: REPLACE """ CANNEDMSG_CONFIG: AdminMessage._ModuleConfigType.ValueType # 6 """ TODO: REPLACE """ AUDIO_CONFIG: AdminMessage._ModuleConfigType.ValueType # 7 """ TODO: REPLACE """ REMOTEHARDWARE_CONFIG: AdminMessage._ModuleConfigType.ValueType # 8 """ TODO: REPLACE """ NEIGHBORINFO_CONFIG: AdminMessage._ModuleConfigType.ValueType # 9 """ TODO: REPLACE """ AMBIENTLIGHTING_CONFIG: AdminMessage._ModuleConfigType.ValueType # 10 """ TODO: REPLACE """ DETECTIONSENSOR_CONFIG: AdminMessage._ModuleConfigType.ValueType # 11 """ TODO: REPLACE """ PAXCOUNTER_CONFIG: AdminMessage._ModuleConfigType.ValueType # 12 """ TODO: REPLACE """ class ModuleConfigType(_ModuleConfigType, metaclass=_ModuleConfigTypeEnumTypeWrapper): """ TODO: REPLACE """ MQTT_CONFIG: AdminMessage.ModuleConfigType.ValueType # 0 """ TODO: REPLACE """ SERIAL_CONFIG: AdminMessage.ModuleConfigType.ValueType # 1 """ TODO: REPLACE """ EXTNOTIF_CONFIG: AdminMessage.ModuleConfigType.ValueType # 2 """ TODO: REPLACE """ STOREFORWARD_CONFIG: AdminMessage.ModuleConfigType.ValueType # 3 """ TODO: REPLACE """ RANGETEST_CONFIG: AdminMessage.ModuleConfigType.ValueType # 4 """ TODO: REPLACE """ TELEMETRY_CONFIG: AdminMessage.ModuleConfigType.ValueType # 5 """ TODO: REPLACE """ CANNEDMSG_CONFIG: AdminMessage.ModuleConfigType.ValueType # 6 """ TODO: REPLACE """ AUDIO_CONFIG: AdminMessage.ModuleConfigType.ValueType # 7 """ TODO: REPLACE """ REMOTEHARDWARE_CONFIG: AdminMessage.ModuleConfigType.ValueType # 8 """ TODO: REPLACE """ NEIGHBORINFO_CONFIG: AdminMessage.ModuleConfigType.ValueType # 9 """ TODO: REPLACE """ AMBIENTLIGHTING_CONFIG: AdminMessage.ModuleConfigType.ValueType # 10 """ TODO: REPLACE """ DETECTIONSENSOR_CONFIG: AdminMessage.ModuleConfigType.ValueType # 11 """ TODO: REPLACE """ PAXCOUNTER_CONFIG: AdminMessage.ModuleConfigType.ValueType # 12 """ TODO: REPLACE """ GET_CHANNEL_REQUEST_FIELD_NUMBER: builtins.int GET_CHANNEL_RESPONSE_FIELD_NUMBER: builtins.int GET_OWNER_REQUEST_FIELD_NUMBER: builtins.int GET_OWNER_RESPONSE_FIELD_NUMBER: builtins.int GET_CONFIG_REQUEST_FIELD_NUMBER: builtins.int GET_CONFIG_RESPONSE_FIELD_NUMBER: builtins.int GET_MODULE_CONFIG_REQUEST_FIELD_NUMBER: builtins.int GET_MODULE_CONFIG_RESPONSE_FIELD_NUMBER: builtins.int GET_CANNED_MESSAGE_MODULE_MESSAGES_REQUEST_FIELD_NUMBER: builtins.int GET_CANNED_MESSAGE_MODULE_MESSAGES_RESPONSE_FIELD_NUMBER: builtins.int GET_DEVICE_METADATA_REQUEST_FIELD_NUMBER: builtins.int GET_DEVICE_METADATA_RESPONSE_FIELD_NUMBER: builtins.int GET_RINGTONE_REQUEST_FIELD_NUMBER: builtins.int GET_RINGTONE_RESPONSE_FIELD_NUMBER: builtins.int GET_DEVICE_CONNECTION_STATUS_REQUEST_FIELD_NUMBER: builtins.int GET_DEVICE_CONNECTION_STATUS_RESPONSE_FIELD_NUMBER: builtins.int SET_HAM_MODE_FIELD_NUMBER: builtins.int GET_NODE_REMOTE_HARDWARE_PINS_REQUEST_FIELD_NUMBER: builtins.int GET_NODE_REMOTE_HARDWARE_PINS_RESPONSE_FIELD_NUMBER: builtins.int ENTER_DFU_MODE_REQUEST_FIELD_NUMBER: builtins.int DELETE_FILE_REQUEST_FIELD_NUMBER: builtins.int SET_SCALE_FIELD_NUMBER: builtins.int SET_OWNER_FIELD_NUMBER: builtins.int SET_CHANNEL_FIELD_NUMBER: builtins.int SET_CONFIG_FIELD_NUMBER: builtins.int SET_MODULE_CONFIG_FIELD_NUMBER: builtins.int SET_CANNED_MESSAGE_MODULE_MESSAGES_FIELD_NUMBER: builtins.int SET_RINGTONE_MESSAGE_FIELD_NUMBER: builtins.int REMOVE_BY_NODENUM_FIELD_NUMBER: builtins.int SET_FAVORITE_NODE_FIELD_NUMBER: builtins.int REMOVE_FAVORITE_NODE_FIELD_NUMBER: builtins.int SET_FIXED_POSITION_FIELD_NUMBER: builtins.int REMOVE_FIXED_POSITION_FIELD_NUMBER: builtins.int BEGIN_EDIT_SETTINGS_FIELD_NUMBER: builtins.int COMMIT_EDIT_SETTINGS_FIELD_NUMBER: builtins.int REBOOT_OTA_SECONDS_FIELD_NUMBER: builtins.int EXIT_SIMULATOR_FIELD_NUMBER: builtins.int REBOOT_SECONDS_FIELD_NUMBER: builtins.int SHUTDOWN_SECONDS_FIELD_NUMBER: builtins.int FACTORY_RESET_FIELD_NUMBER: builtins.int NODEDB_RESET_FIELD_NUMBER: builtins.int get_channel_request: builtins.int """ Send the specified channel in the response to this message NOTE: This field is sent with the channel index + 1 (to ensure we never try to send 'zero' - which protobufs treats as not present) """ get_owner_request: builtins.bool """ Send the current owner data in the response to this message. """ get_config_request: global___AdminMessage.ConfigType.ValueType """ Ask for the following config data to be sent """ get_module_config_request: global___AdminMessage.ModuleConfigType.ValueType """ Ask for the following config data to be sent """ get_canned_message_module_messages_request: builtins.bool """ Get the Canned Message Module messages in the response to this message. """ get_canned_message_module_messages_response: builtins.str """ Get the Canned Message Module messages in the response to this message. """ get_device_metadata_request: builtins.bool """ Request the node to send device metadata (firmware, protobuf version, etc) """ get_ringtone_request: builtins.bool """ Get the Ringtone in the response to this message. """ get_ringtone_response: builtins.str """ Get the Ringtone in the response to this message. """ get_device_connection_status_request: builtins.bool """ Request the node to send it's connection status """ get_node_remote_hardware_pins_request: builtins.bool """ Get the mesh's nodes with their available gpio pins for RemoteHardware module use """ enter_dfu_mode_request: builtins.bool """ Enter (UF2) DFU mode Only implemented on NRF52 currently """ delete_file_request: builtins.str """ Delete the file by the specified path from the device """ set_scale: builtins.int """ Set zero and offset for scale chips """ set_canned_message_module_messages: builtins.str """ Set the Canned Message Module messages text. """ set_ringtone_message: builtins.str """ Set the ringtone for ExternalNotification. """ remove_by_nodenum: builtins.int """ Remove the node by the specified node-num from the NodeDB on the device """ set_favorite_node: builtins.int """ Set specified node-num to be favorited on the NodeDB on the device """ remove_favorite_node: builtins.int """ Set specified node-num to be un-favorited on the NodeDB on the device """ remove_fixed_position: builtins.bool """ Clear fixed position coordinates and then set position.fixed_position = false """ begin_edit_settings: builtins.bool """ Begins an edit transaction for config, module config, owner, and channel settings changes This will delay the standard *implicit* save to the file system and subsequent reboot behavior until committed (commit_edit_settings) """ commit_edit_settings: builtins.bool """ Commits an open transaction for any edits made to config, module config, owner, and channel settings """ reboot_ota_seconds: builtins.int """ Tell the node to reboot into the OTA Firmware in this many seconds (or <0 to cancel reboot) Only Implemented for ESP32 Devices. This needs to be issued to send a new main firmware via bluetooth. """ exit_simulator: builtins.bool """ This message is only supported for the simulator Portduino build. If received the simulator will exit successfully. """ reboot_seconds: builtins.int """ Tell the node to reboot in this many seconds (or <0 to cancel reboot) """ shutdown_seconds: builtins.int """ Tell the node to shutdown in this many seconds (or <0 to cancel shutdown) """ factory_reset: builtins.int """ Tell the node to factory reset, all device settings will be returned to factory defaults. """ nodedb_reset: builtins.int """ Tell the node to reset the nodedb. """ @property def get_channel_response(self) -> meshtastic.protobuf.channel_pb2.Channel: """ TODO: REPLACE """ @property def get_owner_response(self) -> meshtastic.protobuf.mesh_pb2.User: """ TODO: REPLACE """ @property def get_config_response(self) -> meshtastic.protobuf.config_pb2.Config: """ Send the current Config in the response to this message. """ @property def get_module_config_response(self) -> meshtastic.protobuf.module_config_pb2.ModuleConfig: """ Send the current Config in the response to this message. """ @property def get_device_metadata_response(self) -> meshtastic.protobuf.mesh_pb2.DeviceMetadata: """ Device metadata response """ @property def get_device_connection_status_response(self) -> meshtastic.protobuf.connection_status_pb2.DeviceConnectionStatus: """ Device connection status response """ @property def set_ham_mode(self) -> global___HamParameters: """ Setup a node for licensed amateur (ham) radio operation """ @property def get_node_remote_hardware_pins_response(self) -> global___NodeRemoteHardwarePinsResponse: """ Respond with the mesh's nodes with their available gpio pins for RemoteHardware module use """ @property def set_owner(self) -> meshtastic.protobuf.mesh_pb2.User: """ Set the owner for this node """ @property def set_channel(self) -> meshtastic.protobuf.channel_pb2.Channel: """ Set channels (using the new API). A special channel is the "primary channel". The other records are secondary channels. Note: only one channel can be marked as primary. If the client sets a particular channel to be primary, the previous channel will be set to SECONDARY automatically. """ @property def set_config(self) -> meshtastic.protobuf.config_pb2.Config: """ Set the current Config """ @property def set_module_config(self) -> meshtastic.protobuf.module_config_pb2.ModuleConfig: """ Set the current Config """ @property def set_fixed_position(self) -> meshtastic.protobuf.mesh_pb2.Position: """ Set fixed position data on the node and then set the position.fixed_position = true """ def __init__( self, *, get_channel_request: builtins.int = ..., get_channel_response: meshtastic.protobuf.channel_pb2.Channel | None = ..., get_owner_request: builtins.bool = ..., get_owner_response: meshtastic.protobuf.mesh_pb2.User | None = ..., get_config_request: global___AdminMessage.ConfigType.ValueType = ..., get_config_response: meshtastic.protobuf.config_pb2.Config | None = ..., get_module_config_request: global___AdminMessage.ModuleConfigType.ValueType = ..., get_module_config_response: meshtastic.protobuf.module_config_pb2.ModuleConfig | None = ..., get_canned_message_module_messages_request: builtins.bool = ..., get_canned_message_module_messages_response: builtins.str = ..., get_device_metadata_request: builtins.bool = ..., get_device_metadata_response: meshtastic.protobuf.mesh_pb2.DeviceMetadata | None = ..., get_ringtone_request: builtins.bool = ..., get_ringtone_response: builtins.str = ..., get_device_connection_status_request: builtins.bool = ..., get_device_connection_status_response: meshtastic.protobuf.connection_status_pb2.DeviceConnectionStatus | None = ..., set_ham_mode: global___HamParameters | None = ..., get_node_remote_hardware_pins_request: builtins.bool = ..., get_node_remote_hardware_pins_response: global___NodeRemoteHardwarePinsResponse | None = ..., enter_dfu_mode_request: builtins.bool = ..., delete_file_request: builtins.str = ..., set_scale: builtins.int = ..., set_owner: meshtastic.protobuf.mesh_pb2.User | None = ..., set_channel: meshtastic.protobuf.channel_pb2.Channel | None = ..., set_config: meshtastic.protobuf.config_pb2.Config | None = ..., set_module_config: meshtastic.protobuf.module_config_pb2.ModuleConfig | None = ..., set_canned_message_module_messages: builtins.str = ..., set_ringtone_message: builtins.str = ..., remove_by_nodenum: builtins.int = ..., set_favorite_node: builtins.int = ..., remove_favorite_node: builtins.int = ..., set_fixed_position: meshtastic.protobuf.mesh_pb2.Position | None = ..., remove_fixed_position: builtins.bool = ..., begin_edit_settings: builtins.bool = ..., commit_edit_settings: builtins.bool = ..., reboot_ota_seconds: builtins.int = ..., exit_simulator: builtins.bool = ..., reboot_seconds: builtins.int = ..., shutdown_seconds: builtins.int = ..., factory_reset: builtins.int = ..., nodedb_reset: builtins.int = ..., ) -> None: ... def HasField(self, field_name: typing.Literal["begin_edit_settings", b"begin_edit_settings", "commit_edit_settings", b"commit_edit_settings", "delete_file_request", b"delete_file_request", "enter_dfu_mode_request", b"enter_dfu_mode_request", "exit_simulator", b"exit_simulator", "factory_reset", b"factory_reset", "get_canned_message_module_messages_request", b"get_canned_message_module_messages_request", "get_canned_message_module_messages_response", b"get_canned_message_module_messages_response", "get_channel_request", b"get_channel_request", "get_channel_response", b"get_channel_response", "get_config_request", b"get_config_request", "get_config_response", b"get_config_response", "get_device_connection_status_request", b"get_device_connection_status_request", "get_device_connection_status_response", b"get_device_connection_status_response", "get_device_metadata_request", b"get_device_metadata_request", "get_device_metadata_response", b"get_device_metadata_response", "get_module_config_request", b"get_module_config_request", "get_module_config_response", b"get_module_config_response", "get_node_remote_hardware_pins_request", b"get_node_remote_hardware_pins_request", "get_node_remote_hardware_pins_response", b"get_node_remote_hardware_pins_response", "get_owner_request", b"get_owner_request", "get_owner_response", b"get_owner_response", "get_ringtone_request", b"get_ringtone_request", "get_ringtone_response", b"get_ringtone_response", "nodedb_reset", b"nodedb_reset", "payload_variant", b"payload_variant", "reboot_ota_seconds", b"reboot_ota_seconds", "reboot_seconds", b"reboot_seconds", "remove_by_nodenum", b"remove_by_nodenum", "remove_favorite_node", b"remove_favorite_node", "remove_fixed_position", b"remove_fixed_position", "set_canned_message_module_messages", b"set_canned_message_module_messages", "set_channel", b"set_channel", "set_config", b"set_config", "set_favorite_node", b"set_favorite_node", "set_fixed_position", b"set_fixed_position", "set_ham_mode", b"set_ham_mode", "set_module_config", b"set_module_config", "set_owner", b"set_owner", "set_ringtone_message", b"set_ringtone_message", "set_scale", b"set_scale", "shutdown_seconds", b"shutdown_seconds"]) -> builtins.bool: ... def ClearField(self, field_name: typing.Literal["begin_edit_settings", b"begin_edit_settings", "commit_edit_settings", b"commit_edit_settings", "delete_file_request", b"delete_file_request", "enter_dfu_mode_request", b"enter_dfu_mode_request", "exit_simulator", b"exit_simulator", "factory_reset", b"factory_reset", "get_canned_message_module_messages_request", b"get_canned_message_module_messages_request", "get_canned_message_module_messages_response", b"get_canned_message_module_messages_response", "get_channel_request", b"get_channel_request", "get_channel_response", b"get_channel_response", "get_config_request", b"get_config_request", "get_config_response", b"get_config_response", "get_device_connection_status_request", b"get_device_connection_status_request", "get_device_connection_status_response", b"get_device_connection_status_response", "get_device_metadata_request", b"get_device_metadata_request", "get_device_metadata_response", b"get_device_metadata_response", "get_module_config_request", b"get_module_config_request", "get_module_config_response", b"get_module_config_response", "get_node_remote_hardware_pins_request", b"get_node_remote_hardware_pins_request", "get_node_remote_hardware_pins_response", b"get_node_remote_hardware_pins_response", "get_owner_request", b"get_owner_request", "get_owner_response", b"get_owner_response", "get_ringtone_request", b"get_ringtone_request", "get_ringtone_response", b"get_ringtone_response", "nodedb_reset", b"nodedb_reset", "payload_variant", b"payload_variant", "reboot_ota_seconds", b"reboot_ota_seconds", "reboot_seconds", b"reboot_seconds", "remove_by_nodenum", b"remove_by_nodenum", "remove_favorite_node", b"remove_favorite_node", "remove_fixed_position", b"remove_fixed_position", "set_canned_message_module_messages", b"set_canned_message_module_messages", "set_channel", b"set_channel", "set_config", b"set_config", "set_favorite_node", b"set_favorite_node", "set_fixed_position", b"set_fixed_position", "set_ham_mode", b"set_ham_mode", "set_module_config", b"set_module_config", "set_owner", b"set_owner", "set_ringtone_message", b"set_ringtone_message", "set_scale", b"set_scale", "shutdown_seconds", b"shutdown_seconds"]) -> None: ... def WhichOneof(self, oneof_group: typing.Literal["payload_variant", b"payload_variant"]) -> typing.Literal["get_channel_request", "get_channel_response", "get_owner_request", "get_owner_response", "get_config_request", "get_config_response", "get_module_config_request", "get_module_config_response", "get_canned_message_module_messages_request", "get_canned_message_module_messages_response", "get_device_metadata_request", "get_device_metadata_response", "get_ringtone_request", "get_ringtone_response", "get_device_connection_status_request", "get_device_connection_status_response", "set_ham_mode", "get_node_remote_hardware_pins_request", "get_node_remote_hardware_pins_response", "enter_dfu_mode_request", "delete_file_request", "set_scale", "set_owner", "set_channel", "set_config", "set_module_config", "set_canned_message_module_messages", "set_ringtone_message", "remove_by_nodenum", "set_favorite_node", "remove_favorite_node", "set_fixed_position", "remove_fixed_position", "begin_edit_settings", "commit_edit_settings", "reboot_ota_seconds", "exit_simulator", "reboot_seconds", "shutdown_seconds", "factory_reset", "nodedb_reset"] | None: ... global___AdminMessage = AdminMessage @typing.final class HamParameters(google.protobuf.message.Message): """ Parameters for setting up Meshtastic for ameteur radio usage """ DESCRIPTOR: google.protobuf.descriptor.Descriptor CALL_SIGN_FIELD_NUMBER: builtins.int TX_POWER_FIELD_NUMBER: builtins.int FREQUENCY_FIELD_NUMBER: builtins.int SHORT_NAME_FIELD_NUMBER: builtins.int call_sign: builtins.str """ Amateur radio call sign, eg. KD2ABC """ tx_power: builtins.int """ Transmit power in dBm at the LoRA transceiver, not including any amplification """ frequency: builtins.float """ The selected frequency of LoRA operation Please respect your local laws, regulations, and band plans. Ensure your radio is capable of operating of the selected frequency before setting this. """ short_name: builtins.str """ Optional short name of user """ def __init__( self, *, call_sign: builtins.str = ..., tx_power: builtins.int = ..., frequency: builtins.float = ..., short_name: builtins.str = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["call_sign", b"call_sign", "frequency", b"frequency", "short_name", b"short_name", "tx_power", b"tx_power"]) -> None: ... global___HamParameters = HamParameters @typing.final class NodeRemoteHardwarePinsResponse(google.protobuf.message.Message): """ Response envelope for node_remote_hardware_pins """ DESCRIPTOR: google.protobuf.descriptor.Descriptor NODE_REMOTE_HARDWARE_PINS_FIELD_NUMBER: builtins.int @property def node_remote_hardware_pins(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[meshtastic.protobuf.mesh_pb2.NodeRemoteHardwarePin]: """ Nodes and their respective remote hardware GPIO pins """ def __init__( self, *, node_remote_hardware_pins: collections.abc.Iterable[meshtastic.protobuf.mesh_pb2.NodeRemoteHardwarePin] | None = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["node_remote_hardware_pins", b"node_remote_hardware_pins"]) -> None: ... global___NodeRemoteHardwarePinsResponse = NodeRemoteHardwarePinsResponse python-2.3.14/meshtastic/protobuf/apponly_pb2.py000066400000000000000000000033101464266072200217330ustar00rootroot00000000000000# -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # source: meshtastic/protobuf/apponly.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 symbol_database as _symbol_database from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() from meshtastic.protobuf import channel_pb2 as meshtastic_dot_protobuf_dot_channel__pb2 from meshtastic.protobuf import config_pb2 as meshtastic_dot_protobuf_dot_config__pb2 DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n!meshtastic/protobuf/apponly.proto\x12\x13meshtastic.protobuf\x1a!meshtastic/protobuf/channel.proto\x1a meshtastic/protobuf/config.proto\"\x81\x01\n\nChannelSet\x12\x36\n\x08settings\x18\x01 \x03(\x0b\x32$.meshtastic.protobuf.ChannelSettings\x12;\n\x0blora_config\x18\x02 \x01(\x0b\x32&.meshtastic.protobuf.Config.LoRaConfigBb\n\x13\x63om.geeksville.meshB\rAppOnlyProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.apponly_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\rAppOnlyProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' _globals['_CHANNELSET']._serialized_start=128 _globals['_CHANNELSET']._serialized_end=257 # @@protoc_insertion_point(module_scope) python-2.3.14/meshtastic/protobuf/apponly_pb2.pyi000066400000000000000000000034361464266072200221150ustar00rootroot00000000000000""" @generated by mypy-protobuf. Do not edit manually! isort:skip_file """ import builtins import collections.abc import google.protobuf.descriptor import google.protobuf.internal.containers import google.protobuf.message import meshtastic.protobuf.channel_pb2 import meshtastic.protobuf.config_pb2 import typing DESCRIPTOR: google.protobuf.descriptor.FileDescriptor @typing.final class ChannelSet(google.protobuf.message.Message): """ This is the most compact possible representation for a set of channels. It includes only one PRIMARY channel (which must be first) and any SECONDARY channels. No DISABLED channels are included. This abstraction is used only on the the 'app side' of the world (ie python, javascript and android etc) to show a group of Channels as a (long) URL """ DESCRIPTOR: google.protobuf.descriptor.Descriptor SETTINGS_FIELD_NUMBER: builtins.int LORA_CONFIG_FIELD_NUMBER: builtins.int @property def settings(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[meshtastic.protobuf.channel_pb2.ChannelSettings]: """ Channel list with settings """ @property def lora_config(self) -> meshtastic.protobuf.config_pb2.Config.LoRaConfig: """ LoRa config """ def __init__( self, *, settings: collections.abc.Iterable[meshtastic.protobuf.channel_pb2.ChannelSettings] | None = ..., lora_config: meshtastic.protobuf.config_pb2.Config.LoRaConfig | None = ..., ) -> None: ... def HasField(self, field_name: typing.Literal["lora_config", b"lora_config"]) -> builtins.bool: ... def ClearField(self, field_name: typing.Literal["lora_config", b"lora_config", "settings", b"settings"]) -> None: ... global___ChannelSet = ChannelSet python-2.3.14/meshtastic/protobuf/atak_pb2.py000066400000000000000000000073111464266072200211760ustar00rootroot00000000000000# -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # source: meshtastic/protobuf/atak.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 symbol_database as _symbol_database from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1emeshtastic/protobuf/atak.proto\x12\x13meshtastic.protobuf\"\x93\x02\n\tTAKPacket\x12\x15\n\ris_compressed\x18\x01 \x01(\x08\x12-\n\x07\x63ontact\x18\x02 \x01(\x0b\x32\x1c.meshtastic.protobuf.Contact\x12)\n\x05group\x18\x03 \x01(\x0b\x32\x1a.meshtastic.protobuf.Group\x12+\n\x06status\x18\x04 \x01(\x0b\x32\x1b.meshtastic.protobuf.Status\x12\'\n\x03pli\x18\x05 \x01(\x0b\x32\x18.meshtastic.protobuf.PLIH\x00\x12,\n\x04\x63hat\x18\x06 \x01(\x0b\x32\x1c.meshtastic.protobuf.GeoChatH\x00\x42\x11\n\x0fpayload_variant\"\\\n\x07GeoChat\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\x0f\n\x02to\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0bto_callsign\x18\x03 \x01(\tH\x01\x88\x01\x01\x42\x05\n\x03_toB\x0e\n\x0c_to_callsign\"_\n\x05Group\x12-\n\x04role\x18\x01 \x01(\x0e\x32\x1f.meshtastic.protobuf.MemberRole\x12\'\n\x04team\x18\x02 \x01(\x0e\x32\x19.meshtastic.protobuf.Team\"\x19\n\x06Status\x12\x0f\n\x07\x62\x61ttery\x18\x01 \x01(\r\"4\n\x07\x43ontact\x12\x10\n\x08\x63\x61llsign\x18\x01 \x01(\t\x12\x17\n\x0f\x64\x65vice_callsign\x18\x02 \x01(\t\"_\n\x03PLI\x12\x12\n\nlatitude_i\x18\x01 \x01(\x0f\x12\x13\n\x0blongitude_i\x18\x02 \x01(\x0f\x12\x10\n\x08\x61ltitude\x18\x03 \x01(\x05\x12\r\n\x05speed\x18\x04 \x01(\r\x12\x0e\n\x06\x63ourse\x18\x05 \x01(\r*\xc0\x01\n\x04Team\x12\x14\n\x10Unspecifed_Color\x10\x00\x12\t\n\x05White\x10\x01\x12\n\n\x06Yellow\x10\x02\x12\n\n\x06Orange\x10\x03\x12\x0b\n\x07Magenta\x10\x04\x12\x07\n\x03Red\x10\x05\x12\n\n\x06Maroon\x10\x06\x12\n\n\x06Purple\x10\x07\x12\r\n\tDark_Blue\x10\x08\x12\x08\n\x04\x42lue\x10\t\x12\x08\n\x04\x43yan\x10\n\x12\x08\n\x04Teal\x10\x0b\x12\t\n\x05Green\x10\x0c\x12\x0e\n\nDark_Green\x10\r\x12\t\n\x05\x42rown\x10\x0e*\x7f\n\nMemberRole\x12\x0e\n\nUnspecifed\x10\x00\x12\x0e\n\nTeamMember\x10\x01\x12\x0c\n\x08TeamLead\x10\x02\x12\x06\n\x02HQ\x10\x03\x12\n\n\x06Sniper\x10\x04\x12\t\n\x05Medic\x10\x05\x12\x13\n\x0f\x46orwardObserver\x10\x06\x12\x07\n\x03RTO\x10\x07\x12\x06\n\x02K9\x10\x08\x42_\n\x13\x63om.geeksville.meshB\nATAKProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.atak_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\nATAKProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' _globals['_TEAM']._serialized_start=703 _globals['_TEAM']._serialized_end=895 _globals['_MEMBERROLE']._serialized_start=897 _globals['_MEMBERROLE']._serialized_end=1024 _globals['_TAKPACKET']._serialized_start=56 _globals['_TAKPACKET']._serialized_end=331 _globals['_GEOCHAT']._serialized_start=333 _globals['_GEOCHAT']._serialized_end=425 _globals['_GROUP']._serialized_start=427 _globals['_GROUP']._serialized_end=522 _globals['_STATUS']._serialized_start=524 _globals['_STATUS']._serialized_end=549 _globals['_CONTACT']._serialized_start=551 _globals['_CONTACT']._serialized_end=603 _globals['_PLI']._serialized_start=605 _globals['_PLI']._serialized_end=700 # @@protoc_insertion_point(module_scope) python-2.3.14/meshtastic/protobuf/atak_pb2.pyi000066400000000000000000000246341464266072200213560ustar00rootroot00000000000000""" @generated by mypy-protobuf. Do not edit manually! isort:skip_file """ import builtins import google.protobuf.descriptor import google.protobuf.internal.enum_type_wrapper import google.protobuf.message import sys import typing if sys.version_info >= (3, 10): import typing as typing_extensions else: import typing_extensions DESCRIPTOR: google.protobuf.descriptor.FileDescriptor class _Team: ValueType = typing.NewType("ValueType", builtins.int) V: typing_extensions.TypeAlias = ValueType class _TeamEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_Team.ValueType], builtins.type): DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor Unspecifed_Color: _Team.ValueType # 0 """ Unspecifed """ White: _Team.ValueType # 1 """ White """ Yellow: _Team.ValueType # 2 """ Yellow """ Orange: _Team.ValueType # 3 """ Orange """ Magenta: _Team.ValueType # 4 """ Magenta """ Red: _Team.ValueType # 5 """ Red """ Maroon: _Team.ValueType # 6 """ Maroon """ Purple: _Team.ValueType # 7 """ Purple """ Dark_Blue: _Team.ValueType # 8 """ Dark Blue """ Blue: _Team.ValueType # 9 """ Blue """ Cyan: _Team.ValueType # 10 """ Cyan """ Teal: _Team.ValueType # 11 """ Teal """ Green: _Team.ValueType # 12 """ Green """ Dark_Green: _Team.ValueType # 13 """ Dark Green """ Brown: _Team.ValueType # 14 """ Brown """ class Team(_Team, metaclass=_TeamEnumTypeWrapper): ... Unspecifed_Color: Team.ValueType # 0 """ Unspecifed """ White: Team.ValueType # 1 """ White """ Yellow: Team.ValueType # 2 """ Yellow """ Orange: Team.ValueType # 3 """ Orange """ Magenta: Team.ValueType # 4 """ Magenta """ Red: Team.ValueType # 5 """ Red """ Maroon: Team.ValueType # 6 """ Maroon """ Purple: Team.ValueType # 7 """ Purple """ Dark_Blue: Team.ValueType # 8 """ Dark Blue """ Blue: Team.ValueType # 9 """ Blue """ Cyan: Team.ValueType # 10 """ Cyan """ Teal: Team.ValueType # 11 """ Teal """ Green: Team.ValueType # 12 """ Green """ Dark_Green: Team.ValueType # 13 """ Dark Green """ Brown: Team.ValueType # 14 """ Brown """ global___Team = Team class _MemberRole: ValueType = typing.NewType("ValueType", builtins.int) V: typing_extensions.TypeAlias = ValueType class _MemberRoleEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_MemberRole.ValueType], builtins.type): DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor Unspecifed: _MemberRole.ValueType # 0 """ Unspecifed """ TeamMember: _MemberRole.ValueType # 1 """ Team Member """ TeamLead: _MemberRole.ValueType # 2 """ Team Lead """ HQ: _MemberRole.ValueType # 3 """ Headquarters """ Sniper: _MemberRole.ValueType # 4 """ Airsoft enthusiast """ Medic: _MemberRole.ValueType # 5 """ Medic """ ForwardObserver: _MemberRole.ValueType # 6 """ ForwardObserver """ RTO: _MemberRole.ValueType # 7 """ Radio Telephone Operator """ K9: _MemberRole.ValueType # 8 """ Doggo """ class MemberRole(_MemberRole, metaclass=_MemberRoleEnumTypeWrapper): """ Role of the group member """ Unspecifed: MemberRole.ValueType # 0 """ Unspecifed """ TeamMember: MemberRole.ValueType # 1 """ Team Member """ TeamLead: MemberRole.ValueType # 2 """ Team Lead """ HQ: MemberRole.ValueType # 3 """ Headquarters """ Sniper: MemberRole.ValueType # 4 """ Airsoft enthusiast """ Medic: MemberRole.ValueType # 5 """ Medic """ ForwardObserver: MemberRole.ValueType # 6 """ ForwardObserver """ RTO: MemberRole.ValueType # 7 """ Radio Telephone Operator """ K9: MemberRole.ValueType # 8 """ Doggo """ global___MemberRole = MemberRole @typing.final class TAKPacket(google.protobuf.message.Message): """ Packets for the official ATAK Plugin """ DESCRIPTOR: google.protobuf.descriptor.Descriptor IS_COMPRESSED_FIELD_NUMBER: builtins.int CONTACT_FIELD_NUMBER: builtins.int GROUP_FIELD_NUMBER: builtins.int STATUS_FIELD_NUMBER: builtins.int PLI_FIELD_NUMBER: builtins.int CHAT_FIELD_NUMBER: builtins.int is_compressed: builtins.bool """ Are the payloads strings compressed for LoRA transport? """ @property def contact(self) -> global___Contact: """ The contact / callsign for ATAK user """ @property def group(self) -> global___Group: """ The group for ATAK user """ @property def status(self) -> global___Status: """ The status of the ATAK EUD """ @property def pli(self) -> global___PLI: """ TAK position report """ @property def chat(self) -> global___GeoChat: """ ATAK GeoChat message """ def __init__( self, *, is_compressed: builtins.bool = ..., contact: global___Contact | None = ..., group: global___Group | None = ..., status: global___Status | None = ..., pli: global___PLI | None = ..., chat: global___GeoChat | None = ..., ) -> None: ... def HasField(self, field_name: typing.Literal["chat", b"chat", "contact", b"contact", "group", b"group", "payload_variant", b"payload_variant", "pli", b"pli", "status", b"status"]) -> builtins.bool: ... def ClearField(self, field_name: typing.Literal["chat", b"chat", "contact", b"contact", "group", b"group", "is_compressed", b"is_compressed", "payload_variant", b"payload_variant", "pli", b"pli", "status", b"status"]) -> None: ... def WhichOneof(self, oneof_group: typing.Literal["payload_variant", b"payload_variant"]) -> typing.Literal["pli", "chat"] | None: ... global___TAKPacket = TAKPacket @typing.final class GeoChat(google.protobuf.message.Message): """ ATAK GeoChat message """ DESCRIPTOR: google.protobuf.descriptor.Descriptor MESSAGE_FIELD_NUMBER: builtins.int TO_FIELD_NUMBER: builtins.int TO_CALLSIGN_FIELD_NUMBER: builtins.int message: builtins.str """ The text message """ to: builtins.str """ Uid recipient of the message """ to_callsign: builtins.str """ Callsign of the recipient for the message """ def __init__( self, *, message: builtins.str = ..., to: builtins.str | None = ..., to_callsign: builtins.str | None = ..., ) -> None: ... def HasField(self, field_name: typing.Literal["_to", b"_to", "_to_callsign", b"_to_callsign", "to", b"to", "to_callsign", b"to_callsign"]) -> builtins.bool: ... def ClearField(self, field_name: typing.Literal["_to", b"_to", "_to_callsign", b"_to_callsign", "message", b"message", "to", b"to", "to_callsign", b"to_callsign"]) -> None: ... @typing.overload def WhichOneof(self, oneof_group: typing.Literal["_to", b"_to"]) -> typing.Literal["to"] | None: ... @typing.overload def WhichOneof(self, oneof_group: typing.Literal["_to_callsign", b"_to_callsign"]) -> typing.Literal["to_callsign"] | None: ... global___GeoChat = GeoChat @typing.final class Group(google.protobuf.message.Message): """ ATAK Group <__group role='Team Member' name='Cyan'/> """ DESCRIPTOR: google.protobuf.descriptor.Descriptor ROLE_FIELD_NUMBER: builtins.int TEAM_FIELD_NUMBER: builtins.int role: global___MemberRole.ValueType """ Role of the group member """ team: global___Team.ValueType """ Team (color) Default Cyan """ def __init__( self, *, role: global___MemberRole.ValueType = ..., team: global___Team.ValueType = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["role", b"role", "team", b"team"]) -> None: ... global___Group = Group @typing.final class Status(google.protobuf.message.Message): """ ATAK EUD Status """ DESCRIPTOR: google.protobuf.descriptor.Descriptor BATTERY_FIELD_NUMBER: builtins.int battery: builtins.int """ Battery level """ def __init__( self, *, battery: builtins.int = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["battery", b"battery"]) -> None: ... global___Status = Status @typing.final class Contact(google.protobuf.message.Message): """ ATAK Contact """ DESCRIPTOR: google.protobuf.descriptor.Descriptor CALLSIGN_FIELD_NUMBER: builtins.int DEVICE_CALLSIGN_FIELD_NUMBER: builtins.int callsign: builtins.str """ Callsign """ device_callsign: builtins.str """ Device callsign IP address of endpoint in integer form (0.0.0.0 default) """ def __init__( self, *, callsign: builtins.str = ..., device_callsign: builtins.str = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["callsign", b"callsign", "device_callsign", b"device_callsign"]) -> None: ... global___Contact = Contact @typing.final class PLI(google.protobuf.message.Message): """ Position Location Information from ATAK """ DESCRIPTOR: google.protobuf.descriptor.Descriptor LATITUDE_I_FIELD_NUMBER: builtins.int LONGITUDE_I_FIELD_NUMBER: builtins.int ALTITUDE_FIELD_NUMBER: builtins.int SPEED_FIELD_NUMBER: builtins.int COURSE_FIELD_NUMBER: builtins.int latitude_i: builtins.int """ The new preferred location encoding, multiply by 1e-7 to get degrees in floating point """ longitude_i: builtins.int """ The new preferred location encoding, multiply by 1e-7 to get degrees in floating point """ altitude: builtins.int """ Altitude (ATAK prefers HAE) """ speed: builtins.int """ Speed """ course: builtins.int """ Course in degrees """ def __init__( self, *, latitude_i: builtins.int = ..., longitude_i: builtins.int = ..., altitude: builtins.int = ..., speed: builtins.int = ..., course: builtins.int = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["altitude", b"altitude", "course", b"course", "latitude_i", b"latitude_i", "longitude_i", b"longitude_i", "speed", b"speed"]) -> None: ... global___PLI = PLI python-2.3.14/meshtastic/protobuf/cannedmessages_pb2.py000066400000000000000000000026651464266072200232450ustar00rootroot00000000000000# -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # source: meshtastic/protobuf/cannedmessages.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 symbol_database as _symbol_database from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n(meshtastic/protobuf/cannedmessages.proto\x12\x13meshtastic.protobuf\"-\n\x19\x43\x61nnedMessageModuleConfig\x12\x10\n\x08messages\x18\x01 \x01(\tBn\n\x13\x63om.geeksville.meshB\x19\x43\x61nnedMessageConfigProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.cannedmessages_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\031CannedMessageConfigProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' _globals['_CANNEDMESSAGEMODULECONFIG']._serialized_start=65 _globals['_CANNEDMESSAGEMODULECONFIG']._serialized_end=110 # @@protoc_insertion_point(module_scope) python-2.3.14/meshtastic/protobuf/cannedmessages_pb2.pyi000066400000000000000000000015061464266072200234070ustar00rootroot00000000000000""" @generated by mypy-protobuf. Do not edit manually! isort:skip_file """ import builtins import google.protobuf.descriptor import google.protobuf.message import typing DESCRIPTOR: google.protobuf.descriptor.FileDescriptor @typing.final class CannedMessageModuleConfig(google.protobuf.message.Message): """ Canned message module configuration. """ DESCRIPTOR: google.protobuf.descriptor.Descriptor MESSAGES_FIELD_NUMBER: builtins.int messages: builtins.str """ Predefined messages for canned message module separated by '|' characters. """ def __init__( self, *, messages: builtins.str = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["messages", b"messages"]) -> None: ... global___CannedMessageModuleConfig = CannedMessageModuleConfig python-2.3.14/meshtastic/protobuf/channel_pb2.py000066400000000000000000000047741464266072200217000ustar00rootroot00000000000000# -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # source: meshtastic/protobuf/channel.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 symbol_database as _symbol_database from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n!meshtastic/protobuf/channel.proto\x12\x13meshtastic.protobuf\"\xc1\x01\n\x0f\x43hannelSettings\x12\x17\n\x0b\x63hannel_num\x18\x01 \x01(\rB\x02\x18\x01\x12\x0b\n\x03psk\x18\x02 \x01(\x0c\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\n\n\x02id\x18\x04 \x01(\x07\x12\x16\n\x0euplink_enabled\x18\x05 \x01(\x08\x12\x18\n\x10\x64ownlink_enabled\x18\x06 \x01(\x08\x12<\n\x0fmodule_settings\x18\x07 \x01(\x0b\x32#.meshtastic.protobuf.ModuleSettings\"E\n\x0eModuleSettings\x12\x1a\n\x12position_precision\x18\x01 \x01(\r\x12\x17\n\x0fis_client_muted\x18\x02 \x01(\x08\"\xb3\x01\n\x07\x43hannel\x12\r\n\x05index\x18\x01 \x01(\x05\x12\x36\n\x08settings\x18\x02 \x01(\x0b\x32$.meshtastic.protobuf.ChannelSettings\x12/\n\x04role\x18\x03 \x01(\x0e\x32!.meshtastic.protobuf.Channel.Role\"0\n\x04Role\x12\x0c\n\x08\x44ISABLED\x10\x00\x12\x0b\n\x07PRIMARY\x10\x01\x12\r\n\tSECONDARY\x10\x02\x42\x62\n\x13\x63om.geeksville.meshB\rChannelProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.channel_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\rChannelProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' _CHANNELSETTINGS.fields_by_name['channel_num']._options = None _CHANNELSETTINGS.fields_by_name['channel_num']._serialized_options = b'\030\001' _globals['_CHANNELSETTINGS']._serialized_start=59 _globals['_CHANNELSETTINGS']._serialized_end=252 _globals['_MODULESETTINGS']._serialized_start=254 _globals['_MODULESETTINGS']._serialized_end=323 _globals['_CHANNEL']._serialized_start=326 _globals['_CHANNEL']._serialized_end=505 _globals['_CHANNEL_ROLE']._serialized_start=457 _globals['_CHANNEL_ROLE']._serialized_end=505 # @@protoc_insertion_point(module_scope) python-2.3.14/meshtastic/protobuf/channel_pb2.pyi000066400000000000000000000230011464266072200220310ustar00rootroot00000000000000""" @generated by mypy-protobuf. Do not edit manually! isort:skip_file """ import builtins import google.protobuf.descriptor import google.protobuf.internal.enum_type_wrapper import google.protobuf.message import sys import typing if sys.version_info >= (3, 10): import typing as typing_extensions else: import typing_extensions DESCRIPTOR: google.protobuf.descriptor.FileDescriptor @typing.final class ChannelSettings(google.protobuf.message.Message): """ This information can be encoded as a QRcode/url so that other users can configure their radio to join the same channel. A note about how channel names are shown to users: channelname-X poundsymbol is a prefix used to indicate this is a channel name (idea from @professr). Where X is a letter from A-Z (base 26) representing a hash of the PSK for this channel - so that if the user changes anything about the channel (which does force a new PSK) this letter will also change. Thus preventing user confusion if two friends try to type in a channel name of "BobsChan" and then can't talk because their PSKs will be different. The PSK is hashed into this letter by "0x41 + [xor all bytes of the psk ] modulo 26" This also allows the option of someday if people have the PSK off (zero), the users COULD type in a channel name and be able to talk. FIXME: Add description of multi-channel support and how primary vs secondary channels are used. FIXME: explain how apps use channels for security. explain how remote settings and remote gpio are managed as an example """ DESCRIPTOR: google.protobuf.descriptor.Descriptor CHANNEL_NUM_FIELD_NUMBER: builtins.int PSK_FIELD_NUMBER: builtins.int NAME_FIELD_NUMBER: builtins.int ID_FIELD_NUMBER: builtins.int UPLINK_ENABLED_FIELD_NUMBER: builtins.int DOWNLINK_ENABLED_FIELD_NUMBER: builtins.int MODULE_SETTINGS_FIELD_NUMBER: builtins.int channel_num: builtins.int """ Deprecated in favor of LoraConfig.channel_num """ psk: builtins.bytes """ A simple pre-shared key for now for crypto. Must be either 0 bytes (no crypto), 16 bytes (AES128), or 32 bytes (AES256). A special shorthand is used for 1 byte long psks. These psks should be treated as only minimally secure, because they are listed in this source code. Those bytes are mapped using the following scheme: `0` = No crypto `1` = The special "default" channel key: {0xd4, 0xf1, 0xbb, 0x3a, 0x20, 0x29, 0x07, 0x59, 0xf0, 0xbc, 0xff, 0xab, 0xcf, 0x4e, 0x69, 0x01} `2` through 10 = The default channel key, except with 1 through 9 added to the last byte. Shown to user as simple1 through 10 """ name: builtins.str """ A SHORT name that will be packed into the URL. Less than 12 bytes. Something for end users to call the channel If this is the empty string it is assumed that this channel is the special (minimally secure) "Default"channel. In user interfaces it should be rendered as a local language translation of "X". For channel_num hashing empty string will be treated as "X". Where "X" is selected based on the English words listed above for ModemPreset """ id: builtins.int """ Used to construct a globally unique channel ID. The full globally unique ID will be: "name.id" where ID is shown as base36. Assuming that the number of meshtastic users is below 20K (true for a long time) the chance of this 64 bit random number colliding with anyone else is super low. And the penalty for collision is low as well, it just means that anyone trying to decrypt channel messages might need to try multiple candidate channels. Any time a non wire compatible change is made to a channel, this field should be regenerated. There are a small number of 'special' globally known (and fairly) insecure standard channels. Those channels do not have a numeric id included in the settings, but instead it is pulled from a table of well known IDs. (see Well Known Channels FIXME) """ uplink_enabled: builtins.bool """ If true, messages on the mesh will be sent to the *public* internet by any gateway ndoe """ downlink_enabled: builtins.bool """ If true, messages seen on the internet will be forwarded to the local mesh. """ @property def module_settings(self) -> global___ModuleSettings: """ Per-channel module settings. """ def __init__( self, *, channel_num: builtins.int = ..., psk: builtins.bytes = ..., name: builtins.str = ..., id: builtins.int = ..., uplink_enabled: builtins.bool = ..., downlink_enabled: builtins.bool = ..., module_settings: global___ModuleSettings | None = ..., ) -> None: ... def HasField(self, field_name: typing.Literal["module_settings", b"module_settings"]) -> builtins.bool: ... def ClearField(self, field_name: typing.Literal["channel_num", b"channel_num", "downlink_enabled", b"downlink_enabled", "id", b"id", "module_settings", b"module_settings", "name", b"name", "psk", b"psk", "uplink_enabled", b"uplink_enabled"]) -> None: ... global___ChannelSettings = ChannelSettings @typing.final class ModuleSettings(google.protobuf.message.Message): """ This message is specifically for modules to store per-channel configuration data. """ DESCRIPTOR: google.protobuf.descriptor.Descriptor POSITION_PRECISION_FIELD_NUMBER: builtins.int IS_CLIENT_MUTED_FIELD_NUMBER: builtins.int position_precision: builtins.int """ Bits of precision for the location sent in position packets. """ is_client_muted: builtins.bool """ Controls whether or not the phone / clients should mute the current channel Useful for noisy public channels you don't necessarily want to disable """ def __init__( self, *, position_precision: builtins.int = ..., is_client_muted: builtins.bool = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["is_client_muted", b"is_client_muted", "position_precision", b"position_precision"]) -> None: ... global___ModuleSettings = ModuleSettings @typing.final class Channel(google.protobuf.message.Message): """ A pair of a channel number, mode and the (sharable) settings for that channel """ DESCRIPTOR: google.protobuf.descriptor.Descriptor class _Role: ValueType = typing.NewType("ValueType", builtins.int) V: typing_extensions.TypeAlias = ValueType class _RoleEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[Channel._Role.ValueType], builtins.type): DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor DISABLED: Channel._Role.ValueType # 0 """ This channel is not in use right now """ PRIMARY: Channel._Role.ValueType # 1 """ This channel is used to set the frequency for the radio - all other enabled channels must be SECONDARY """ SECONDARY: Channel._Role.ValueType # 2 """ Secondary channels are only used for encryption/decryption/authentication purposes. Their radio settings (freq etc) are ignored, only psk is used. """ class Role(_Role, metaclass=_RoleEnumTypeWrapper): """ How this channel is being used (or not). Note: this field is an enum to give us options for the future. In particular, someday we might make a 'SCANNING' option. SCANNING channels could have different frequencies and the radio would occasionally check that freq to see if anything is being transmitted. For devices that have multiple physical radios attached, we could keep multiple PRIMARY/SCANNING channels active at once to allow cross band routing as needed. If a device has only a single radio (the common case) only one channel can be PRIMARY at a time (but any number of SECONDARY channels can't be sent received on that common frequency) """ DISABLED: Channel.Role.ValueType # 0 """ This channel is not in use right now """ PRIMARY: Channel.Role.ValueType # 1 """ This channel is used to set the frequency for the radio - all other enabled channels must be SECONDARY """ SECONDARY: Channel.Role.ValueType # 2 """ Secondary channels are only used for encryption/decryption/authentication purposes. Their radio settings (freq etc) are ignored, only psk is used. """ INDEX_FIELD_NUMBER: builtins.int SETTINGS_FIELD_NUMBER: builtins.int ROLE_FIELD_NUMBER: builtins.int index: builtins.int """ The index of this channel in the channel table (from 0 to MAX_NUM_CHANNELS-1) (Someday - not currently implemented) An index of -1 could be used to mean "set by name", in which case the target node will find and set the channel by settings.name. """ role: global___Channel.Role.ValueType """ TODO: REPLACE """ @property def settings(self) -> global___ChannelSettings: """ The new settings, or NULL to disable that channel """ def __init__( self, *, index: builtins.int = ..., settings: global___ChannelSettings | None = ..., role: global___Channel.Role.ValueType = ..., ) -> None: ... def HasField(self, field_name: typing.Literal["settings", b"settings"]) -> builtins.bool: ... def ClearField(self, field_name: typing.Literal["index", b"index", "role", b"role", "settings", b"settings"]) -> None: ... global___Channel = Channel python-2.3.14/meshtastic/protobuf/clientonly_pb2.py000066400000000000000000000036471464266072200224460ustar00rootroot00000000000000# -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # source: meshtastic/protobuf/clientonly.proto """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() from meshtastic.protobuf import localonly_pb2 as meshtastic_dot_protobuf_dot_localonly__pb2 DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n$meshtastic/protobuf/clientonly.proto\x12\x13meshtastic.protobuf\x1a#meshtastic/protobuf/localonly.proto\"\x9f\x02\n\rDeviceProfile\x12\x16\n\tlong_name\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x17\n\nshort_name\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0b\x63hannel_url\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x35\n\x06\x63onfig\x18\x04 \x01(\x0b\x32 .meshtastic.protobuf.LocalConfigH\x03\x88\x01\x01\x12\x42\n\rmodule_config\x18\x05 \x01(\x0b\x32&.meshtastic.protobuf.LocalModuleConfigH\x04\x88\x01\x01\x42\x0c\n\n_long_nameB\r\n\x0b_short_nameB\x0e\n\x0c_channel_urlB\t\n\x07_configB\x10\n\x0e_module_configBe\n\x13\x63om.geeksville.meshB\x10\x43lientOnlyProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.clientonly_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\020ClientOnlyProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' _globals['_DEVICEPROFILE']._serialized_start=99 _globals['_DEVICEPROFILE']._serialized_end=386 # @@protoc_insertion_point(module_scope) python-2.3.14/meshtastic/protobuf/clientonly_pb2.pyi000066400000000000000000000061721464266072200226130ustar00rootroot00000000000000""" @generated by mypy-protobuf. Do not edit manually! isort:skip_file """ import builtins import google.protobuf.descriptor import google.protobuf.message import meshtastic.protobuf.localonly_pb2 import typing DESCRIPTOR: google.protobuf.descriptor.FileDescriptor @typing.final class DeviceProfile(google.protobuf.message.Message): """ This abstraction is used to contain any configuration for provisioning a node on any client. It is useful for importing and exporting configurations. """ DESCRIPTOR: google.protobuf.descriptor.Descriptor LONG_NAME_FIELD_NUMBER: builtins.int SHORT_NAME_FIELD_NUMBER: builtins.int CHANNEL_URL_FIELD_NUMBER: builtins.int CONFIG_FIELD_NUMBER: builtins.int MODULE_CONFIG_FIELD_NUMBER: builtins.int long_name: builtins.str """ Long name for the node """ short_name: builtins.str """ Short name of the node """ channel_url: builtins.str """ The url of the channels from our node """ @property def config(self) -> meshtastic.protobuf.localonly_pb2.LocalConfig: """ The Config of the node """ @property def module_config(self) -> meshtastic.protobuf.localonly_pb2.LocalModuleConfig: """ The ModuleConfig of the node """ def __init__( self, *, long_name: builtins.str | None = ..., short_name: builtins.str | None = ..., channel_url: builtins.str | None = ..., config: meshtastic.protobuf.localonly_pb2.LocalConfig | None = ..., module_config: meshtastic.protobuf.localonly_pb2.LocalModuleConfig | None = ..., ) -> None: ... def HasField(self, field_name: typing.Literal["_channel_url", b"_channel_url", "_config", b"_config", "_long_name", b"_long_name", "_module_config", b"_module_config", "_short_name", b"_short_name", "channel_url", b"channel_url", "config", b"config", "long_name", b"long_name", "module_config", b"module_config", "short_name", b"short_name"]) -> builtins.bool: ... def ClearField(self, field_name: typing.Literal["_channel_url", b"_channel_url", "_config", b"_config", "_long_name", b"_long_name", "_module_config", b"_module_config", "_short_name", b"_short_name", "channel_url", b"channel_url", "config", b"config", "long_name", b"long_name", "module_config", b"module_config", "short_name", b"short_name"]) -> None: ... @typing.overload def WhichOneof(self, oneof_group: typing.Literal["_channel_url", b"_channel_url"]) -> typing.Literal["channel_url"] | None: ... @typing.overload def WhichOneof(self, oneof_group: typing.Literal["_config", b"_config"]) -> typing.Literal["config"] | None: ... @typing.overload def WhichOneof(self, oneof_group: typing.Literal["_long_name", b"_long_name"]) -> typing.Literal["long_name"] | None: ... @typing.overload def WhichOneof(self, oneof_group: typing.Literal["_module_config", b"_module_config"]) -> typing.Literal["module_config"] | None: ... @typing.overload def WhichOneof(self, oneof_group: typing.Literal["_short_name", b"_short_name"]) -> typing.Literal["short_name"] | None: ... global___DeviceProfile = DeviceProfile python-2.3.14/meshtastic/protobuf/config_pb2.py000066400000000000000000000302061464266072200215220ustar00rootroot00000000000000# -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # source: meshtastic/protobuf/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 symbol_database as _symbol_database from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n meshtastic/protobuf/config.proto\x12\x13meshtastic.protobuf\"\xbf#\n\x06\x43onfig\x12:\n\x06\x64\x65vice\x18\x01 \x01(\x0b\x32(.meshtastic.protobuf.Config.DeviceConfigH\x00\x12>\n\x08position\x18\x02 \x01(\x0b\x32*.meshtastic.protobuf.Config.PositionConfigH\x00\x12\x38\n\x05power\x18\x03 \x01(\x0b\x32\'.meshtastic.protobuf.Config.PowerConfigH\x00\x12<\n\x07network\x18\x04 \x01(\x0b\x32).meshtastic.protobuf.Config.NetworkConfigH\x00\x12<\n\x07\x64isplay\x18\x05 \x01(\x0b\x32).meshtastic.protobuf.Config.DisplayConfigH\x00\x12\x36\n\x04lora\x18\x06 \x01(\x0b\x32&.meshtastic.protobuf.Config.LoRaConfigH\x00\x12@\n\tbluetooth\x18\x07 \x01(\x0b\x32+.meshtastic.protobuf.Config.BluetoothConfigH\x00\x1a\xa3\x05\n\x0c\x44\x65viceConfig\x12;\n\x04role\x18\x01 \x01(\x0e\x32-.meshtastic.protobuf.Config.DeviceConfig.Role\x12\x16\n\x0eserial_enabled\x18\x02 \x01(\x08\x12\x19\n\x11\x64\x65\x62ug_log_enabled\x18\x03 \x01(\x08\x12\x13\n\x0b\x62utton_gpio\x18\x04 \x01(\r\x12\x13\n\x0b\x62uzzer_gpio\x18\x05 \x01(\r\x12R\n\x10rebroadcast_mode\x18\x06 \x01(\x0e\x32\x38.meshtastic.protobuf.Config.DeviceConfig.RebroadcastMode\x12 \n\x18node_info_broadcast_secs\x18\x07 \x01(\r\x12\"\n\x1a\x64ouble_tap_as_button_press\x18\x08 \x01(\x08\x12\x12\n\nis_managed\x18\t \x01(\x08\x12\x1c\n\x14\x64isable_triple_click\x18\n \x01(\x08\x12\r\n\x05tzdef\x18\x0b \x01(\t\x12\x1e\n\x16led_heartbeat_disabled\x18\x0c \x01(\x08\"\xaa\x01\n\x04Role\x12\n\n\x06\x43LIENT\x10\x00\x12\x0f\n\x0b\x43LIENT_MUTE\x10\x01\x12\n\n\x06ROUTER\x10\x02\x12\x11\n\rROUTER_CLIENT\x10\x03\x12\x0c\n\x08REPEATER\x10\x04\x12\x0b\n\x07TRACKER\x10\x05\x12\n\n\x06SENSOR\x10\x06\x12\x07\n\x03TAK\x10\x07\x12\x11\n\rCLIENT_HIDDEN\x10\x08\x12\x12\n\x0eLOST_AND_FOUND\x10\t\x12\x0f\n\x0bTAK_TRACKER\x10\n\"Q\n\x0fRebroadcastMode\x12\x07\n\x03\x41LL\x10\x00\x12\x15\n\x11\x41LL_SKIP_DECODING\x10\x01\x12\x0e\n\nLOCAL_ONLY\x10\x02\x12\x0e\n\nKNOWN_ONLY\x10\x03\x1a\x9a\x05\n\x0ePositionConfig\x12\x1f\n\x17position_broadcast_secs\x18\x01 \x01(\r\x12(\n position_broadcast_smart_enabled\x18\x02 \x01(\x08\x12\x16\n\x0e\x66ixed_position\x18\x03 \x01(\x08\x12\x17\n\x0bgps_enabled\x18\x04 \x01(\x08\x42\x02\x18\x01\x12\x1b\n\x13gps_update_interval\x18\x05 \x01(\r\x12\x1c\n\x10gps_attempt_time\x18\x06 \x01(\rB\x02\x18\x01\x12\x16\n\x0eposition_flags\x18\x07 \x01(\r\x12\x0f\n\x07rx_gpio\x18\x08 \x01(\r\x12\x0f\n\x07tx_gpio\x18\t \x01(\r\x12(\n broadcast_smart_minimum_distance\x18\n \x01(\r\x12-\n%broadcast_smart_minimum_interval_secs\x18\x0b \x01(\r\x12\x13\n\x0bgps_en_gpio\x18\x0c \x01(\r\x12\x44\n\x08gps_mode\x18\r \x01(\x0e\x32\x32.meshtastic.protobuf.Config.PositionConfig.GpsMode\"\xab\x01\n\rPositionFlags\x12\t\n\x05UNSET\x10\x00\x12\x0c\n\x08\x41LTITUDE\x10\x01\x12\x10\n\x0c\x41LTITUDE_MSL\x10\x02\x12\x16\n\x12GEOIDAL_SEPARATION\x10\x04\x12\x07\n\x03\x44OP\x10\x08\x12\t\n\x05HVDOP\x10\x10\x12\r\n\tSATINVIEW\x10 \x12\n\n\x06SEQ_NO\x10@\x12\x0e\n\tTIMESTAMP\x10\x80\x01\x12\x0c\n\x07HEADING\x10\x80\x02\x12\n\n\x05SPEED\x10\x80\x04\"5\n\x07GpsMode\x12\x0c\n\x08\x44ISABLED\x10\x00\x12\x0b\n\x07\x45NABLED\x10\x01\x12\x0f\n\x0bNOT_PRESENT\x10\x02\x1a\x84\x02\n\x0bPowerConfig\x12\x17\n\x0fis_power_saving\x18\x01 \x01(\x08\x12&\n\x1eon_battery_shutdown_after_secs\x18\x02 \x01(\r\x12\x1f\n\x17\x61\x64\x63_multiplier_override\x18\x03 \x01(\x02\x12\x1b\n\x13wait_bluetooth_secs\x18\x04 \x01(\r\x12\x10\n\x08sds_secs\x18\x06 \x01(\r\x12\x0f\n\x07ls_secs\x18\x07 \x01(\r\x12\x15\n\rmin_wake_secs\x18\x08 \x01(\r\x12\"\n\x1a\x64\x65vice_battery_ina_address\x18\t \x01(\r\x12\x18\n\x10powermon_enables\x18 \x01(\x04\x1a\x90\x03\n\rNetworkConfig\x12\x14\n\x0cwifi_enabled\x18\x01 \x01(\x08\x12\x11\n\twifi_ssid\x18\x03 \x01(\t\x12\x10\n\x08wifi_psk\x18\x04 \x01(\t\x12\x12\n\nntp_server\x18\x05 \x01(\t\x12\x13\n\x0b\x65th_enabled\x18\x06 \x01(\x08\x12K\n\x0c\x61\x64\x64ress_mode\x18\x07 \x01(\x0e\x32\x35.meshtastic.protobuf.Config.NetworkConfig.AddressMode\x12I\n\x0bipv4_config\x18\x08 \x01(\x0b\x32\x34.meshtastic.protobuf.Config.NetworkConfig.IpV4Config\x12\x16\n\x0ersyslog_server\x18\t \x01(\t\x1a\x46\n\nIpV4Config\x12\n\n\x02ip\x18\x01 \x01(\x07\x12\x0f\n\x07gateway\x18\x02 \x01(\x07\x12\x0e\n\x06subnet\x18\x03 \x01(\x07\x12\x0b\n\x03\x64ns\x18\x04 \x01(\x07\"#\n\x0b\x41\x64\x64ressMode\x12\x08\n\x04\x44HCP\x10\x00\x12\n\n\x06STATIC\x10\x01\x1a\xfa\x07\n\rDisplayConfig\x12\x16\n\x0escreen_on_secs\x18\x01 \x01(\r\x12Q\n\ngps_format\x18\x02 \x01(\x0e\x32=.meshtastic.protobuf.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\x12\x13\n\x0b\x66lip_screen\x18\x05 \x01(\x08\x12\x45\n\x05units\x18\x06 \x01(\x0e\x32\x36.meshtastic.protobuf.Config.DisplayConfig.DisplayUnits\x12@\n\x04oled\x18\x07 \x01(\x0e\x32\x32.meshtastic.protobuf.Config.DisplayConfig.OledType\x12J\n\x0b\x64isplaymode\x18\x08 \x01(\x0e\x32\x35.meshtastic.protobuf.Config.DisplayConfig.DisplayMode\x12\x14\n\x0cheading_bold\x18\t \x01(\x08\x12\x1d\n\x15wake_on_tap_or_motion\x18\n \x01(\x08\x12Y\n\x13\x63ompass_orientation\x18\x0b \x01(\x0e\x32<.meshtastic.protobuf.Config.DisplayConfig.CompassOrientation\"M\n\x13GpsCoordinateFormat\x12\x07\n\x03\x44\x45\x43\x10\x00\x12\x07\n\x03\x44MS\x10\x01\x12\x07\n\x03UTM\x10\x02\x12\x08\n\x04MGRS\x10\x03\x12\x07\n\x03OLC\x10\x04\x12\x08\n\x04OSGR\x10\x05\"(\n\x0c\x44isplayUnits\x12\n\n\x06METRIC\x10\x00\x12\x0c\n\x08IMPERIAL\x10\x01\"M\n\x08OledType\x12\r\n\tOLED_AUTO\x10\x00\x12\x10\n\x0cOLED_SSD1306\x10\x01\x12\x0f\n\x0bOLED_SH1106\x10\x02\x12\x0f\n\x0bOLED_SH1107\x10\x03\"A\n\x0b\x44isplayMode\x12\x0b\n\x07\x44\x45\x46\x41ULT\x10\x00\x12\x0c\n\x08TWOCOLOR\x10\x01\x12\x0c\n\x08INVERTED\x10\x02\x12\t\n\x05\x43OLOR\x10\x03\"\xba\x01\n\x12\x43ompassOrientation\x12\r\n\tDEGREES_0\x10\x00\x12\x0e\n\nDEGREES_90\x10\x01\x12\x0f\n\x0b\x44\x45GREES_180\x10\x02\x12\x0f\n\x0b\x44\x45GREES_270\x10\x03\x12\x16\n\x12\x44\x45GREES_0_INVERTED\x10\x04\x12\x17\n\x13\x44\x45GREES_90_INVERTED\x10\x05\x12\x18\n\x14\x44\x45GREES_180_INVERTED\x10\x06\x12\x18\n\x14\x44\x45GREES_270_INVERTED\x10\x07\x1a\xc2\x06\n\nLoRaConfig\x12\x12\n\nuse_preset\x18\x01 \x01(\x08\x12H\n\x0cmodem_preset\x18\x02 \x01(\x0e\x32\x32.meshtastic.protobuf.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\x41\n\x06region\x18\x07 \x01(\x0e\x32\x31.meshtastic.protobuf.Config.LoRaConfig.RegionCode\x12\x11\n\thop_limit\x18\x08 \x01(\r\x12\x12\n\ntx_enabled\x18\t \x01(\x08\x12\x10\n\x08tx_power\x18\n \x01(\x05\x12\x13\n\x0b\x63hannel_num\x18\x0b \x01(\r\x12\x1b\n\x13override_duty_cycle\x18\x0c \x01(\x08\x12\x1e\n\x16sx126x_rx_boosted_gain\x18\r \x01(\x08\x12\x1a\n\x12override_frequency\x18\x0e \x01(\x02\x12\x17\n\x0fignore_incoming\x18g \x03(\r\x12\x13\n\x0bignore_mqtt\x18h \x01(\x08\"\xcd\x01\n\nRegionCode\x12\t\n\x05UNSET\x10\x00\x12\x06\n\x02US\x10\x01\x12\n\n\x06\x45U_433\x10\x02\x12\n\n\x06\x45U_868\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\n\n\x06NZ_865\x10\x0b\x12\x06\n\x02TH\x10\x0c\x12\x0b\n\x07LORA_24\x10\r\x12\n\n\x06UA_433\x10\x0e\x12\n\n\x06UA_868\x10\x0f\x12\n\n\x06MY_433\x10\x10\x12\n\n\x06MY_919\x10\x11\x12\n\n\x06SG_923\x10\x12\"\x94\x01\n\x0bModemPreset\x12\r\n\tLONG_FAST\x10\x00\x12\r\n\tLONG_SLOW\x10\x01\x12\x12\n\x0eVERY_LONG_SLOW\x10\x02\x12\x0f\n\x0bMEDIUM_SLOW\x10\x03\x12\x0f\n\x0bMEDIUM_FAST\x10\x04\x12\x0e\n\nSHORT_SLOW\x10\x05\x12\x0e\n\nSHORT_FAST\x10\x06\x12\x11\n\rLONG_MODERATE\x10\x07\x1a\xd6\x01\n\x0f\x42luetoothConfig\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x45\n\x04mode\x18\x02 \x01(\x0e\x32\x37.meshtastic.protobuf.Config.BluetoothConfig.PairingMode\x12\x11\n\tfixed_pin\x18\x03 \x01(\r\x12\x1e\n\x16\x64\x65vice_logging_enabled\x18\x04 \x01(\x08\"8\n\x0bPairingMode\x12\x0e\n\nRANDOM_PIN\x10\x00\x12\r\n\tFIXED_PIN\x10\x01\x12\n\n\x06NO_PIN\x10\x02\x42\x11\n\x0fpayload_variantBa\n\x13\x63om.geeksville.meshB\x0c\x43onfigProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.config_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\014ConfigProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' _CONFIG_POSITIONCONFIG.fields_by_name['gps_enabled']._options = None _CONFIG_POSITIONCONFIG.fields_by_name['gps_enabled']._serialized_options = b'\030\001' _CONFIG_POSITIONCONFIG.fields_by_name['gps_attempt_time']._options = None _CONFIG_POSITIONCONFIG.fields_by_name['gps_attempt_time']._serialized_options = b'\030\001' _globals['_CONFIG']._serialized_start=58 _globals['_CONFIG']._serialized_end=4601 _globals['_CONFIG_DEVICECONFIG']._serialized_start=497 _globals['_CONFIG_DEVICECONFIG']._serialized_end=1172 _globals['_CONFIG_DEVICECONFIG_ROLE']._serialized_start=919 _globals['_CONFIG_DEVICECONFIG_ROLE']._serialized_end=1089 _globals['_CONFIG_DEVICECONFIG_REBROADCASTMODE']._serialized_start=1091 _globals['_CONFIG_DEVICECONFIG_REBROADCASTMODE']._serialized_end=1172 _globals['_CONFIG_POSITIONCONFIG']._serialized_start=1175 _globals['_CONFIG_POSITIONCONFIG']._serialized_end=1841 _globals['_CONFIG_POSITIONCONFIG_POSITIONFLAGS']._serialized_start=1615 _globals['_CONFIG_POSITIONCONFIG_POSITIONFLAGS']._serialized_end=1786 _globals['_CONFIG_POSITIONCONFIG_GPSMODE']._serialized_start=1788 _globals['_CONFIG_POSITIONCONFIG_GPSMODE']._serialized_end=1841 _globals['_CONFIG_POWERCONFIG']._serialized_start=1844 _globals['_CONFIG_POWERCONFIG']._serialized_end=2104 _globals['_CONFIG_NETWORKCONFIG']._serialized_start=2107 _globals['_CONFIG_NETWORKCONFIG']._serialized_end=2507 _globals['_CONFIG_NETWORKCONFIG_IPV4CONFIG']._serialized_start=2400 _globals['_CONFIG_NETWORKCONFIG_IPV4CONFIG']._serialized_end=2470 _globals['_CONFIG_NETWORKCONFIG_ADDRESSMODE']._serialized_start=2472 _globals['_CONFIG_NETWORKCONFIG_ADDRESSMODE']._serialized_end=2507 _globals['_CONFIG_DISPLAYCONFIG']._serialized_start=2510 _globals['_CONFIG_DISPLAYCONFIG']._serialized_end=3528 _globals['_CONFIG_DISPLAYCONFIG_GPSCOORDINATEFORMAT']._serialized_start=3074 _globals['_CONFIG_DISPLAYCONFIG_GPSCOORDINATEFORMAT']._serialized_end=3151 _globals['_CONFIG_DISPLAYCONFIG_DISPLAYUNITS']._serialized_start=3153 _globals['_CONFIG_DISPLAYCONFIG_DISPLAYUNITS']._serialized_end=3193 _globals['_CONFIG_DISPLAYCONFIG_OLEDTYPE']._serialized_start=3195 _globals['_CONFIG_DISPLAYCONFIG_OLEDTYPE']._serialized_end=3272 _globals['_CONFIG_DISPLAYCONFIG_DISPLAYMODE']._serialized_start=3274 _globals['_CONFIG_DISPLAYCONFIG_DISPLAYMODE']._serialized_end=3339 _globals['_CONFIG_DISPLAYCONFIG_COMPASSORIENTATION']._serialized_start=3342 _globals['_CONFIG_DISPLAYCONFIG_COMPASSORIENTATION']._serialized_end=3528 _globals['_CONFIG_LORACONFIG']._serialized_start=3531 _globals['_CONFIG_LORACONFIG']._serialized_end=4365 _globals['_CONFIG_LORACONFIG_REGIONCODE']._serialized_start=4009 _globals['_CONFIG_LORACONFIG_REGIONCODE']._serialized_end=4214 _globals['_CONFIG_LORACONFIG_MODEMPRESET']._serialized_start=4217 _globals['_CONFIG_LORACONFIG_MODEMPRESET']._serialized_end=4365 _globals['_CONFIG_BLUETOOTHCONFIG']._serialized_start=4368 _globals['_CONFIG_BLUETOOTHCONFIG']._serialized_end=4582 _globals['_CONFIG_BLUETOOTHCONFIG_PAIRINGMODE']._serialized_start=4526 _globals['_CONFIG_BLUETOOTHCONFIG_PAIRINGMODE']._serialized_end=4582 # @@protoc_insertion_point(module_scope) python-2.3.14/meshtastic/protobuf/config_pb2.pyi000066400000000000000000002113661464266072200217030ustar00rootroot00000000000000""" @generated by mypy-protobuf. Do not edit manually! isort:skip_file """ import builtins import collections.abc import google.protobuf.descriptor import google.protobuf.internal.containers import google.protobuf.internal.enum_type_wrapper import google.protobuf.message import sys import typing if sys.version_info >= (3, 10): import typing as typing_extensions else: import typing_extensions DESCRIPTOR: google.protobuf.descriptor.FileDescriptor @typing.final class Config(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @typing.final class DeviceConfig(google.protobuf.message.Message): """ Configuration """ DESCRIPTOR: google.protobuf.descriptor.Descriptor class _Role: ValueType = typing.NewType("ValueType", builtins.int) V: typing_extensions.TypeAlias = ValueType class _RoleEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[Config.DeviceConfig._Role.ValueType], builtins.type): DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor CLIENT: Config.DeviceConfig._Role.ValueType # 0 """ Description: App connected or stand alone messaging device. Technical Details: Default Role """ CLIENT_MUTE: Config.DeviceConfig._Role.ValueType # 1 """ Description: Device that does not forward packets from other devices. """ ROUTER: Config.DeviceConfig._Role.ValueType # 2 """ Description: Infrastructure node for extending network coverage by relaying messages. Visible in Nodes list. Technical Details: Mesh packets will prefer to be routed over this node. This node will not be used by client apps. The wifi radio and the oled screen will be put to sleep. This mode may still potentially have higher power usage due to it's preference in message rebroadcasting on the mesh. """ ROUTER_CLIENT: Config.DeviceConfig._Role.ValueType # 3 """ Description: Combination of both ROUTER and CLIENT. Not for mobile devices. """ REPEATER: Config.DeviceConfig._Role.ValueType # 4 """ Description: Infrastructure node for extending network coverage by relaying messages with minimal overhead. Not visible in Nodes list. Technical Details: Mesh packets will simply be rebroadcasted over this node. Nodes configured with this role will not originate NodeInfo, Position, Telemetry or any other packet type. They will simply rebroadcast any mesh packets on the same frequency, channel num, spread factor, and coding rate. """ TRACKER: Config.DeviceConfig._Role.ValueType # 5 """ Description: Broadcasts GPS position packets as priority. Technical Details: Position Mesh packets will be prioritized higher and sent more frequently by default. When used in conjunction with power.is_power_saving = true, nodes will wake up, send position, and then sleep for position.position_broadcast_secs seconds. """ SENSOR: Config.DeviceConfig._Role.ValueType # 6 """ Description: Broadcasts telemetry packets as priority. Technical Details: Telemetry Mesh packets will be prioritized higher and sent more frequently by default. When used in conjunction with power.is_power_saving = true, nodes will wake up, send environment telemetry, and then sleep for telemetry.environment_update_interval seconds. """ TAK: Config.DeviceConfig._Role.ValueType # 7 """ Description: Optimized for ATAK system communication and reduces routine broadcasts. Technical Details: Used for nodes dedicated for connection to an ATAK EUD. Turns off many of the routine broadcasts to favor CoT packet stream from the Meshtastic ATAK plugin -> IMeshService -> Node """ CLIENT_HIDDEN: Config.DeviceConfig._Role.ValueType # 8 """ Description: Device that only broadcasts as needed for stealth or power savings. Technical Details: Used for nodes that "only speak when spoken to" Turns all of the routine broadcasts but allows for ad-hoc communication Still rebroadcasts, but with local only rebroadcast mode (known meshes only) Can be used for clandestine operation or to dramatically reduce airtime / power consumption """ LOST_AND_FOUND: Config.DeviceConfig._Role.ValueType # 9 """ Description: Broadcasts location as message to default channel regularly for to assist with device recovery. Technical Details: Used to automatically send a text message to the mesh with the current position of the device on a frequent interval: "I'm lost! Position: lat / long" """ TAK_TRACKER: Config.DeviceConfig._Role.ValueType # 10 """ Description: Enables automatic TAK PLI broadcasts and reduces routine broadcasts. Technical Details: Turns off many of the routine broadcasts to favor ATAK CoT packet stream and automatic TAK PLI (position location information) broadcasts. Uses position module configuration to determine TAK PLI broadcast interval. """ class Role(_Role, metaclass=_RoleEnumTypeWrapper): """ Defines the device's role on the Mesh network """ CLIENT: Config.DeviceConfig.Role.ValueType # 0 """ Description: App connected or stand alone messaging device. Technical Details: Default Role """ CLIENT_MUTE: Config.DeviceConfig.Role.ValueType # 1 """ Description: Device that does not forward packets from other devices. """ ROUTER: Config.DeviceConfig.Role.ValueType # 2 """ Description: Infrastructure node for extending network coverage by relaying messages. Visible in Nodes list. Technical Details: Mesh packets will prefer to be routed over this node. This node will not be used by client apps. The wifi radio and the oled screen will be put to sleep. This mode may still potentially have higher power usage due to it's preference in message rebroadcasting on the mesh. """ ROUTER_CLIENT: Config.DeviceConfig.Role.ValueType # 3 """ Description: Combination of both ROUTER and CLIENT. Not for mobile devices. """ REPEATER: Config.DeviceConfig.Role.ValueType # 4 """ Description: Infrastructure node for extending network coverage by relaying messages with minimal overhead. Not visible in Nodes list. Technical Details: Mesh packets will simply be rebroadcasted over this node. Nodes configured with this role will not originate NodeInfo, Position, Telemetry or any other packet type. They will simply rebroadcast any mesh packets on the same frequency, channel num, spread factor, and coding rate. """ TRACKER: Config.DeviceConfig.Role.ValueType # 5 """ Description: Broadcasts GPS position packets as priority. Technical Details: Position Mesh packets will be prioritized higher and sent more frequently by default. When used in conjunction with power.is_power_saving = true, nodes will wake up, send position, and then sleep for position.position_broadcast_secs seconds. """ SENSOR: Config.DeviceConfig.Role.ValueType # 6 """ Description: Broadcasts telemetry packets as priority. Technical Details: Telemetry Mesh packets will be prioritized higher and sent more frequently by default. When used in conjunction with power.is_power_saving = true, nodes will wake up, send environment telemetry, and then sleep for telemetry.environment_update_interval seconds. """ TAK: Config.DeviceConfig.Role.ValueType # 7 """ Description: Optimized for ATAK system communication and reduces routine broadcasts. Technical Details: Used for nodes dedicated for connection to an ATAK EUD. Turns off many of the routine broadcasts to favor CoT packet stream from the Meshtastic ATAK plugin -> IMeshService -> Node """ CLIENT_HIDDEN: Config.DeviceConfig.Role.ValueType # 8 """ Description: Device that only broadcasts as needed for stealth or power savings. Technical Details: Used for nodes that "only speak when spoken to" Turns all of the routine broadcasts but allows for ad-hoc communication Still rebroadcasts, but with local only rebroadcast mode (known meshes only) Can be used for clandestine operation or to dramatically reduce airtime / power consumption """ LOST_AND_FOUND: Config.DeviceConfig.Role.ValueType # 9 """ Description: Broadcasts location as message to default channel regularly for to assist with device recovery. Technical Details: Used to automatically send a text message to the mesh with the current position of the device on a frequent interval: "I'm lost! Position: lat / long" """ TAK_TRACKER: Config.DeviceConfig.Role.ValueType # 10 """ Description: Enables automatic TAK PLI broadcasts and reduces routine broadcasts. Technical Details: Turns off many of the routine broadcasts to favor ATAK CoT packet stream and automatic TAK PLI (position location information) broadcasts. Uses position module configuration to determine TAK PLI broadcast interval. """ class _RebroadcastMode: ValueType = typing.NewType("ValueType", builtins.int) V: typing_extensions.TypeAlias = ValueType class _RebroadcastModeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[Config.DeviceConfig._RebroadcastMode.ValueType], builtins.type): DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor ALL: Config.DeviceConfig._RebroadcastMode.ValueType # 0 """ Default behavior. Rebroadcast any observed message, if it was on our private channel or from another mesh with the same lora params. """ ALL_SKIP_DECODING: Config.DeviceConfig._RebroadcastMode.ValueType # 1 """ Same as behavior as ALL but skips packet decoding and simply rebroadcasts them. Only available in Repeater role. Setting this on any other roles will result in ALL behavior. """ LOCAL_ONLY: Config.DeviceConfig._RebroadcastMode.ValueType # 2 """ Ignores observed messages from foreign meshes that are open or those which it cannot decrypt. Only rebroadcasts message on the nodes local primary / secondary channels. """ KNOWN_ONLY: Config.DeviceConfig._RebroadcastMode.ValueType # 3 """ Ignores observed messages from foreign meshes like LOCAL_ONLY, but takes it step further by also ignoring messages from nodenums not in the node's known list (NodeDB) """ class RebroadcastMode(_RebroadcastMode, metaclass=_RebroadcastModeEnumTypeWrapper): """ Defines the device's behavior for how messages are rebroadcast """ ALL: Config.DeviceConfig.RebroadcastMode.ValueType # 0 """ Default behavior. Rebroadcast any observed message, if it was on our private channel or from another mesh with the same lora params. """ ALL_SKIP_DECODING: Config.DeviceConfig.RebroadcastMode.ValueType # 1 """ Same as behavior as ALL but skips packet decoding and simply rebroadcasts them. Only available in Repeater role. Setting this on any other roles will result in ALL behavior. """ LOCAL_ONLY: Config.DeviceConfig.RebroadcastMode.ValueType # 2 """ Ignores observed messages from foreign meshes that are open or those which it cannot decrypt. Only rebroadcasts message on the nodes local primary / secondary channels. """ KNOWN_ONLY: Config.DeviceConfig.RebroadcastMode.ValueType # 3 """ Ignores observed messages from foreign meshes like LOCAL_ONLY, but takes it step further by also ignoring messages from nodenums not in the node's known list (NodeDB) """ ROLE_FIELD_NUMBER: builtins.int SERIAL_ENABLED_FIELD_NUMBER: builtins.int DEBUG_LOG_ENABLED_FIELD_NUMBER: builtins.int BUTTON_GPIO_FIELD_NUMBER: builtins.int BUZZER_GPIO_FIELD_NUMBER: builtins.int REBROADCAST_MODE_FIELD_NUMBER: builtins.int NODE_INFO_BROADCAST_SECS_FIELD_NUMBER: builtins.int DOUBLE_TAP_AS_BUTTON_PRESS_FIELD_NUMBER: builtins.int IS_MANAGED_FIELD_NUMBER: builtins.int DISABLE_TRIPLE_CLICK_FIELD_NUMBER: builtins.int TZDEF_FIELD_NUMBER: builtins.int LED_HEARTBEAT_DISABLED_FIELD_NUMBER: builtins.int role: global___Config.DeviceConfig.Role.ValueType """ Sets the role of node """ serial_enabled: builtins.bool """ Disabling this will disable the SerialConsole by not initilizing the StreamAPI """ debug_log_enabled: builtins.bool """ By default we turn off logging as soon as an API client connects (to keep shared serial link quiet). Set this to true to leave the debug log outputting even when API is active. """ button_gpio: builtins.int """ For boards without a hard wired button, this is the pin number that will be used Boards that have more than one button can swap the function with this one. defaults to BUTTON_PIN if defined. """ buzzer_gpio: builtins.int """ For boards without a PWM buzzer, this is the pin number that will be used Defaults to PIN_BUZZER if defined. """ rebroadcast_mode: global___Config.DeviceConfig.RebroadcastMode.ValueType """ Sets the role of node """ node_info_broadcast_secs: builtins.int """ Send our nodeinfo this often Defaults to 900 Seconds (15 minutes) """ double_tap_as_button_press: builtins.bool """ Treat double tap interrupt on supported accelerometers as a button press if set to true """ is_managed: builtins.bool """ If true, device is considered to be "managed" by a mesh administrator Clients should then limit available configuration and administrative options inside the user interface """ disable_triple_click: builtins.bool """ Disables the triple-press of user button to enable or disable GPS """ tzdef: builtins.str """ POSIX Timezone definition string from https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv. """ led_heartbeat_disabled: builtins.bool """ If true, disable the default blinking LED (LED_PIN) behavior on the device """ def __init__( self, *, role: global___Config.DeviceConfig.Role.ValueType = ..., serial_enabled: builtins.bool = ..., debug_log_enabled: builtins.bool = ..., button_gpio: builtins.int = ..., buzzer_gpio: builtins.int = ..., rebroadcast_mode: global___Config.DeviceConfig.RebroadcastMode.ValueType = ..., node_info_broadcast_secs: builtins.int = ..., double_tap_as_button_press: builtins.bool = ..., is_managed: builtins.bool = ..., disable_triple_click: builtins.bool = ..., tzdef: builtins.str = ..., led_heartbeat_disabled: builtins.bool = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["button_gpio", b"button_gpio", "buzzer_gpio", b"buzzer_gpio", "debug_log_enabled", b"debug_log_enabled", "disable_triple_click", b"disable_triple_click", "double_tap_as_button_press", b"double_tap_as_button_press", "is_managed", b"is_managed", "led_heartbeat_disabled", b"led_heartbeat_disabled", "node_info_broadcast_secs", b"node_info_broadcast_secs", "rebroadcast_mode", b"rebroadcast_mode", "role", b"role", "serial_enabled", b"serial_enabled", "tzdef", b"tzdef"]) -> None: ... @typing.final class PositionConfig(google.protobuf.message.Message): """ Position Config """ DESCRIPTOR: google.protobuf.descriptor.Descriptor class _PositionFlags: ValueType = typing.NewType("ValueType", builtins.int) V: typing_extensions.TypeAlias = ValueType class _PositionFlagsEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[Config.PositionConfig._PositionFlags.ValueType], builtins.type): DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor UNSET: Config.PositionConfig._PositionFlags.ValueType # 0 """ Required for compilation """ ALTITUDE: Config.PositionConfig._PositionFlags.ValueType # 1 """ Include an altitude value (if available) """ ALTITUDE_MSL: Config.PositionConfig._PositionFlags.ValueType # 2 """ Altitude value is MSL """ GEOIDAL_SEPARATION: Config.PositionConfig._PositionFlags.ValueType # 4 """ Include geoidal separation """ DOP: Config.PositionConfig._PositionFlags.ValueType # 8 """ Include the DOP value ; PDOP used by default, see below """ HVDOP: Config.PositionConfig._PositionFlags.ValueType # 16 """ If POS_DOP set, send separate HDOP / VDOP values instead of PDOP """ SATINVIEW: Config.PositionConfig._PositionFlags.ValueType # 32 """ Include number of "satellites in view" """ SEQ_NO: Config.PositionConfig._PositionFlags.ValueType # 64 """ Include a sequence number incremented per packet """ TIMESTAMP: Config.PositionConfig._PositionFlags.ValueType # 128 """ Include positional timestamp (from GPS solution) """ HEADING: Config.PositionConfig._PositionFlags.ValueType # 256 """ Include positional heading Intended for use with vehicle not walking speeds walking speeds are likely to be error prone like the compass """ SPEED: Config.PositionConfig._PositionFlags.ValueType # 512 """ Include positional speed Intended for use with vehicle not walking speeds walking speeds are likely to be error prone like the compass """ class PositionFlags(_PositionFlags, metaclass=_PositionFlagsEnumTypeWrapper): """ Bit field of boolean configuration options, indicating which optional fields to include when assembling POSITION messages. Longitude, latitude, altitude, speed, heading, and DOP are always included (also time if GPS-synced) NOTE: the more fields are included, the larger the message will be - leading to longer airtime and a higher risk of packet loss """ UNSET: Config.PositionConfig.PositionFlags.ValueType # 0 """ Required for compilation """ ALTITUDE: Config.PositionConfig.PositionFlags.ValueType # 1 """ Include an altitude value (if available) """ ALTITUDE_MSL: Config.PositionConfig.PositionFlags.ValueType # 2 """ Altitude value is MSL """ GEOIDAL_SEPARATION: Config.PositionConfig.PositionFlags.ValueType # 4 """ Include geoidal separation """ DOP: Config.PositionConfig.PositionFlags.ValueType # 8 """ Include the DOP value ; PDOP used by default, see below """ HVDOP: Config.PositionConfig.PositionFlags.ValueType # 16 """ If POS_DOP set, send separate HDOP / VDOP values instead of PDOP """ SATINVIEW: Config.PositionConfig.PositionFlags.ValueType # 32 """ Include number of "satellites in view" """ SEQ_NO: Config.PositionConfig.PositionFlags.ValueType # 64 """ Include a sequence number incremented per packet """ TIMESTAMP: Config.PositionConfig.PositionFlags.ValueType # 128 """ Include positional timestamp (from GPS solution) """ HEADING: Config.PositionConfig.PositionFlags.ValueType # 256 """ Include positional heading Intended for use with vehicle not walking speeds walking speeds are likely to be error prone like the compass """ SPEED: Config.PositionConfig.PositionFlags.ValueType # 512 """ Include positional speed Intended for use with vehicle not walking speeds walking speeds are likely to be error prone like the compass """ class _GpsMode: ValueType = typing.NewType("ValueType", builtins.int) V: typing_extensions.TypeAlias = ValueType class _GpsModeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[Config.PositionConfig._GpsMode.ValueType], builtins.type): DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor DISABLED: Config.PositionConfig._GpsMode.ValueType # 0 """ GPS is present but disabled """ ENABLED: Config.PositionConfig._GpsMode.ValueType # 1 """ GPS is present and enabled """ NOT_PRESENT: Config.PositionConfig._GpsMode.ValueType # 2 """ GPS is not present on the device """ class GpsMode(_GpsMode, metaclass=_GpsModeEnumTypeWrapper): ... DISABLED: Config.PositionConfig.GpsMode.ValueType # 0 """ GPS is present but disabled """ ENABLED: Config.PositionConfig.GpsMode.ValueType # 1 """ GPS is present and enabled """ NOT_PRESENT: Config.PositionConfig.GpsMode.ValueType # 2 """ GPS is not present on the device """ POSITION_BROADCAST_SECS_FIELD_NUMBER: builtins.int POSITION_BROADCAST_SMART_ENABLED_FIELD_NUMBER: builtins.int FIXED_POSITION_FIELD_NUMBER: builtins.int GPS_ENABLED_FIELD_NUMBER: builtins.int GPS_UPDATE_INTERVAL_FIELD_NUMBER: builtins.int GPS_ATTEMPT_TIME_FIELD_NUMBER: builtins.int POSITION_FLAGS_FIELD_NUMBER: builtins.int RX_GPIO_FIELD_NUMBER: builtins.int TX_GPIO_FIELD_NUMBER: builtins.int BROADCAST_SMART_MINIMUM_DISTANCE_FIELD_NUMBER: builtins.int BROADCAST_SMART_MINIMUM_INTERVAL_SECS_FIELD_NUMBER: builtins.int GPS_EN_GPIO_FIELD_NUMBER: builtins.int GPS_MODE_FIELD_NUMBER: builtins.int position_broadcast_secs: builtins.int """ We should send our position this often (but only if it has changed significantly) Defaults to 15 minutes """ position_broadcast_smart_enabled: builtins.bool """ Adaptive position braoadcast, which is now the default. """ fixed_position: builtins.bool """ If set, this node is at a fixed position. We will generate GPS position updates at the regular interval, but use whatever the last lat/lon/alt we have for the node. The lat/lon/alt can be set by an internal GPS or with the help of the app. """ gps_enabled: builtins.bool """ Is GPS enabled for this node? """ gps_update_interval: builtins.int """ How often should we try to get GPS position (in seconds) or zero for the default of once every 30 seconds or a very large value (maxint) to update only once at boot. """ gps_attempt_time: builtins.int """ Deprecated in favor of using smart / regular broadcast intervals as implicit attempt time """ position_flags: builtins.int """ Bit field of boolean configuration options for POSITION messages (bitwise OR of PositionFlags) """ rx_gpio: builtins.int """ (Re)define GPS_RX_PIN for your board. """ tx_gpio: builtins.int """ (Re)define GPS_TX_PIN for your board. """ broadcast_smart_minimum_distance: builtins.int """ The minimum distance in meters traveled (since the last send) before we can send a position to the mesh if position_broadcast_smart_enabled """ broadcast_smart_minimum_interval_secs: builtins.int """ The minimum number of seconds (since the last send) before we can send a position to the mesh if position_broadcast_smart_enabled """ gps_en_gpio: builtins.int """ (Re)define PIN_GPS_EN for your board. """ gps_mode: global___Config.PositionConfig.GpsMode.ValueType """ Set where GPS is enabled, disabled, or not present """ def __init__( self, *, position_broadcast_secs: builtins.int = ..., position_broadcast_smart_enabled: builtins.bool = ..., fixed_position: builtins.bool = ..., gps_enabled: builtins.bool = ..., gps_update_interval: builtins.int = ..., gps_attempt_time: builtins.int = ..., position_flags: builtins.int = ..., rx_gpio: builtins.int = ..., tx_gpio: builtins.int = ..., broadcast_smart_minimum_distance: builtins.int = ..., broadcast_smart_minimum_interval_secs: builtins.int = ..., gps_en_gpio: builtins.int = ..., gps_mode: global___Config.PositionConfig.GpsMode.ValueType = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["broadcast_smart_minimum_distance", b"broadcast_smart_minimum_distance", "broadcast_smart_minimum_interval_secs", b"broadcast_smart_minimum_interval_secs", "fixed_position", b"fixed_position", "gps_attempt_time", b"gps_attempt_time", "gps_en_gpio", b"gps_en_gpio", "gps_enabled", b"gps_enabled", "gps_mode", b"gps_mode", "gps_update_interval", b"gps_update_interval", "position_broadcast_secs", b"position_broadcast_secs", "position_broadcast_smart_enabled", b"position_broadcast_smart_enabled", "position_flags", b"position_flags", "rx_gpio", b"rx_gpio", "tx_gpio", b"tx_gpio"]) -> None: ... @typing.final class PowerConfig(google.protobuf.message.Message): """ Power Config\\ See [Power Config](/docs/settings/config/power) for additional power config details. """ DESCRIPTOR: google.protobuf.descriptor.Descriptor IS_POWER_SAVING_FIELD_NUMBER: builtins.int ON_BATTERY_SHUTDOWN_AFTER_SECS_FIELD_NUMBER: builtins.int ADC_MULTIPLIER_OVERRIDE_FIELD_NUMBER: builtins.int WAIT_BLUETOOTH_SECS_FIELD_NUMBER: builtins.int SDS_SECS_FIELD_NUMBER: builtins.int LS_SECS_FIELD_NUMBER: builtins.int MIN_WAKE_SECS_FIELD_NUMBER: builtins.int DEVICE_BATTERY_INA_ADDRESS_FIELD_NUMBER: builtins.int POWERMON_ENABLES_FIELD_NUMBER: builtins.int is_power_saving: builtins.bool """ Description: Will sleep everything as much as possible, for the tracker and sensor role this will also include the lora radio. Don't use this setting if you want to use your device with the phone apps or are using a device without a user button. Technical Details: Works for ESP32 devices and NRF52 devices in the Sensor or Tracker roles """ on_battery_shutdown_after_secs: builtins.int """ Description: If non-zero, the device will fully power off this many seconds after external power is removed. """ adc_multiplier_override: builtins.float """ Ratio of voltage divider for battery pin eg. 3.20 (R1=100k, R2=220k) Overrides the ADC_MULTIPLIER defined in variant for battery voltage calculation. https://meshtastic.org/docs/configuration/radio/power/#adc-multiplier-override Should be set to floating point value between 2 and 6 """ wait_bluetooth_secs: builtins.int """ Description: The number of seconds for to wait before turning off BLE in No Bluetooth states Technical Details: ESP32 Only 0 for default of 1 minute """ sds_secs: builtins.int """ Super Deep Sleep Seconds While in Light Sleep if mesh_sds_timeout_secs is exceeded we will lower into super deep sleep for this value (default 1 year) or a button press 0 for default of one year """ ls_secs: builtins.int """ Description: In light sleep the CPU is suspended, LoRa radio is on, BLE is off an GPS is on Technical Details: ESP32 Only 0 for default of 300 """ min_wake_secs: builtins.int """ Description: While in light sleep when we receive packets on the LoRa radio we will wake and handle them and stay awake in no BLE mode for this value Technical Details: ESP32 Only 0 for default of 10 seconds """ device_battery_ina_address: builtins.int """ I2C address of INA_2XX to use for reading device battery voltage """ powermon_enables: builtins.int """ If non-zero, we want powermon log outputs. With the particular (bitfield) sources enabled. Note: we picked an ID of 32 so that lower more efficient IDs can be used for more frequently used options. """ def __init__( self, *, is_power_saving: builtins.bool = ..., on_battery_shutdown_after_secs: builtins.int = ..., adc_multiplier_override: builtins.float = ..., wait_bluetooth_secs: builtins.int = ..., sds_secs: builtins.int = ..., ls_secs: builtins.int = ..., min_wake_secs: builtins.int = ..., device_battery_ina_address: builtins.int = ..., powermon_enables: builtins.int = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["adc_multiplier_override", b"adc_multiplier_override", "device_battery_ina_address", b"device_battery_ina_address", "is_power_saving", b"is_power_saving", "ls_secs", b"ls_secs", "min_wake_secs", b"min_wake_secs", "on_battery_shutdown_after_secs", b"on_battery_shutdown_after_secs", "powermon_enables", b"powermon_enables", "sds_secs", b"sds_secs", "wait_bluetooth_secs", b"wait_bluetooth_secs"]) -> None: ... @typing.final class NetworkConfig(google.protobuf.message.Message): """ Network Config """ DESCRIPTOR: google.protobuf.descriptor.Descriptor class _AddressMode: ValueType = typing.NewType("ValueType", builtins.int) V: typing_extensions.TypeAlias = ValueType class _AddressModeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[Config.NetworkConfig._AddressMode.ValueType], builtins.type): DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor DHCP: Config.NetworkConfig._AddressMode.ValueType # 0 """ obtain ip address via DHCP """ STATIC: Config.NetworkConfig._AddressMode.ValueType # 1 """ use static ip address """ class AddressMode(_AddressMode, metaclass=_AddressModeEnumTypeWrapper): ... DHCP: Config.NetworkConfig.AddressMode.ValueType # 0 """ obtain ip address via DHCP """ STATIC: Config.NetworkConfig.AddressMode.ValueType # 1 """ use static ip address """ @typing.final class IpV4Config(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor IP_FIELD_NUMBER: builtins.int GATEWAY_FIELD_NUMBER: builtins.int SUBNET_FIELD_NUMBER: builtins.int DNS_FIELD_NUMBER: builtins.int ip: builtins.int """ Static IP address """ gateway: builtins.int """ Static gateway address """ subnet: builtins.int """ Static subnet mask """ dns: builtins.int """ Static DNS server address """ def __init__( self, *, ip: builtins.int = ..., gateway: builtins.int = ..., subnet: builtins.int = ..., dns: builtins.int = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["dns", b"dns", "gateway", b"gateway", "ip", b"ip", "subnet", b"subnet"]) -> None: ... WIFI_ENABLED_FIELD_NUMBER: builtins.int WIFI_SSID_FIELD_NUMBER: builtins.int WIFI_PSK_FIELD_NUMBER: builtins.int NTP_SERVER_FIELD_NUMBER: builtins.int ETH_ENABLED_FIELD_NUMBER: builtins.int ADDRESS_MODE_FIELD_NUMBER: builtins.int IPV4_CONFIG_FIELD_NUMBER: builtins.int RSYSLOG_SERVER_FIELD_NUMBER: builtins.int wifi_enabled: builtins.bool """ Enable WiFi (disables Bluetooth) """ wifi_ssid: builtins.str """ If set, this node will try to join the specified wifi network and acquire an address via DHCP """ wifi_psk: builtins.str """ If set, will be use to authenticate to the named wifi """ ntp_server: builtins.str """ NTP server to use if WiFi is conneced, defaults to `0.pool.ntp.org` """ eth_enabled: builtins.bool """ Enable Ethernet """ address_mode: global___Config.NetworkConfig.AddressMode.ValueType """ acquire an address via DHCP or assign static """ rsyslog_server: builtins.str """ rsyslog Server and Port """ @property def ipv4_config(self) -> global___Config.NetworkConfig.IpV4Config: """ struct to keep static address """ def __init__( self, *, wifi_enabled: builtins.bool = ..., wifi_ssid: builtins.str = ..., wifi_psk: builtins.str = ..., ntp_server: builtins.str = ..., eth_enabled: builtins.bool = ..., address_mode: global___Config.NetworkConfig.AddressMode.ValueType = ..., ipv4_config: global___Config.NetworkConfig.IpV4Config | None = ..., rsyslog_server: builtins.str = ..., ) -> None: ... def HasField(self, field_name: typing.Literal["ipv4_config", b"ipv4_config"]) -> builtins.bool: ... def ClearField(self, field_name: typing.Literal["address_mode", b"address_mode", "eth_enabled", b"eth_enabled", "ipv4_config", b"ipv4_config", "ntp_server", b"ntp_server", "rsyslog_server", b"rsyslog_server", "wifi_enabled", b"wifi_enabled", "wifi_psk", b"wifi_psk", "wifi_ssid", b"wifi_ssid"]) -> None: ... @typing.final class DisplayConfig(google.protobuf.message.Message): """ Display Config """ DESCRIPTOR: google.protobuf.descriptor.Descriptor class _GpsCoordinateFormat: ValueType = typing.NewType("ValueType", builtins.int) V: typing_extensions.TypeAlias = ValueType class _GpsCoordinateFormatEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[Config.DisplayConfig._GpsCoordinateFormat.ValueType], builtins.type): DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor DEC: Config.DisplayConfig._GpsCoordinateFormat.ValueType # 0 """ GPS coordinates are displayed in the normal decimal degrees format: DD.DDDDDD DDD.DDDDDD """ DMS: Config.DisplayConfig._GpsCoordinateFormat.ValueType # 1 """ GPS coordinates are displayed in the degrees minutes seconds format: DD°MM'SS"C DDD°MM'SS"C, where C is the compass point representing the locations quadrant """ UTM: Config.DisplayConfig._GpsCoordinateFormat.ValueType # 2 """ Universal Transverse Mercator format: ZZB EEEEEE NNNNNNN, where Z is zone, B is band, E is easting, N is northing """ MGRS: Config.DisplayConfig._GpsCoordinateFormat.ValueType # 3 """ Military Grid Reference System format: ZZB CD EEEEE NNNNN, where Z is zone, B is band, C is the east 100k square, D is the north 100k square, E is easting, N is northing """ OLC: Config.DisplayConfig._GpsCoordinateFormat.ValueType # 4 """ Open Location Code (aka Plus Codes). """ OSGR: Config.DisplayConfig._GpsCoordinateFormat.ValueType # 5 """ Ordnance Survey Grid Reference (the National Grid System of the UK). Format: AB EEEEE NNNNN, where A is the east 100k square, B is the north 100k square, E is the easting, N is the northing """ class GpsCoordinateFormat(_GpsCoordinateFormat, metaclass=_GpsCoordinateFormatEnumTypeWrapper): """ How the GPS coordinates are displayed on the OLED screen. """ DEC: Config.DisplayConfig.GpsCoordinateFormat.ValueType # 0 """ GPS coordinates are displayed in the normal decimal degrees format: DD.DDDDDD DDD.DDDDDD """ DMS: Config.DisplayConfig.GpsCoordinateFormat.ValueType # 1 """ GPS coordinates are displayed in the degrees minutes seconds format: DD°MM'SS"C DDD°MM'SS"C, where C is the compass point representing the locations quadrant """ UTM: Config.DisplayConfig.GpsCoordinateFormat.ValueType # 2 """ Universal Transverse Mercator format: ZZB EEEEEE NNNNNNN, where Z is zone, B is band, E is easting, N is northing """ MGRS: Config.DisplayConfig.GpsCoordinateFormat.ValueType # 3 """ Military Grid Reference System format: ZZB CD EEEEE NNNNN, where Z is zone, B is band, C is the east 100k square, D is the north 100k square, E is easting, N is northing """ OLC: Config.DisplayConfig.GpsCoordinateFormat.ValueType # 4 """ Open Location Code (aka Plus Codes). """ OSGR: Config.DisplayConfig.GpsCoordinateFormat.ValueType # 5 """ Ordnance Survey Grid Reference (the National Grid System of the UK). Format: AB EEEEE NNNNN, where A is the east 100k square, B is the north 100k square, E is the easting, N is the northing """ class _DisplayUnits: ValueType = typing.NewType("ValueType", builtins.int) V: typing_extensions.TypeAlias = ValueType class _DisplayUnitsEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[Config.DisplayConfig._DisplayUnits.ValueType], builtins.type): DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor METRIC: Config.DisplayConfig._DisplayUnits.ValueType # 0 """ Metric (Default) """ IMPERIAL: Config.DisplayConfig._DisplayUnits.ValueType # 1 """ Imperial """ class DisplayUnits(_DisplayUnits, metaclass=_DisplayUnitsEnumTypeWrapper): """ Unit display preference """ METRIC: Config.DisplayConfig.DisplayUnits.ValueType # 0 """ Metric (Default) """ IMPERIAL: Config.DisplayConfig.DisplayUnits.ValueType # 1 """ Imperial """ class _OledType: ValueType = typing.NewType("ValueType", builtins.int) V: typing_extensions.TypeAlias = ValueType class _OledTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[Config.DisplayConfig._OledType.ValueType], builtins.type): DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor OLED_AUTO: Config.DisplayConfig._OledType.ValueType # 0 """ Default / Auto """ OLED_SSD1306: Config.DisplayConfig._OledType.ValueType # 1 """ Default / Auto """ OLED_SH1106: Config.DisplayConfig._OledType.ValueType # 2 """ Default / Auto """ OLED_SH1107: Config.DisplayConfig._OledType.ValueType # 3 """ Can not be auto detected but set by proto. Used for 128x128 screens """ class OledType(_OledType, metaclass=_OledTypeEnumTypeWrapper): """ Override OLED outo detect with this if it fails. """ OLED_AUTO: Config.DisplayConfig.OledType.ValueType # 0 """ Default / Auto """ OLED_SSD1306: Config.DisplayConfig.OledType.ValueType # 1 """ Default / Auto """ OLED_SH1106: Config.DisplayConfig.OledType.ValueType # 2 """ Default / Auto """ OLED_SH1107: Config.DisplayConfig.OledType.ValueType # 3 """ Can not be auto detected but set by proto. Used for 128x128 screens """ class _DisplayMode: ValueType = typing.NewType("ValueType", builtins.int) V: typing_extensions.TypeAlias = ValueType class _DisplayModeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[Config.DisplayConfig._DisplayMode.ValueType], builtins.type): DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor DEFAULT: Config.DisplayConfig._DisplayMode.ValueType # 0 """ Default. The old style for the 128x64 OLED screen """ TWOCOLOR: Config.DisplayConfig._DisplayMode.ValueType # 1 """ Rearrange display elements to cater for bicolor OLED displays """ INVERTED: Config.DisplayConfig._DisplayMode.ValueType # 2 """ Same as TwoColor, but with inverted top bar. Not so good for Epaper displays """ COLOR: Config.DisplayConfig._DisplayMode.ValueType # 3 """ TFT Full Color Displays (not implemented yet) """ class DisplayMode(_DisplayMode, metaclass=_DisplayModeEnumTypeWrapper): ... DEFAULT: Config.DisplayConfig.DisplayMode.ValueType # 0 """ Default. The old style for the 128x64 OLED screen """ TWOCOLOR: Config.DisplayConfig.DisplayMode.ValueType # 1 """ Rearrange display elements to cater for bicolor OLED displays """ INVERTED: Config.DisplayConfig.DisplayMode.ValueType # 2 """ Same as TwoColor, but with inverted top bar. Not so good for Epaper displays """ COLOR: Config.DisplayConfig.DisplayMode.ValueType # 3 """ TFT Full Color Displays (not implemented yet) """ class _CompassOrientation: ValueType = typing.NewType("ValueType", builtins.int) V: typing_extensions.TypeAlias = ValueType class _CompassOrientationEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[Config.DisplayConfig._CompassOrientation.ValueType], builtins.type): DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor DEGREES_0: Config.DisplayConfig._CompassOrientation.ValueType # 0 """ The compass and the display are in the same orientation. """ DEGREES_90: Config.DisplayConfig._CompassOrientation.ValueType # 1 """ Rotate the compass by 90 degrees. """ DEGREES_180: Config.DisplayConfig._CompassOrientation.ValueType # 2 """ Rotate the compass by 180 degrees. """ DEGREES_270: Config.DisplayConfig._CompassOrientation.ValueType # 3 """ Rotate the compass by 270 degrees. """ DEGREES_0_INVERTED: Config.DisplayConfig._CompassOrientation.ValueType # 4 """ Don't rotate the compass, but invert the result. """ DEGREES_90_INVERTED: Config.DisplayConfig._CompassOrientation.ValueType # 5 """ Rotate the compass by 90 degrees and invert. """ DEGREES_180_INVERTED: Config.DisplayConfig._CompassOrientation.ValueType # 6 """ Rotate the compass by 180 degrees and invert. """ DEGREES_270_INVERTED: Config.DisplayConfig._CompassOrientation.ValueType # 7 """ Rotate the compass by 270 degrees and invert. """ class CompassOrientation(_CompassOrientation, metaclass=_CompassOrientationEnumTypeWrapper): ... DEGREES_0: Config.DisplayConfig.CompassOrientation.ValueType # 0 """ The compass and the display are in the same orientation. """ DEGREES_90: Config.DisplayConfig.CompassOrientation.ValueType # 1 """ Rotate the compass by 90 degrees. """ DEGREES_180: Config.DisplayConfig.CompassOrientation.ValueType # 2 """ Rotate the compass by 180 degrees. """ DEGREES_270: Config.DisplayConfig.CompassOrientation.ValueType # 3 """ Rotate the compass by 270 degrees. """ DEGREES_0_INVERTED: Config.DisplayConfig.CompassOrientation.ValueType # 4 """ Don't rotate the compass, but invert the result. """ DEGREES_90_INVERTED: Config.DisplayConfig.CompassOrientation.ValueType # 5 """ Rotate the compass by 90 degrees and invert. """ DEGREES_180_INVERTED: Config.DisplayConfig.CompassOrientation.ValueType # 6 """ Rotate the compass by 180 degrees and invert. """ DEGREES_270_INVERTED: Config.DisplayConfig.CompassOrientation.ValueType # 7 """ Rotate the compass by 270 degrees and invert. """ SCREEN_ON_SECS_FIELD_NUMBER: builtins.int GPS_FORMAT_FIELD_NUMBER: builtins.int AUTO_SCREEN_CAROUSEL_SECS_FIELD_NUMBER: builtins.int COMPASS_NORTH_TOP_FIELD_NUMBER: builtins.int FLIP_SCREEN_FIELD_NUMBER: builtins.int UNITS_FIELD_NUMBER: builtins.int OLED_FIELD_NUMBER: builtins.int DISPLAYMODE_FIELD_NUMBER: builtins.int HEADING_BOLD_FIELD_NUMBER: builtins.int WAKE_ON_TAP_OR_MOTION_FIELD_NUMBER: builtins.int COMPASS_ORIENTATION_FIELD_NUMBER: builtins.int screen_on_secs: builtins.int """ Number of seconds the screen stays on after pressing the user button or receiving a message 0 for default of one minute MAXUINT for always on """ gps_format: global___Config.DisplayConfig.GpsCoordinateFormat.ValueType """ How the GPS coordinates are formatted on the OLED screen. """ auto_screen_carousel_secs: builtins.int """ Automatically toggles to the next page on the screen like a carousel, based the specified interval in seconds. Potentially useful for devices without user buttons. """ compass_north_top: builtins.bool """ If this is set, the displayed compass will always point north. if unset, the old behaviour (top of display is heading direction) is used. """ flip_screen: builtins.bool """ Flip screen vertically, for cases that mount the screen upside down """ units: global___Config.DisplayConfig.DisplayUnits.ValueType """ Perferred display units """ oled: global___Config.DisplayConfig.OledType.ValueType """ Override auto-detect in screen """ displaymode: global___Config.DisplayConfig.DisplayMode.ValueType """ Display Mode """ heading_bold: builtins.bool """ Print first line in pseudo-bold? FALSE is original style, TRUE is bold """ wake_on_tap_or_motion: builtins.bool """ Should we wake the screen up on accelerometer detected motion or tap """ compass_orientation: global___Config.DisplayConfig.CompassOrientation.ValueType """ Indicates how to rotate or invert the compass output to accurate display on the display. """ def __init__( self, *, screen_on_secs: builtins.int = ..., gps_format: global___Config.DisplayConfig.GpsCoordinateFormat.ValueType = ..., auto_screen_carousel_secs: builtins.int = ..., compass_north_top: builtins.bool = ..., flip_screen: builtins.bool = ..., units: global___Config.DisplayConfig.DisplayUnits.ValueType = ..., oled: global___Config.DisplayConfig.OledType.ValueType = ..., displaymode: global___Config.DisplayConfig.DisplayMode.ValueType = ..., heading_bold: builtins.bool = ..., wake_on_tap_or_motion: builtins.bool = ..., compass_orientation: global___Config.DisplayConfig.CompassOrientation.ValueType = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["auto_screen_carousel_secs", b"auto_screen_carousel_secs", "compass_north_top", b"compass_north_top", "compass_orientation", b"compass_orientation", "displaymode", b"displaymode", "flip_screen", b"flip_screen", "gps_format", b"gps_format", "heading_bold", b"heading_bold", "oled", b"oled", "screen_on_secs", b"screen_on_secs", "units", b"units", "wake_on_tap_or_motion", b"wake_on_tap_or_motion"]) -> None: ... @typing.final class LoRaConfig(google.protobuf.message.Message): """ Lora Config """ DESCRIPTOR: google.protobuf.descriptor.Descriptor class _RegionCode: ValueType = typing.NewType("ValueType", builtins.int) V: typing_extensions.TypeAlias = ValueType class _RegionCodeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[Config.LoRaConfig._RegionCode.ValueType], builtins.type): DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor UNSET: Config.LoRaConfig._RegionCode.ValueType # 0 """ Region is not set """ US: Config.LoRaConfig._RegionCode.ValueType # 1 """ United States """ EU_433: Config.LoRaConfig._RegionCode.ValueType # 2 """ European Union 433mhz """ EU_868: Config.LoRaConfig._RegionCode.ValueType # 3 """ European Union 868mhz """ CN: Config.LoRaConfig._RegionCode.ValueType # 4 """ China """ JP: Config.LoRaConfig._RegionCode.ValueType # 5 """ Japan """ ANZ: Config.LoRaConfig._RegionCode.ValueType # 6 """ Australia / New Zealand """ KR: Config.LoRaConfig._RegionCode.ValueType # 7 """ Korea """ TW: Config.LoRaConfig._RegionCode.ValueType # 8 """ Taiwan """ RU: Config.LoRaConfig._RegionCode.ValueType # 9 """ Russia """ IN: Config.LoRaConfig._RegionCode.ValueType # 10 """ India """ NZ_865: Config.LoRaConfig._RegionCode.ValueType # 11 """ New Zealand 865mhz """ TH: Config.LoRaConfig._RegionCode.ValueType # 12 """ Thailand """ LORA_24: Config.LoRaConfig._RegionCode.ValueType # 13 """ WLAN Band """ UA_433: Config.LoRaConfig._RegionCode.ValueType # 14 """ Ukraine 433mhz """ UA_868: Config.LoRaConfig._RegionCode.ValueType # 15 """ Ukraine 868mhz """ MY_433: Config.LoRaConfig._RegionCode.ValueType # 16 """ Malaysia 433mhz """ MY_919: Config.LoRaConfig._RegionCode.ValueType # 17 """ Malaysia 919mhz """ SG_923: Config.LoRaConfig._RegionCode.ValueType # 18 """ Singapore 923mhz """ class RegionCode(_RegionCode, metaclass=_RegionCodeEnumTypeWrapper): ... UNSET: Config.LoRaConfig.RegionCode.ValueType # 0 """ Region is not set """ US: Config.LoRaConfig.RegionCode.ValueType # 1 """ United States """ EU_433: Config.LoRaConfig.RegionCode.ValueType # 2 """ European Union 433mhz """ EU_868: Config.LoRaConfig.RegionCode.ValueType # 3 """ European Union 868mhz """ CN: Config.LoRaConfig.RegionCode.ValueType # 4 """ China """ JP: Config.LoRaConfig.RegionCode.ValueType # 5 """ Japan """ ANZ: Config.LoRaConfig.RegionCode.ValueType # 6 """ Australia / New Zealand """ KR: Config.LoRaConfig.RegionCode.ValueType # 7 """ Korea """ TW: Config.LoRaConfig.RegionCode.ValueType # 8 """ Taiwan """ RU: Config.LoRaConfig.RegionCode.ValueType # 9 """ Russia """ IN: Config.LoRaConfig.RegionCode.ValueType # 10 """ India """ NZ_865: Config.LoRaConfig.RegionCode.ValueType # 11 """ New Zealand 865mhz """ TH: Config.LoRaConfig.RegionCode.ValueType # 12 """ Thailand """ LORA_24: Config.LoRaConfig.RegionCode.ValueType # 13 """ WLAN Band """ UA_433: Config.LoRaConfig.RegionCode.ValueType # 14 """ Ukraine 433mhz """ UA_868: Config.LoRaConfig.RegionCode.ValueType # 15 """ Ukraine 868mhz """ MY_433: Config.LoRaConfig.RegionCode.ValueType # 16 """ Malaysia 433mhz """ MY_919: Config.LoRaConfig.RegionCode.ValueType # 17 """ Malaysia 919mhz """ SG_923: Config.LoRaConfig.RegionCode.ValueType # 18 """ Singapore 923mhz """ class _ModemPreset: ValueType = typing.NewType("ValueType", builtins.int) V: typing_extensions.TypeAlias = ValueType class _ModemPresetEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[Config.LoRaConfig._ModemPreset.ValueType], builtins.type): DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor LONG_FAST: Config.LoRaConfig._ModemPreset.ValueType # 0 """ Long Range - Fast """ LONG_SLOW: Config.LoRaConfig._ModemPreset.ValueType # 1 """ Long Range - Slow """ VERY_LONG_SLOW: Config.LoRaConfig._ModemPreset.ValueType # 2 """ Very Long Range - Slow """ MEDIUM_SLOW: Config.LoRaConfig._ModemPreset.ValueType # 3 """ Medium Range - Slow """ MEDIUM_FAST: Config.LoRaConfig._ModemPreset.ValueType # 4 """ Medium Range - Fast """ SHORT_SLOW: Config.LoRaConfig._ModemPreset.ValueType # 5 """ Short Range - Slow """ SHORT_FAST: Config.LoRaConfig._ModemPreset.ValueType # 6 """ Short Range - Fast """ LONG_MODERATE: Config.LoRaConfig._ModemPreset.ValueType # 7 """ Long Range - Moderately Fast """ class ModemPreset(_ModemPreset, metaclass=_ModemPresetEnumTypeWrapper): """ Standard predefined channel settings Note: these mappings must match ModemPreset Choice in the device code. """ LONG_FAST: Config.LoRaConfig.ModemPreset.ValueType # 0 """ Long Range - Fast """ LONG_SLOW: Config.LoRaConfig.ModemPreset.ValueType # 1 """ Long Range - Slow """ VERY_LONG_SLOW: Config.LoRaConfig.ModemPreset.ValueType # 2 """ Very Long Range - Slow """ MEDIUM_SLOW: Config.LoRaConfig.ModemPreset.ValueType # 3 """ Medium Range - Slow """ MEDIUM_FAST: Config.LoRaConfig.ModemPreset.ValueType # 4 """ Medium Range - Fast """ SHORT_SLOW: Config.LoRaConfig.ModemPreset.ValueType # 5 """ Short Range - Slow """ SHORT_FAST: Config.LoRaConfig.ModemPreset.ValueType # 6 """ Short Range - Fast """ LONG_MODERATE: Config.LoRaConfig.ModemPreset.ValueType # 7 """ Long Range - Moderately Fast """ USE_PRESET_FIELD_NUMBER: builtins.int MODEM_PRESET_FIELD_NUMBER: builtins.int BANDWIDTH_FIELD_NUMBER: builtins.int SPREAD_FACTOR_FIELD_NUMBER: builtins.int CODING_RATE_FIELD_NUMBER: builtins.int FREQUENCY_OFFSET_FIELD_NUMBER: builtins.int REGION_FIELD_NUMBER: builtins.int HOP_LIMIT_FIELD_NUMBER: builtins.int TX_ENABLED_FIELD_NUMBER: builtins.int TX_POWER_FIELD_NUMBER: builtins.int CHANNEL_NUM_FIELD_NUMBER: builtins.int OVERRIDE_DUTY_CYCLE_FIELD_NUMBER: builtins.int SX126X_RX_BOOSTED_GAIN_FIELD_NUMBER: builtins.int OVERRIDE_FREQUENCY_FIELD_NUMBER: builtins.int IGNORE_INCOMING_FIELD_NUMBER: builtins.int IGNORE_MQTT_FIELD_NUMBER: builtins.int use_preset: builtins.bool """ When enabled, the `modem_preset` fields will be adhered to, else the `bandwidth`/`spread_factor`/`coding_rate` will be taked from their respective manually defined fields """ modem_preset: global___Config.LoRaConfig.ModemPreset.ValueType """ Either modem_config or bandwidth/spreading/coding will be specified - NOT BOTH. As a heuristic: If bandwidth is specified, do not use modem_config. Because protobufs take ZERO space when the value is zero this works out nicely. This value is replaced by bandwidth/spread_factor/coding_rate. If you'd like to experiment with other options add them to MeshRadio.cpp in the device code. """ bandwidth: builtins.int """ Bandwidth in MHz Certain bandwidth numbers are 'special' and will be converted to the appropriate floating point value: 31 -> 31.25MHz """ spread_factor: builtins.int """ A number from 7 to 12. Indicates number of chirps per symbol as 1< 7 results in the default """ tx_enabled: builtins.bool """ Disable TX from the LoRa radio. Useful for hot-swapping antennas and other tests. Defaults to false """ tx_power: builtins.int """ If zero, then use default max legal continuous power (ie. something that won't burn out the radio hardware) In most cases you should use zero here. Units are in dBm. """ channel_num: builtins.int """ This controls the actual hardware frequency the radio transmits on. Most users should never need to be exposed to this field/concept. A channel number between 1 and NUM_CHANNELS (whatever the max is in the current region). If ZERO then the rule is "use the old channel name hash based algorithm to derive the channel number") If using the hash algorithm the channel number will be: hash(channel_name) % NUM_CHANNELS (Where num channels depends on the regulatory region). """ override_duty_cycle: builtins.bool """ If true, duty cycle limits will be exceeded and thus you're possibly not following the local regulations if you're not a HAM. Has no effect if the duty cycle of the used region is 100%. """ sx126x_rx_boosted_gain: builtins.bool """ If true, sets RX boosted gain mode on SX126X based radios """ override_frequency: builtins.float """ This parameter is for advanced users and licensed HAM radio operators. Ignore Channel Calculation and use this frequency instead. The frequency_offset will still be applied. This will allow you to use out-of-band frequencies. Please respect your local laws and regulations. If you are a HAM, make sure you enable HAM mode and turn off encryption. """ ignore_mqtt: builtins.bool """ If true, the device will not process any packets received via LoRa that passed via MQTT anywhere on the path towards it. """ @property def ignore_incoming(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.int]: """ For testing it is useful sometimes to force a node to never listen to particular other nodes (simulating radio out of range). All nodenums listed in ignore_incoming will have packets they send dropped on receive (by router.cpp) """ def __init__( self, *, use_preset: builtins.bool = ..., modem_preset: global___Config.LoRaConfig.ModemPreset.ValueType = ..., bandwidth: builtins.int = ..., spread_factor: builtins.int = ..., coding_rate: builtins.int = ..., frequency_offset: builtins.float = ..., region: global___Config.LoRaConfig.RegionCode.ValueType = ..., hop_limit: builtins.int = ..., tx_enabled: builtins.bool = ..., tx_power: builtins.int = ..., channel_num: builtins.int = ..., override_duty_cycle: builtins.bool = ..., sx126x_rx_boosted_gain: builtins.bool = ..., override_frequency: builtins.float = ..., ignore_incoming: collections.abc.Iterable[builtins.int] | None = ..., ignore_mqtt: builtins.bool = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["bandwidth", b"bandwidth", "channel_num", b"channel_num", "coding_rate", b"coding_rate", "frequency_offset", b"frequency_offset", "hop_limit", b"hop_limit", "ignore_incoming", b"ignore_incoming", "ignore_mqtt", b"ignore_mqtt", "modem_preset", b"modem_preset", "override_duty_cycle", b"override_duty_cycle", "override_frequency", b"override_frequency", "region", b"region", "spread_factor", b"spread_factor", "sx126x_rx_boosted_gain", b"sx126x_rx_boosted_gain", "tx_enabled", b"tx_enabled", "tx_power", b"tx_power", "use_preset", b"use_preset"]) -> None: ... @typing.final class BluetoothConfig(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor class _PairingMode: ValueType = typing.NewType("ValueType", builtins.int) V: typing_extensions.TypeAlias = ValueType class _PairingModeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[Config.BluetoothConfig._PairingMode.ValueType], builtins.type): DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor RANDOM_PIN: Config.BluetoothConfig._PairingMode.ValueType # 0 """ Device generates a random PIN that will be shown on the screen of the device for pairing """ FIXED_PIN: Config.BluetoothConfig._PairingMode.ValueType # 1 """ Device requires a specified fixed PIN for pairing """ NO_PIN: Config.BluetoothConfig._PairingMode.ValueType # 2 """ Device requires no PIN for pairing """ class PairingMode(_PairingMode, metaclass=_PairingModeEnumTypeWrapper): ... RANDOM_PIN: Config.BluetoothConfig.PairingMode.ValueType # 0 """ Device generates a random PIN that will be shown on the screen of the device for pairing """ FIXED_PIN: Config.BluetoothConfig.PairingMode.ValueType # 1 """ Device requires a specified fixed PIN for pairing """ NO_PIN: Config.BluetoothConfig.PairingMode.ValueType # 2 """ Device requires no PIN for pairing """ ENABLED_FIELD_NUMBER: builtins.int MODE_FIELD_NUMBER: builtins.int FIXED_PIN_FIELD_NUMBER: builtins.int DEVICE_LOGGING_ENABLED_FIELD_NUMBER: builtins.int enabled: builtins.bool """ Enable Bluetooth on the device """ mode: global___Config.BluetoothConfig.PairingMode.ValueType """ Determines the pairing strategy for the device """ fixed_pin: builtins.int """ Specified PIN for PairingMode.FixedPin """ device_logging_enabled: builtins.bool """ Enables device (serial style logs) over Bluetooth """ def __init__( self, *, enabled: builtins.bool = ..., mode: global___Config.BluetoothConfig.PairingMode.ValueType = ..., fixed_pin: builtins.int = ..., device_logging_enabled: builtins.bool = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["device_logging_enabled", b"device_logging_enabled", "enabled", b"enabled", "fixed_pin", b"fixed_pin", "mode", b"mode"]) -> None: ... DEVICE_FIELD_NUMBER: builtins.int POSITION_FIELD_NUMBER: builtins.int POWER_FIELD_NUMBER: builtins.int NETWORK_FIELD_NUMBER: builtins.int DISPLAY_FIELD_NUMBER: builtins.int LORA_FIELD_NUMBER: builtins.int BLUETOOTH_FIELD_NUMBER: builtins.int @property def device(self) -> global___Config.DeviceConfig: ... @property def position(self) -> global___Config.PositionConfig: ... @property def power(self) -> global___Config.PowerConfig: ... @property def network(self) -> global___Config.NetworkConfig: ... @property def display(self) -> global___Config.DisplayConfig: ... @property def lora(self) -> global___Config.LoRaConfig: ... @property def bluetooth(self) -> global___Config.BluetoothConfig: ... def __init__( self, *, device: global___Config.DeviceConfig | None = ..., position: global___Config.PositionConfig | None = ..., power: global___Config.PowerConfig | None = ..., network: global___Config.NetworkConfig | None = ..., display: global___Config.DisplayConfig | None = ..., lora: global___Config.LoRaConfig | None = ..., bluetooth: global___Config.BluetoothConfig | None = ..., ) -> None: ... def HasField(self, field_name: typing.Literal["bluetooth", b"bluetooth", "device", b"device", "display", b"display", "lora", b"lora", "network", b"network", "payload_variant", b"payload_variant", "position", b"position", "power", b"power"]) -> builtins.bool: ... def ClearField(self, field_name: typing.Literal["bluetooth", b"bluetooth", "device", b"device", "display", b"display", "lora", b"lora", "network", b"network", "payload_variant", b"payload_variant", "position", b"position", "power", b"power"]) -> None: ... def WhichOneof(self, oneof_group: typing.Literal["payload_variant", b"payload_variant"]) -> typing.Literal["device", "position", "power", "network", "display", "lora", "bluetooth"] | None: ... global___Config = Config python-2.3.14/meshtastic/protobuf/connection_status_pb2.py000066400000000000000000000063221464266072200240210ustar00rootroot00000000000000# -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # source: meshtastic/protobuf/connection_status.proto """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n+meshtastic/protobuf/connection_status.proto\x12\x13meshtastic.protobuf\"\xd5\x02\n\x16\x44\x65viceConnectionStatus\x12<\n\x04wifi\x18\x01 \x01(\x0b\x32).meshtastic.protobuf.WifiConnectionStatusH\x00\x88\x01\x01\x12\x44\n\x08\x65thernet\x18\x02 \x01(\x0b\x32-.meshtastic.protobuf.EthernetConnectionStatusH\x01\x88\x01\x01\x12\x46\n\tbluetooth\x18\x03 \x01(\x0b\x32..meshtastic.protobuf.BluetoothConnectionStatusH\x02\x88\x01\x01\x12@\n\x06serial\x18\x04 \x01(\x0b\x32+.meshtastic.protobuf.SerialConnectionStatusH\x03\x88\x01\x01\x42\x07\n\x05_wifiB\x0b\n\t_ethernetB\x0c\n\n_bluetoothB\t\n\x07_serial\"p\n\x14WifiConnectionStatus\x12<\n\x06status\x18\x01 \x01(\x0b\x32,.meshtastic.protobuf.NetworkConnectionStatus\x12\x0c\n\x04ssid\x18\x02 \x01(\t\x12\x0c\n\x04rssi\x18\x03 \x01(\x05\"X\n\x18\x45thernetConnectionStatus\x12<\n\x06status\x18\x01 \x01(\x0b\x32,.meshtastic.protobuf.NetworkConnectionStatus\"{\n\x17NetworkConnectionStatus\x12\x12\n\nip_address\x18\x01 \x01(\x07\x12\x14\n\x0cis_connected\x18\x02 \x01(\x08\x12\x19\n\x11is_mqtt_connected\x18\x03 \x01(\x08\x12\x1b\n\x13is_syslog_connected\x18\x04 \x01(\x08\"L\n\x19\x42luetoothConnectionStatus\x12\x0b\n\x03pin\x18\x01 \x01(\r\x12\x0c\n\x04rssi\x18\x02 \x01(\x05\x12\x14\n\x0cis_connected\x18\x03 \x01(\x08\"<\n\x16SerialConnectionStatus\x12\x0c\n\x04\x62\x61ud\x18\x01 \x01(\r\x12\x14\n\x0cis_connected\x18\x02 \x01(\x08\x42\x65\n\x13\x63om.geeksville.meshB\x10\x43onnStatusProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.connection_status_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\020ConnStatusProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' _globals['_DEVICECONNECTIONSTATUS']._serialized_start=69 _globals['_DEVICECONNECTIONSTATUS']._serialized_end=410 _globals['_WIFICONNECTIONSTATUS']._serialized_start=412 _globals['_WIFICONNECTIONSTATUS']._serialized_end=524 _globals['_ETHERNETCONNECTIONSTATUS']._serialized_start=526 _globals['_ETHERNETCONNECTIONSTATUS']._serialized_end=614 _globals['_NETWORKCONNECTIONSTATUS']._serialized_start=616 _globals['_NETWORKCONNECTIONSTATUS']._serialized_end=739 _globals['_BLUETOOTHCONNECTIONSTATUS']._serialized_start=741 _globals['_BLUETOOTHCONNECTIONSTATUS']._serialized_end=817 _globals['_SERIALCONNECTIONSTATUS']._serialized_start=819 _globals['_SERIALCONNECTIONSTATUS']._serialized_end=879 # @@protoc_insertion_point(module_scope) python-2.3.14/meshtastic/protobuf/connection_status_pb2.pyi000066400000000000000000000157211464266072200241750ustar00rootroot00000000000000""" @generated by mypy-protobuf. Do not edit manually! isort:skip_file """ import builtins import google.protobuf.descriptor import google.protobuf.message import typing DESCRIPTOR: google.protobuf.descriptor.FileDescriptor @typing.final class DeviceConnectionStatus(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor WIFI_FIELD_NUMBER: builtins.int ETHERNET_FIELD_NUMBER: builtins.int BLUETOOTH_FIELD_NUMBER: builtins.int SERIAL_FIELD_NUMBER: builtins.int @property def wifi(self) -> global___WifiConnectionStatus: """ WiFi Status """ @property def ethernet(self) -> global___EthernetConnectionStatus: """ WiFi Status """ @property def bluetooth(self) -> global___BluetoothConnectionStatus: """ Bluetooth Status """ @property def serial(self) -> global___SerialConnectionStatus: """ Serial Status """ def __init__( self, *, wifi: global___WifiConnectionStatus | None = ..., ethernet: global___EthernetConnectionStatus | None = ..., bluetooth: global___BluetoothConnectionStatus | None = ..., serial: global___SerialConnectionStatus | None = ..., ) -> None: ... def HasField(self, field_name: typing.Literal["_bluetooth", b"_bluetooth", "_ethernet", b"_ethernet", "_serial", b"_serial", "_wifi", b"_wifi", "bluetooth", b"bluetooth", "ethernet", b"ethernet", "serial", b"serial", "wifi", b"wifi"]) -> builtins.bool: ... def ClearField(self, field_name: typing.Literal["_bluetooth", b"_bluetooth", "_ethernet", b"_ethernet", "_serial", b"_serial", "_wifi", b"_wifi", "bluetooth", b"bluetooth", "ethernet", b"ethernet", "serial", b"serial", "wifi", b"wifi"]) -> None: ... @typing.overload def WhichOneof(self, oneof_group: typing.Literal["_bluetooth", b"_bluetooth"]) -> typing.Literal["bluetooth"] | None: ... @typing.overload def WhichOneof(self, oneof_group: typing.Literal["_ethernet", b"_ethernet"]) -> typing.Literal["ethernet"] | None: ... @typing.overload def WhichOneof(self, oneof_group: typing.Literal["_serial", b"_serial"]) -> typing.Literal["serial"] | None: ... @typing.overload def WhichOneof(self, oneof_group: typing.Literal["_wifi", b"_wifi"]) -> typing.Literal["wifi"] | None: ... global___DeviceConnectionStatus = DeviceConnectionStatus @typing.final class WifiConnectionStatus(google.protobuf.message.Message): """ WiFi connection status """ DESCRIPTOR: google.protobuf.descriptor.Descriptor STATUS_FIELD_NUMBER: builtins.int SSID_FIELD_NUMBER: builtins.int RSSI_FIELD_NUMBER: builtins.int ssid: builtins.str """ WiFi access point SSID """ rssi: builtins.int """ RSSI of wireless connection """ @property def status(self) -> global___NetworkConnectionStatus: """ Connection status """ def __init__( self, *, status: global___NetworkConnectionStatus | None = ..., ssid: builtins.str = ..., rssi: builtins.int = ..., ) -> None: ... def HasField(self, field_name: typing.Literal["status", b"status"]) -> builtins.bool: ... def ClearField(self, field_name: typing.Literal["rssi", b"rssi", "ssid", b"ssid", "status", b"status"]) -> None: ... global___WifiConnectionStatus = WifiConnectionStatus @typing.final class EthernetConnectionStatus(google.protobuf.message.Message): """ Ethernet connection status """ DESCRIPTOR: google.protobuf.descriptor.Descriptor STATUS_FIELD_NUMBER: builtins.int @property def status(self) -> global___NetworkConnectionStatus: """ Connection status """ def __init__( self, *, status: global___NetworkConnectionStatus | None = ..., ) -> None: ... def HasField(self, field_name: typing.Literal["status", b"status"]) -> builtins.bool: ... def ClearField(self, field_name: typing.Literal["status", b"status"]) -> None: ... global___EthernetConnectionStatus = EthernetConnectionStatus @typing.final class NetworkConnectionStatus(google.protobuf.message.Message): """ Ethernet or WiFi connection status """ DESCRIPTOR: google.protobuf.descriptor.Descriptor IP_ADDRESS_FIELD_NUMBER: builtins.int IS_CONNECTED_FIELD_NUMBER: builtins.int IS_MQTT_CONNECTED_FIELD_NUMBER: builtins.int IS_SYSLOG_CONNECTED_FIELD_NUMBER: builtins.int ip_address: builtins.int """ IP address of device """ is_connected: builtins.bool """ Whether the device has an active connection or not """ is_mqtt_connected: builtins.bool """ Whether the device has an active connection to an MQTT broker or not """ is_syslog_connected: builtins.bool """ Whether the device is actively remote syslogging or not """ def __init__( self, *, ip_address: builtins.int = ..., is_connected: builtins.bool = ..., is_mqtt_connected: builtins.bool = ..., is_syslog_connected: builtins.bool = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["ip_address", b"ip_address", "is_connected", b"is_connected", "is_mqtt_connected", b"is_mqtt_connected", "is_syslog_connected", b"is_syslog_connected"]) -> None: ... global___NetworkConnectionStatus = NetworkConnectionStatus @typing.final class BluetoothConnectionStatus(google.protobuf.message.Message): """ Bluetooth connection status """ DESCRIPTOR: google.protobuf.descriptor.Descriptor PIN_FIELD_NUMBER: builtins.int RSSI_FIELD_NUMBER: builtins.int IS_CONNECTED_FIELD_NUMBER: builtins.int pin: builtins.int """ The pairing PIN for bluetooth """ rssi: builtins.int """ RSSI of bluetooth connection """ is_connected: builtins.bool """ Whether the device has an active connection or not """ def __init__( self, *, pin: builtins.int = ..., rssi: builtins.int = ..., is_connected: builtins.bool = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["is_connected", b"is_connected", "pin", b"pin", "rssi", b"rssi"]) -> None: ... global___BluetoothConnectionStatus = BluetoothConnectionStatus @typing.final class SerialConnectionStatus(google.protobuf.message.Message): """ Serial connection status """ DESCRIPTOR: google.protobuf.descriptor.Descriptor BAUD_FIELD_NUMBER: builtins.int IS_CONNECTED_FIELD_NUMBER: builtins.int baud: builtins.int """ Serial baud rate """ is_connected: builtins.bool """ Whether the device has an active connection or not """ def __init__( self, *, baud: builtins.int = ..., is_connected: builtins.bool = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["baud", b"baud", "is_connected", b"is_connected"]) -> None: ... global___SerialConnectionStatus = SerialConnectionStatus python-2.3.14/meshtastic/protobuf/deviceonly_pb2.py000066400000000000000000000121411464266072200224140ustar00rootroot00000000000000# -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # source: meshtastic/protobuf/deviceonly.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 symbol_database as _symbol_database from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() from meshtastic.protobuf import channel_pb2 as meshtastic_dot_protobuf_dot_channel__pb2 from meshtastic.protobuf import localonly_pb2 as meshtastic_dot_protobuf_dot_localonly__pb2 from meshtastic.protobuf import mesh_pb2 as meshtastic_dot_protobuf_dot_mesh__pb2 from meshtastic.protobuf import module_config_pb2 as meshtastic_dot_protobuf_dot_module__config__pb2 from meshtastic.protobuf import telemetry_pb2 as meshtastic_dot_protobuf_dot_telemetry__pb2 import nanopb_pb2 as nanopb__pb2 DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n$meshtastic/protobuf/deviceonly.proto\x12\x13meshtastic.protobuf\x1a!meshtastic/protobuf/channel.proto\x1a#meshtastic/protobuf/localonly.proto\x1a\x1emeshtastic/protobuf/mesh.proto\x1a\'meshtastic/protobuf/module_config.proto\x1a#meshtastic/protobuf/telemetry.proto\x1a\x0cnanopb.proto\"\x99\x01\n\x0cPositionLite\x12\x12\n\nlatitude_i\x18\x01 \x01(\x0f\x12\x13\n\x0blongitude_i\x18\x02 \x01(\x0f\x12\x10\n\x08\x61ltitude\x18\x03 \x01(\x05\x12\x0c\n\x04time\x18\x04 \x01(\x07\x12@\n\x0flocation_source\x18\x05 \x01(\x0e\x32\'.meshtastic.protobuf.Position.LocSource\"\xa1\x02\n\x0cNodeInfoLite\x12\x0b\n\x03num\x18\x01 \x01(\r\x12\'\n\x04user\x18\x02 \x01(\x0b\x32\x19.meshtastic.protobuf.User\x12\x33\n\x08position\x18\x03 \x01(\x0b\x32!.meshtastic.protobuf.PositionLite\x12\x0b\n\x03snr\x18\x04 \x01(\x02\x12\x12\n\nlast_heard\x18\x05 \x01(\x07\x12:\n\x0e\x64\x65vice_metrics\x18\x06 \x01(\x0b\x32\".meshtastic.protobuf.DeviceMetrics\x12\x0f\n\x07\x63hannel\x18\x07 \x01(\r\x12\x10\n\x08via_mqtt\x18\x08 \x01(\x08\x12\x11\n\thops_away\x18\t \x01(\r\x12\x13\n\x0bis_favorite\x18\n \x01(\x08\"\x82\x04\n\x0b\x44\x65viceState\x12\x30\n\x07my_node\x18\x02 \x01(\x0b\x32\x1f.meshtastic.protobuf.MyNodeInfo\x12(\n\x05owner\x18\x03 \x01(\x0b\x32\x19.meshtastic.protobuf.User\x12\x36\n\rreceive_queue\x18\x05 \x03(\x0b\x32\x1f.meshtastic.protobuf.MeshPacket\x12\x0f\n\x07version\x18\x08 \x01(\r\x12\x38\n\x0frx_text_message\x18\x07 \x01(\x0b\x32\x1f.meshtastic.protobuf.MeshPacket\x12\x13\n\x07no_save\x18\t \x01(\x08\x42\x02\x18\x01\x12\x15\n\rdid_gps_reset\x18\x0b \x01(\x08\x12\x34\n\x0brx_waypoint\x18\x0c \x01(\x0b\x32\x1f.meshtastic.protobuf.MeshPacket\x12M\n\x19node_remote_hardware_pins\x18\r \x03(\x0b\x32*.meshtastic.protobuf.NodeRemoteHardwarePin\x12\x63\n\x0cnode_db_lite\x18\x0e \x03(\x0b\x32!.meshtastic.protobuf.NodeInfoLiteB*\x92?\'\x92\x01$std::vector\"N\n\x0b\x43hannelFile\x12.\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x1c.meshtastic.protobuf.Channel\x12\x0f\n\x07version\x18\x02 \x01(\r\"\xb2\x02\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\x32\n\x08oem_font\x18\x04 \x01(\x0e\x32 .meshtastic.protobuf.ScreenFonts\x12\x10\n\x08oem_text\x18\x05 \x01(\t\x12\x13\n\x0boem_aes_key\x18\x06 \x01(\x0c\x12:\n\x10oem_local_config\x18\x07 \x01(\x0b\x32 .meshtastic.protobuf.LocalConfig\x12G\n\x17oem_local_module_config\x18\x08 \x01(\x0b\x32&.meshtastic.protobuf.LocalModuleConfig*>\n\x0bScreenFonts\x12\x0e\n\nFONT_SMALL\x10\x00\x12\x0f\n\x0b\x46ONT_MEDIUM\x10\x01\x12\x0e\n\nFONT_LARGE\x10\x02\x42m\n\x13\x63om.geeksville.meshB\nDeviceOnlyZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x92?\x0b\xc2\x01\x08b\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.deviceonly_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\nDeviceOnlyZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000\222?\013\302\001\010' _DEVICESTATE.fields_by_name['no_save']._options = None _DEVICESTATE.fields_by_name['no_save']._serialized_options = b'\030\001' _DEVICESTATE.fields_by_name['node_db_lite']._options = None _DEVICESTATE.fields_by_name['node_db_lite']._serialized_options = b'\222?\'\222\001$std::vector' _globals['_SCREENFONTS']._serialized_start=1611 _globals['_SCREENFONTS']._serialized_end=1673 _globals['_POSITIONLITE']._serialized_start=258 _globals['_POSITIONLITE']._serialized_end=411 _globals['_NODEINFOLITE']._serialized_start=414 _globals['_NODEINFOLITE']._serialized_end=703 _globals['_DEVICESTATE']._serialized_start=706 _globals['_DEVICESTATE']._serialized_end=1220 _globals['_CHANNELFILE']._serialized_start=1222 _globals['_CHANNELFILE']._serialized_end=1300 _globals['_OEMSTORE']._serialized_start=1303 _globals['_OEMSTORE']._serialized_end=1609 # @@protoc_insertion_point(module_scope) python-2.3.14/meshtastic/protobuf/deviceonly_pb2.pyi000066400000000000000000000337531464266072200226010ustar00rootroot00000000000000""" @generated by mypy-protobuf. Do not edit manually! isort:skip_file """ import builtins import collections.abc import google.protobuf.descriptor import google.protobuf.internal.containers import google.protobuf.internal.enum_type_wrapper import google.protobuf.message import meshtastic.protobuf.channel_pb2 import meshtastic.protobuf.localonly_pb2 import meshtastic.protobuf.mesh_pb2 import meshtastic.protobuf.telemetry_pb2 import sys import typing if sys.version_info >= (3, 10): import typing as typing_extensions else: import typing_extensions DESCRIPTOR: google.protobuf.descriptor.FileDescriptor class _ScreenFonts: ValueType = typing.NewType("ValueType", builtins.int) V: typing_extensions.TypeAlias = ValueType class _ScreenFontsEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_ScreenFonts.ValueType], builtins.type): DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor FONT_SMALL: _ScreenFonts.ValueType # 0 """ TODO: REPLACE """ FONT_MEDIUM: _ScreenFonts.ValueType # 1 """ TODO: REPLACE """ FONT_LARGE: _ScreenFonts.ValueType # 2 """ TODO: REPLACE """ class ScreenFonts(_ScreenFonts, metaclass=_ScreenFontsEnumTypeWrapper): """ Font sizes for the device screen """ FONT_SMALL: ScreenFonts.ValueType # 0 """ TODO: REPLACE """ FONT_MEDIUM: ScreenFonts.ValueType # 1 """ TODO: REPLACE """ FONT_LARGE: ScreenFonts.ValueType # 2 """ TODO: REPLACE """ global___ScreenFonts = ScreenFonts @typing.final class PositionLite(google.protobuf.message.Message): """ Position with static location information only for NodeDBLite """ DESCRIPTOR: google.protobuf.descriptor.Descriptor LATITUDE_I_FIELD_NUMBER: builtins.int LONGITUDE_I_FIELD_NUMBER: builtins.int ALTITUDE_FIELD_NUMBER: builtins.int TIME_FIELD_NUMBER: builtins.int LOCATION_SOURCE_FIELD_NUMBER: builtins.int latitude_i: builtins.int """ The new preferred location encoding, multiply by 1e-7 to get degrees in floating point """ longitude_i: builtins.int """ TODO: REPLACE """ altitude: builtins.int """ In meters above MSL (but see issue #359) """ time: builtins.int """ This is usually not sent over the mesh (to save space), but it is sent from the phone so that the local device can set its RTC If it is sent over the mesh (because there are devices on the mesh without GPS), it will only be sent by devices which has a hardware GPS clock. seconds since 1970 """ location_source: meshtastic.protobuf.mesh_pb2.Position.LocSource.ValueType """ TODO: REPLACE """ def __init__( self, *, latitude_i: builtins.int = ..., longitude_i: builtins.int = ..., altitude: builtins.int = ..., time: builtins.int = ..., location_source: meshtastic.protobuf.mesh_pb2.Position.LocSource.ValueType = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["altitude", b"altitude", "latitude_i", b"latitude_i", "location_source", b"location_source", "longitude_i", b"longitude_i", "time", b"time"]) -> None: ... global___PositionLite = PositionLite @typing.final class NodeInfoLite(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NUM_FIELD_NUMBER: builtins.int USER_FIELD_NUMBER: builtins.int POSITION_FIELD_NUMBER: builtins.int SNR_FIELD_NUMBER: builtins.int LAST_HEARD_FIELD_NUMBER: builtins.int DEVICE_METRICS_FIELD_NUMBER: builtins.int CHANNEL_FIELD_NUMBER: builtins.int VIA_MQTT_FIELD_NUMBER: builtins.int HOPS_AWAY_FIELD_NUMBER: builtins.int IS_FAVORITE_FIELD_NUMBER: builtins.int num: builtins.int """ The node number """ snr: builtins.float """ Returns the Signal-to-noise ratio (SNR) of the last received message, as measured by the receiver. Return SNR of the last received message in dB """ last_heard: builtins.int """ Set to indicate the last time we received a packet from this node """ channel: builtins.int """ local channel index we heard that node on. Only populated if its not the default channel. """ via_mqtt: builtins.bool """ True if we witnessed the node over MQTT instead of LoRA transport """ hops_away: builtins.int """ Number of hops away from us this node is (0 if adjacent) """ is_favorite: builtins.bool """ True if node is in our favorites list Persists between NodeDB internal clean ups """ @property def user(self) -> meshtastic.protobuf.mesh_pb2.User: """ The user info for this node """ @property def position(self) -> global___PositionLite: """ This position data. Note: before 1.2.14 we would also store the last time we've heard from this node in position.time, that is no longer true. Position.time now indicates the last time we received a POSITION from that node. """ @property def device_metrics(self) -> meshtastic.protobuf.telemetry_pb2.DeviceMetrics: """ The latest device metrics for the node. """ def __init__( self, *, num: builtins.int = ..., user: meshtastic.protobuf.mesh_pb2.User | None = ..., position: global___PositionLite | None = ..., snr: builtins.float = ..., last_heard: builtins.int = ..., device_metrics: meshtastic.protobuf.telemetry_pb2.DeviceMetrics | None = ..., channel: builtins.int = ..., via_mqtt: builtins.bool = ..., hops_away: builtins.int = ..., is_favorite: builtins.bool = ..., ) -> None: ... def HasField(self, field_name: typing.Literal["device_metrics", b"device_metrics", "position", b"position", "user", b"user"]) -> builtins.bool: ... def ClearField(self, field_name: typing.Literal["channel", b"channel", "device_metrics", b"device_metrics", "hops_away", b"hops_away", "is_favorite", b"is_favorite", "last_heard", b"last_heard", "num", b"num", "position", b"position", "snr", b"snr", "user", b"user", "via_mqtt", b"via_mqtt"]) -> None: ... global___NodeInfoLite = NodeInfoLite @typing.final class DeviceState(google.protobuf.message.Message): """ This message is never sent over the wire, but it is used for serializing DB state to flash in the device code FIXME, since we write this each time we enter deep sleep (and have infinite flash) it would be better to use some sort of append only data structure for the receive queue and use the preferences store for the other stuff """ DESCRIPTOR: google.protobuf.descriptor.Descriptor MY_NODE_FIELD_NUMBER: builtins.int OWNER_FIELD_NUMBER: builtins.int RECEIVE_QUEUE_FIELD_NUMBER: builtins.int VERSION_FIELD_NUMBER: builtins.int RX_TEXT_MESSAGE_FIELD_NUMBER: builtins.int NO_SAVE_FIELD_NUMBER: builtins.int DID_GPS_RESET_FIELD_NUMBER: builtins.int RX_WAYPOINT_FIELD_NUMBER: builtins.int NODE_REMOTE_HARDWARE_PINS_FIELD_NUMBER: builtins.int NODE_DB_LITE_FIELD_NUMBER: builtins.int version: builtins.int """ A version integer used to invalidate old save files when we make incompatible changes This integer is set at build time and is private to NodeDB.cpp in the device code. """ no_save: builtins.bool """ Used only during development. Indicates developer is testing and changes should never be saved to flash. Deprecated in 2.3.1 """ did_gps_reset: builtins.bool """ Some GPS receivers seem to have bogus settings from the factory, so we always do one factory reset. """ @property def my_node(self) -> meshtastic.protobuf.mesh_pb2.MyNodeInfo: """ Read only settings/info about this node """ @property def owner(self) -> meshtastic.protobuf.mesh_pb2.User: """ My owner info """ @property def receive_queue(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[meshtastic.protobuf.mesh_pb2.MeshPacket]: """ Received packets saved for delivery to the phone """ @property def rx_text_message(self) -> meshtastic.protobuf.mesh_pb2.MeshPacket: """ We keep the last received text message (only) stored in the device flash, so we can show it on the screen. Might be null """ @property def rx_waypoint(self) -> meshtastic.protobuf.mesh_pb2.MeshPacket: """ We keep the last received waypoint stored in the device flash, so we can show it on the screen. Might be null """ @property def node_remote_hardware_pins(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[meshtastic.protobuf.mesh_pb2.NodeRemoteHardwarePin]: """ The mesh's nodes with their available gpio pins for RemoteHardware module """ @property def node_db_lite(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___NodeInfoLite]: """ New lite version of NodeDB to decrease memory footprint """ def __init__( self, *, my_node: meshtastic.protobuf.mesh_pb2.MyNodeInfo | None = ..., owner: meshtastic.protobuf.mesh_pb2.User | None = ..., receive_queue: collections.abc.Iterable[meshtastic.protobuf.mesh_pb2.MeshPacket] | None = ..., version: builtins.int = ..., rx_text_message: meshtastic.protobuf.mesh_pb2.MeshPacket | None = ..., no_save: builtins.bool = ..., did_gps_reset: builtins.bool = ..., rx_waypoint: meshtastic.protobuf.mesh_pb2.MeshPacket | None = ..., node_remote_hardware_pins: collections.abc.Iterable[meshtastic.protobuf.mesh_pb2.NodeRemoteHardwarePin] | None = ..., node_db_lite: collections.abc.Iterable[global___NodeInfoLite] | None = ..., ) -> None: ... def HasField(self, field_name: typing.Literal["my_node", b"my_node", "owner", b"owner", "rx_text_message", b"rx_text_message", "rx_waypoint", b"rx_waypoint"]) -> builtins.bool: ... def ClearField(self, field_name: typing.Literal["did_gps_reset", b"did_gps_reset", "my_node", b"my_node", "no_save", b"no_save", "node_db_lite", b"node_db_lite", "node_remote_hardware_pins", b"node_remote_hardware_pins", "owner", b"owner", "receive_queue", b"receive_queue", "rx_text_message", b"rx_text_message", "rx_waypoint", b"rx_waypoint", "version", b"version"]) -> None: ... global___DeviceState = DeviceState @typing.final class ChannelFile(google.protobuf.message.Message): """ The on-disk saved channels """ DESCRIPTOR: google.protobuf.descriptor.Descriptor CHANNELS_FIELD_NUMBER: builtins.int VERSION_FIELD_NUMBER: builtins.int version: builtins.int """ A version integer used to invalidate old save files when we make incompatible changes This integer is set at build time and is private to NodeDB.cpp in the device code. """ @property def channels(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[meshtastic.protobuf.channel_pb2.Channel]: """ The channels our node knows about """ def __init__( self, *, channels: collections.abc.Iterable[meshtastic.protobuf.channel_pb2.Channel] | None = ..., version: builtins.int = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["channels", b"channels", "version", b"version"]) -> None: ... global___ChannelFile = ChannelFile @typing.final class OEMStore(google.protobuf.message.Message): """ This can be used for customizing the firmware distribution. If populated, show a secondary bootup screen with custom logo and text for 2.5 seconds. """ DESCRIPTOR: google.protobuf.descriptor.Descriptor OEM_ICON_WIDTH_FIELD_NUMBER: builtins.int OEM_ICON_HEIGHT_FIELD_NUMBER: builtins.int OEM_ICON_BITS_FIELD_NUMBER: builtins.int OEM_FONT_FIELD_NUMBER: builtins.int OEM_TEXT_FIELD_NUMBER: builtins.int OEM_AES_KEY_FIELD_NUMBER: builtins.int OEM_LOCAL_CONFIG_FIELD_NUMBER: builtins.int OEM_LOCAL_MODULE_CONFIG_FIELD_NUMBER: builtins.int oem_icon_width: builtins.int """ The Logo width in Px """ oem_icon_height: builtins.int """ The Logo height in Px """ oem_icon_bits: builtins.bytes """ The Logo in XBM bytechar format """ oem_font: global___ScreenFonts.ValueType """ Use this font for the OEM text. """ oem_text: builtins.str """ Use this font for the OEM text. """ oem_aes_key: builtins.bytes """ The default device encryption key, 16 or 32 byte """ @property def oem_local_config(self) -> meshtastic.protobuf.localonly_pb2.LocalConfig: """ A Preset LocalConfig to apply during factory reset """ @property def oem_local_module_config(self) -> meshtastic.protobuf.localonly_pb2.LocalModuleConfig: """ A Preset LocalModuleConfig to apply during factory reset """ def __init__( self, *, oem_icon_width: builtins.int = ..., oem_icon_height: builtins.int = ..., oem_icon_bits: builtins.bytes = ..., oem_font: global___ScreenFonts.ValueType = ..., oem_text: builtins.str = ..., oem_aes_key: builtins.bytes = ..., oem_local_config: meshtastic.protobuf.localonly_pb2.LocalConfig | None = ..., oem_local_module_config: meshtastic.protobuf.localonly_pb2.LocalModuleConfig | None = ..., ) -> None: ... def HasField(self, field_name: typing.Literal["oem_local_config", b"oem_local_config", "oem_local_module_config", b"oem_local_module_config"]) -> builtins.bool: ... def ClearField(self, field_name: typing.Literal["oem_aes_key", b"oem_aes_key", "oem_font", b"oem_font", "oem_icon_bits", b"oem_icon_bits", "oem_icon_height", b"oem_icon_height", "oem_icon_width", b"oem_icon_width", "oem_local_config", b"oem_local_config", "oem_local_module_config", b"oem_local_module_config", "oem_text", b"oem_text"]) -> None: ... global___OEMStore = OEMStore python-2.3.14/meshtastic/protobuf/localonly_pb2.py000066400000000000000000000071771464266072200222640ustar00rootroot00000000000000# -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # source: meshtastic/protobuf/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 symbol_database as _symbol_database from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() from meshtastic.protobuf import config_pb2 as meshtastic_dot_protobuf_dot_config__pb2 from meshtastic.protobuf import module_config_pb2 as meshtastic_dot_protobuf_dot_module__config__pb2 DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n#meshtastic/protobuf/localonly.proto\x12\x13meshtastic.protobuf\x1a meshtastic/protobuf/config.proto\x1a\'meshtastic/protobuf/module_config.proto\"\xbc\x03\n\x0bLocalConfig\x12\x38\n\x06\x64\x65vice\x18\x01 \x01(\x0b\x32(.meshtastic.protobuf.Config.DeviceConfig\x12<\n\x08position\x18\x02 \x01(\x0b\x32*.meshtastic.protobuf.Config.PositionConfig\x12\x36\n\x05power\x18\x03 \x01(\x0b\x32\'.meshtastic.protobuf.Config.PowerConfig\x12:\n\x07network\x18\x04 \x01(\x0b\x32).meshtastic.protobuf.Config.NetworkConfig\x12:\n\x07\x64isplay\x18\x05 \x01(\x0b\x32).meshtastic.protobuf.Config.DisplayConfig\x12\x34\n\x04lora\x18\x06 \x01(\x0b\x32&.meshtastic.protobuf.Config.LoRaConfig\x12>\n\tbluetooth\x18\x07 \x01(\x0b\x32+.meshtastic.protobuf.Config.BluetoothConfig\x12\x0f\n\x07version\x18\x08 \x01(\r\"\xf0\x07\n\x11LocalModuleConfig\x12:\n\x04mqtt\x18\x01 \x01(\x0b\x32,.meshtastic.protobuf.ModuleConfig.MQTTConfig\x12>\n\x06serial\x18\x02 \x01(\x0b\x32..meshtastic.protobuf.ModuleConfig.SerialConfig\x12[\n\x15\x65xternal_notification\x18\x03 \x01(\x0b\x32<.meshtastic.protobuf.ModuleConfig.ExternalNotificationConfig\x12K\n\rstore_forward\x18\x04 \x01(\x0b\x32\x34.meshtastic.protobuf.ModuleConfig.StoreForwardConfig\x12\x45\n\nrange_test\x18\x05 \x01(\x0b\x32\x31.meshtastic.protobuf.ModuleConfig.RangeTestConfig\x12\x44\n\ttelemetry\x18\x06 \x01(\x0b\x32\x31.meshtastic.protobuf.ModuleConfig.TelemetryConfig\x12M\n\x0e\x63\x61nned_message\x18\x07 \x01(\x0b\x32\x35.meshtastic.protobuf.ModuleConfig.CannedMessageConfig\x12<\n\x05\x61udio\x18\t \x01(\x0b\x32-.meshtastic.protobuf.ModuleConfig.AudioConfig\x12O\n\x0fremote_hardware\x18\n \x01(\x0b\x32\x36.meshtastic.protobuf.ModuleConfig.RemoteHardwareConfig\x12K\n\rneighbor_info\x18\x0b \x01(\x0b\x32\x34.meshtastic.protobuf.ModuleConfig.NeighborInfoConfig\x12Q\n\x10\x61mbient_lighting\x18\x0c \x01(\x0b\x32\x37.meshtastic.protobuf.ModuleConfig.AmbientLightingConfig\x12Q\n\x10\x64\x65tection_sensor\x18\r \x01(\x0b\x32\x37.meshtastic.protobuf.ModuleConfig.DetectionSensorConfig\x12\x46\n\npaxcounter\x18\x0e \x01(\x0b\x32\x32.meshtastic.protobuf.ModuleConfig.PaxcounterConfig\x12\x0f\n\x07version\x18\x08 \x01(\rBd\n\x13\x63om.geeksville.meshB\x0fLocalOnlyProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.localonly_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\017LocalOnlyProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' _globals['_LOCALCONFIG']._serialized_start=136 _globals['_LOCALCONFIG']._serialized_end=580 _globals['_LOCALMODULECONFIG']._serialized_start=583 _globals['_LOCALMODULECONFIG']._serialized_end=1591 # @@protoc_insertion_point(module_scope) python-2.3.14/meshtastic/protobuf/localonly_pb2.pyi000066400000000000000000000230551464266072200224260ustar00rootroot00000000000000""" @generated by mypy-protobuf. Do not edit manually! isort:skip_file """ import builtins import google.protobuf.descriptor import google.protobuf.message import meshtastic.protobuf.config_pb2 import meshtastic.protobuf.module_config_pb2 import typing DESCRIPTOR: google.protobuf.descriptor.FileDescriptor @typing.final class LocalConfig(google.protobuf.message.Message): """ Protobuf structures common to apponly.proto and deviceonly.proto This is never sent over the wire, only for local use """ DESCRIPTOR: google.protobuf.descriptor.Descriptor DEVICE_FIELD_NUMBER: builtins.int POSITION_FIELD_NUMBER: builtins.int POWER_FIELD_NUMBER: builtins.int NETWORK_FIELD_NUMBER: builtins.int DISPLAY_FIELD_NUMBER: builtins.int LORA_FIELD_NUMBER: builtins.int BLUETOOTH_FIELD_NUMBER: builtins.int VERSION_FIELD_NUMBER: builtins.int version: builtins.int """ A version integer used to invalidate old save files when we make incompatible changes This integer is set at build time and is private to NodeDB.cpp in the device code. """ @property def device(self) -> meshtastic.protobuf.config_pb2.Config.DeviceConfig: """ The part of the config that is specific to the Device """ @property def position(self) -> meshtastic.protobuf.config_pb2.Config.PositionConfig: """ The part of the config that is specific to the GPS Position """ @property def power(self) -> meshtastic.protobuf.config_pb2.Config.PowerConfig: """ The part of the config that is specific to the Power settings """ @property def network(self) -> meshtastic.protobuf.config_pb2.Config.NetworkConfig: """ The part of the config that is specific to the Wifi Settings """ @property def display(self) -> meshtastic.protobuf.config_pb2.Config.DisplayConfig: """ The part of the config that is specific to the Display """ @property def lora(self) -> meshtastic.protobuf.config_pb2.Config.LoRaConfig: """ The part of the config that is specific to the Lora Radio """ @property def bluetooth(self) -> meshtastic.protobuf.config_pb2.Config.BluetoothConfig: """ The part of the config that is specific to the Bluetooth settings """ def __init__( self, *, device: meshtastic.protobuf.config_pb2.Config.DeviceConfig | None = ..., position: meshtastic.protobuf.config_pb2.Config.PositionConfig | None = ..., power: meshtastic.protobuf.config_pb2.Config.PowerConfig | None = ..., network: meshtastic.protobuf.config_pb2.Config.NetworkConfig | None = ..., display: meshtastic.protobuf.config_pb2.Config.DisplayConfig | None = ..., lora: meshtastic.protobuf.config_pb2.Config.LoRaConfig | None = ..., bluetooth: meshtastic.protobuf.config_pb2.Config.BluetoothConfig | None = ..., version: builtins.int = ..., ) -> None: ... def HasField(self, field_name: typing.Literal["bluetooth", b"bluetooth", "device", b"device", "display", b"display", "lora", b"lora", "network", b"network", "position", b"position", "power", b"power"]) -> builtins.bool: ... def ClearField(self, field_name: typing.Literal["bluetooth", b"bluetooth", "device", b"device", "display", b"display", "lora", b"lora", "network", b"network", "position", b"position", "power", b"power", "version", b"version"]) -> None: ... global___LocalConfig = LocalConfig @typing.final class LocalModuleConfig(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor MQTT_FIELD_NUMBER: builtins.int SERIAL_FIELD_NUMBER: builtins.int EXTERNAL_NOTIFICATION_FIELD_NUMBER: builtins.int STORE_FORWARD_FIELD_NUMBER: builtins.int RANGE_TEST_FIELD_NUMBER: builtins.int TELEMETRY_FIELD_NUMBER: builtins.int CANNED_MESSAGE_FIELD_NUMBER: builtins.int AUDIO_FIELD_NUMBER: builtins.int REMOTE_HARDWARE_FIELD_NUMBER: builtins.int NEIGHBOR_INFO_FIELD_NUMBER: builtins.int AMBIENT_LIGHTING_FIELD_NUMBER: builtins.int DETECTION_SENSOR_FIELD_NUMBER: builtins.int PAXCOUNTER_FIELD_NUMBER: builtins.int VERSION_FIELD_NUMBER: builtins.int version: builtins.int """ A version integer used to invalidate old save files when we make incompatible changes This integer is set at build time and is private to NodeDB.cpp in the device code. """ @property def mqtt(self) -> meshtastic.protobuf.module_config_pb2.ModuleConfig.MQTTConfig: """ The part of the config that is specific to the MQTT module """ @property def serial(self) -> meshtastic.protobuf.module_config_pb2.ModuleConfig.SerialConfig: """ The part of the config that is specific to the Serial module """ @property def external_notification(self) -> meshtastic.protobuf.module_config_pb2.ModuleConfig.ExternalNotificationConfig: """ The part of the config that is specific to the ExternalNotification module """ @property def store_forward(self) -> meshtastic.protobuf.module_config_pb2.ModuleConfig.StoreForwardConfig: """ The part of the config that is specific to the Store & Forward module """ @property def range_test(self) -> meshtastic.protobuf.module_config_pb2.ModuleConfig.RangeTestConfig: """ The part of the config that is specific to the RangeTest module """ @property def telemetry(self) -> meshtastic.protobuf.module_config_pb2.ModuleConfig.TelemetryConfig: """ The part of the config that is specific to the Telemetry module """ @property def canned_message(self) -> meshtastic.protobuf.module_config_pb2.ModuleConfig.CannedMessageConfig: """ The part of the config that is specific to the Canned Message module """ @property def audio(self) -> meshtastic.protobuf.module_config_pb2.ModuleConfig.AudioConfig: """ The part of the config that is specific to the Audio module """ @property def remote_hardware(self) -> meshtastic.protobuf.module_config_pb2.ModuleConfig.RemoteHardwareConfig: """ The part of the config that is specific to the Remote Hardware module """ @property def neighbor_info(self) -> meshtastic.protobuf.module_config_pb2.ModuleConfig.NeighborInfoConfig: """ The part of the config that is specific to the Neighbor Info module """ @property def ambient_lighting(self) -> meshtastic.protobuf.module_config_pb2.ModuleConfig.AmbientLightingConfig: """ The part of the config that is specific to the Ambient Lighting module """ @property def detection_sensor(self) -> meshtastic.protobuf.module_config_pb2.ModuleConfig.DetectionSensorConfig: """ The part of the config that is specific to the Detection Sensor module """ @property def paxcounter(self) -> meshtastic.protobuf.module_config_pb2.ModuleConfig.PaxcounterConfig: """ Paxcounter Config """ def __init__( self, *, mqtt: meshtastic.protobuf.module_config_pb2.ModuleConfig.MQTTConfig | None = ..., serial: meshtastic.protobuf.module_config_pb2.ModuleConfig.SerialConfig | None = ..., external_notification: meshtastic.protobuf.module_config_pb2.ModuleConfig.ExternalNotificationConfig | None = ..., store_forward: meshtastic.protobuf.module_config_pb2.ModuleConfig.StoreForwardConfig | None = ..., range_test: meshtastic.protobuf.module_config_pb2.ModuleConfig.RangeTestConfig | None = ..., telemetry: meshtastic.protobuf.module_config_pb2.ModuleConfig.TelemetryConfig | None = ..., canned_message: meshtastic.protobuf.module_config_pb2.ModuleConfig.CannedMessageConfig | None = ..., audio: meshtastic.protobuf.module_config_pb2.ModuleConfig.AudioConfig | None = ..., remote_hardware: meshtastic.protobuf.module_config_pb2.ModuleConfig.RemoteHardwareConfig | None = ..., neighbor_info: meshtastic.protobuf.module_config_pb2.ModuleConfig.NeighborInfoConfig | None = ..., ambient_lighting: meshtastic.protobuf.module_config_pb2.ModuleConfig.AmbientLightingConfig | None = ..., detection_sensor: meshtastic.protobuf.module_config_pb2.ModuleConfig.DetectionSensorConfig | None = ..., paxcounter: meshtastic.protobuf.module_config_pb2.ModuleConfig.PaxcounterConfig | None = ..., version: builtins.int = ..., ) -> None: ... def HasField(self, field_name: typing.Literal["ambient_lighting", b"ambient_lighting", "audio", b"audio", "canned_message", b"canned_message", "detection_sensor", b"detection_sensor", "external_notification", b"external_notification", "mqtt", b"mqtt", "neighbor_info", b"neighbor_info", "paxcounter", b"paxcounter", "range_test", b"range_test", "remote_hardware", b"remote_hardware", "serial", b"serial", "store_forward", b"store_forward", "telemetry", b"telemetry"]) -> builtins.bool: ... def ClearField(self, field_name: typing.Literal["ambient_lighting", b"ambient_lighting", "audio", b"audio", "canned_message", b"canned_message", "detection_sensor", b"detection_sensor", "external_notification", b"external_notification", "mqtt", b"mqtt", "neighbor_info", b"neighbor_info", "paxcounter", b"paxcounter", "range_test", b"range_test", "remote_hardware", b"remote_hardware", "serial", b"serial", "store_forward", b"store_forward", "telemetry", b"telemetry", "version", b"version"]) -> None: ... global___LocalModuleConfig = LocalModuleConfig python-2.3.14/meshtastic/protobuf/mesh_pb2.py000066400000000000000000000420651464266072200212170ustar00rootroot00000000000000# -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # source: meshtastic/protobuf/mesh.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 symbol_database as _symbol_database from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() from meshtastic.protobuf import channel_pb2 as meshtastic_dot_protobuf_dot_channel__pb2 from meshtastic.protobuf import config_pb2 as meshtastic_dot_protobuf_dot_config__pb2 from meshtastic.protobuf import module_config_pb2 as meshtastic_dot_protobuf_dot_module__config__pb2 from meshtastic.protobuf import portnums_pb2 as meshtastic_dot_protobuf_dot_portnums__pb2 from meshtastic.protobuf import telemetry_pb2 as meshtastic_dot_protobuf_dot_telemetry__pb2 from meshtastic.protobuf import xmodem_pb2 as meshtastic_dot_protobuf_dot_xmodem__pb2 DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1emeshtastic/protobuf/mesh.proto\x12\x13meshtastic.protobuf\x1a!meshtastic/protobuf/channel.proto\x1a meshtastic/protobuf/config.proto\x1a\'meshtastic/protobuf/module_config.proto\x1a\"meshtastic/protobuf/portnums.proto\x1a#meshtastic/protobuf/telemetry.proto\x1a meshtastic/protobuf/xmodem.proto\"\xf7\x05\n\x08Position\x12\x12\n\nlatitude_i\x18\x01 \x01(\x0f\x12\x13\n\x0blongitude_i\x18\x02 \x01(\x0f\x12\x10\n\x08\x61ltitude\x18\x03 \x01(\x05\x12\x0c\n\x04time\x18\x04 \x01(\x07\x12@\n\x0flocation_source\x18\x05 \x01(\x0e\x32\'.meshtastic.protobuf.Position.LocSource\x12@\n\x0f\x61ltitude_source\x18\x06 \x01(\x0e\x32\'.meshtastic.protobuf.Position.AltSource\x12\x11\n\ttimestamp\x18\x07 \x01(\x07\x12\x1f\n\x17timestamp_millis_adjust\x18\x08 \x01(\x05\x12\x14\n\x0c\x61ltitude_hae\x18\t \x01(\x11\x12#\n\x1b\x61ltitude_geoidal_separation\x18\n \x01(\x11\x12\x0c\n\x04PDOP\x18\x0b \x01(\r\x12\x0c\n\x04HDOP\x18\x0c \x01(\r\x12\x0c\n\x04VDOP\x18\r \x01(\r\x12\x14\n\x0cgps_accuracy\x18\x0e \x01(\r\x12\x14\n\x0cground_speed\x18\x0f \x01(\r\x12\x14\n\x0cground_track\x18\x10 \x01(\r\x12\x13\n\x0b\x66ix_quality\x18\x11 \x01(\r\x12\x10\n\x08\x66ix_type\x18\x12 \x01(\r\x12\x14\n\x0csats_in_view\x18\x13 \x01(\r\x12\x11\n\tsensor_id\x18\x14 \x01(\r\x12\x13\n\x0bnext_update\x18\x15 \x01(\r\x12\x12\n\nseq_number\x18\x16 \x01(\r\x12\x16\n\x0eprecision_bits\x18\x17 \x01(\r\"N\n\tLocSource\x12\r\n\tLOC_UNSET\x10\x00\x12\x0e\n\nLOC_MANUAL\x10\x01\x12\x10\n\x0cLOC_INTERNAL\x10\x02\x12\x10\n\x0cLOC_EXTERNAL\x10\x03\"b\n\tAltSource\x12\r\n\tALT_UNSET\x10\x00\x12\x0e\n\nALT_MANUAL\x10\x01\x12\x10\n\x0c\x41LT_INTERNAL\x10\x02\x12\x10\n\x0c\x41LT_EXTERNAL\x10\x03\x12\x12\n\x0e\x41LT_BAROMETRIC\x10\x04\"\xd6\x01\n\x04User\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\tlong_name\x18\x02 \x01(\t\x12\x12\n\nshort_name\x18\x03 \x01(\t\x12\x13\n\x07macaddr\x18\x04 \x01(\x0c\x42\x02\x18\x01\x12\x34\n\x08hw_model\x18\x05 \x01(\x0e\x32\".meshtastic.protobuf.HardwareModel\x12\x13\n\x0bis_licensed\x18\x06 \x01(\x08\x12;\n\x04role\x18\x07 \x01(\x0e\x32-.meshtastic.protobuf.Config.DeviceConfig.Role\"\x1f\n\x0eRouteDiscovery\x12\r\n\x05route\x18\x01 \x03(\x07\"\x97\x03\n\x07Routing\x12<\n\rroute_request\x18\x01 \x01(\x0b\x32#.meshtastic.protobuf.RouteDiscoveryH\x00\x12:\n\x0broute_reply\x18\x02 \x01(\x0b\x32#.meshtastic.protobuf.RouteDiscoveryH\x00\x12:\n\x0c\x65rror_reason\x18\x03 \x01(\x0e\x32\".meshtastic.protobuf.Routing.ErrorH\x00\"\xca\x01\n\x05\x45rror\x12\x08\n\x04NONE\x10\x00\x12\x0c\n\x08NO_ROUTE\x10\x01\x12\x0b\n\x07GOT_NAK\x10\x02\x12\x0b\n\x07TIMEOUT\x10\x03\x12\x10\n\x0cNO_INTERFACE\x10\x04\x12\x12\n\x0eMAX_RETRANSMIT\x10\x05\x12\x0e\n\nNO_CHANNEL\x10\x06\x12\r\n\tTOO_LARGE\x10\x07\x12\x0f\n\x0bNO_RESPONSE\x10\x08\x12\x14\n\x10\x44UTY_CYCLE_LIMIT\x10\t\x12\x0f\n\x0b\x42\x41\x44_REQUEST\x10 \x12\x12\n\x0eNOT_AUTHORIZED\x10!B\t\n\x07variant\"\xb0\x01\n\x04\x44\x61ta\x12-\n\x07portnum\x18\x01 \x01(\x0e\x32\x1c.meshtastic.protobuf.PortNum\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\x12\x15\n\rwant_response\x18\x03 \x01(\x08\x12\x0c\n\x04\x64\x65st\x18\x04 \x01(\x07\x12\x0e\n\x06source\x18\x05 \x01(\x07\x12\x12\n\nrequest_id\x18\x06 \x01(\x07\x12\x10\n\x08reply_id\x18\x07 \x01(\x07\x12\r\n\x05\x65moji\x18\x08 \x01(\x07\"\x93\x01\n\x08Waypoint\x12\n\n\x02id\x18\x01 \x01(\r\x12\x12\n\nlatitude_i\x18\x02 \x01(\x0f\x12\x13\n\x0blongitude_i\x18\x03 \x01(\x0f\x12\x0e\n\x06\x65xpire\x18\x04 \x01(\r\x12\x11\n\tlocked_to\x18\x05 \x01(\r\x12\x0c\n\x04name\x18\x06 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x07 \x01(\t\x12\x0c\n\x04icon\x18\x08 \x01(\x07\"l\n\x16MqttClientProxyMessage\x12\r\n\x05topic\x18\x01 \x01(\t\x12\x0e\n\x04\x64\x61ta\x18\x02 \x01(\x0cH\x00\x12\x0e\n\x04text\x18\x03 \x01(\tH\x00\x12\x10\n\x08retained\x18\x04 \x01(\x08\x42\x11\n\x0fpayload_variant\"\xb0\x04\n\nMeshPacket\x12\x0c\n\x04\x66rom\x18\x01 \x01(\x07\x12\n\n\x02to\x18\x02 \x01(\x07\x12\x0f\n\x07\x63hannel\x18\x03 \x01(\r\x12,\n\x07\x64\x65\x63oded\x18\x04 \x01(\x0b\x32\x19.meshtastic.protobuf.DataH\x00\x12\x13\n\tencrypted\x18\x05 \x01(\x0cH\x00\x12\n\n\x02id\x18\x06 \x01(\x07\x12\x0f\n\x07rx_time\x18\x07 \x01(\x07\x12\x0e\n\x06rx_snr\x18\x08 \x01(\x02\x12\x11\n\thop_limit\x18\t \x01(\r\x12\x10\n\x08want_ack\x18\n \x01(\x08\x12:\n\x08priority\x18\x0b \x01(\x0e\x32(.meshtastic.protobuf.MeshPacket.Priority\x12\x0f\n\x07rx_rssi\x18\x0c \x01(\x05\x12<\n\x07\x64\x65layed\x18\r \x01(\x0e\x32\'.meshtastic.protobuf.MeshPacket.DelayedB\x02\x18\x01\x12\x10\n\x08via_mqtt\x18\x0e \x01(\x08\x12\x11\n\thop_start\x18\x0f \x01(\r\"[\n\x08Priority\x12\t\n\x05UNSET\x10\x00\x12\x07\n\x03MIN\x10\x01\x12\x0e\n\nBACKGROUND\x10\n\x12\x0b\n\x07\x44\x45\x46\x41ULT\x10@\x12\x0c\n\x08RELIABLE\x10\x46\x12\x07\n\x03\x41\x43K\x10x\x12\x07\n\x03MAX\x10\x7f\"B\n\x07\x44\x65layed\x12\x0c\n\x08NO_DELAY\x10\x00\x12\x15\n\x11\x44\x45LAYED_BROADCAST\x10\x01\x12\x12\n\x0e\x44\x45LAYED_DIRECT\x10\x02\x42\x11\n\x0fpayload_variant\"\x99\x02\n\x08NodeInfo\x12\x0b\n\x03num\x18\x01 \x01(\r\x12\'\n\x04user\x18\x02 \x01(\x0b\x32\x19.meshtastic.protobuf.User\x12/\n\x08position\x18\x03 \x01(\x0b\x32\x1d.meshtastic.protobuf.Position\x12\x0b\n\x03snr\x18\x04 \x01(\x02\x12\x12\n\nlast_heard\x18\x05 \x01(\x07\x12:\n\x0e\x64\x65vice_metrics\x18\x06 \x01(\x0b\x32\".meshtastic.protobuf.DeviceMetrics\x12\x0f\n\x07\x63hannel\x18\x07 \x01(\r\x12\x10\n\x08via_mqtt\x18\x08 \x01(\x08\x12\x11\n\thops_away\x18\t \x01(\r\x12\x13\n\x0bis_favorite\x18\n \x01(\x08\"P\n\nMyNodeInfo\x12\x13\n\x0bmy_node_num\x18\x01 \x01(\r\x12\x14\n\x0creboot_count\x18\x08 \x01(\r\x12\x17\n\x0fmin_app_version\x18\x0b \x01(\r\"\xc9\x01\n\tLogRecord\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\x0c\n\x04time\x18\x02 \x01(\x07\x12\x0e\n\x06source\x18\x03 \x01(\t\x12\x33\n\x05level\x18\x04 \x01(\x0e\x32$.meshtastic.protobuf.LogRecord.Level\"X\n\x05Level\x12\t\n\x05UNSET\x10\x00\x12\x0c\n\x08\x43RITICAL\x10\x32\x12\t\n\x05\x45RROR\x10(\x12\x0b\n\x07WARNING\x10\x1e\x12\x08\n\x04INFO\x10\x14\x12\t\n\x05\x44\x45\x42UG\x10\n\x12\t\n\x05TRACE\x10\x05\"P\n\x0bQueueStatus\x12\x0b\n\x03res\x18\x01 \x01(\x05\x12\x0c\n\x04\x66ree\x18\x02 \x01(\r\x12\x0e\n\x06maxlen\x18\x03 \x01(\r\x12\x16\n\x0emesh_packet_id\x18\x04 \x01(\r\"\xbe\x05\n\tFromRadio\x12\n\n\x02id\x18\x01 \x01(\r\x12\x31\n\x06packet\x18\x02 \x01(\x0b\x32\x1f.meshtastic.protobuf.MeshPacketH\x00\x12\x32\n\x07my_info\x18\x03 \x01(\x0b\x32\x1f.meshtastic.protobuf.MyNodeInfoH\x00\x12\x32\n\tnode_info\x18\x04 \x01(\x0b\x32\x1d.meshtastic.protobuf.NodeInfoH\x00\x12-\n\x06\x63onfig\x18\x05 \x01(\x0b\x32\x1b.meshtastic.protobuf.ConfigH\x00\x12\x34\n\nlog_record\x18\x06 \x01(\x0b\x32\x1e.meshtastic.protobuf.LogRecordH\x00\x12\x1c\n\x12\x63onfig_complete_id\x18\x07 \x01(\rH\x00\x12\x12\n\x08rebooted\x18\x08 \x01(\x08H\x00\x12\x39\n\x0cmoduleConfig\x18\t \x01(\x0b\x32!.meshtastic.protobuf.ModuleConfigH\x00\x12/\n\x07\x63hannel\x18\n \x01(\x0b\x32\x1c.meshtastic.protobuf.ChannelH\x00\x12\x37\n\x0bqueueStatus\x18\x0b \x01(\x0b\x32 .meshtastic.protobuf.QueueStatusH\x00\x12\x33\n\x0cxmodemPacket\x18\x0c \x01(\x0b\x32\x1b.meshtastic.protobuf.XModemH\x00\x12\x37\n\x08metadata\x18\r \x01(\x0b\x32#.meshtastic.protobuf.DeviceMetadataH\x00\x12M\n\x16mqttClientProxyMessage\x18\x0e \x01(\x0b\x32+.meshtastic.protobuf.MqttClientProxyMessageH\x00\x42\x11\n\x0fpayload_variant\"\xb8\x02\n\x07ToRadio\x12\x31\n\x06packet\x18\x01 \x01(\x0b\x32\x1f.meshtastic.protobuf.MeshPacketH\x00\x12\x18\n\x0ewant_config_id\x18\x03 \x01(\rH\x00\x12\x14\n\ndisconnect\x18\x04 \x01(\x08H\x00\x12\x33\n\x0cxmodemPacket\x18\x05 \x01(\x0b\x32\x1b.meshtastic.protobuf.XModemH\x00\x12M\n\x16mqttClientProxyMessage\x18\x06 \x01(\x0b\x32+.meshtastic.protobuf.MqttClientProxyMessageH\x00\x12\x33\n\theartbeat\x18\x07 \x01(\x0b\x32\x1e.meshtastic.protobuf.HeartbeatH\x00\x42\x11\n\x0fpayload_variant\"I\n\nCompressed\x12-\n\x07portnum\x18\x01 \x01(\x0e\x32\x1c.meshtastic.protobuf.PortNum\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\"\x90\x01\n\x0cNeighborInfo\x12\x0f\n\x07node_id\x18\x01 \x01(\r\x12\x17\n\x0flast_sent_by_id\x18\x02 \x01(\r\x12$\n\x1cnode_broadcast_interval_secs\x18\x03 \x01(\r\x12\x30\n\tneighbors\x18\x04 \x03(\x0b\x32\x1d.meshtastic.protobuf.Neighbor\"d\n\x08Neighbor\x12\x0f\n\x07node_id\x18\x01 \x01(\r\x12\x0b\n\x03snr\x18\x02 \x01(\x02\x12\x14\n\x0clast_rx_time\x18\x03 \x01(\x07\x12$\n\x1cnode_broadcast_interval_secs\x18\x04 \x01(\r\"\xbf\x02\n\x0e\x44\x65viceMetadata\x12\x18\n\x10\x66irmware_version\x18\x01 \x01(\t\x12\x1c\n\x14\x64\x65vice_state_version\x18\x02 \x01(\r\x12\x13\n\x0b\x63\x61nShutdown\x18\x03 \x01(\x08\x12\x0f\n\x07hasWifi\x18\x04 \x01(\x08\x12\x14\n\x0chasBluetooth\x18\x05 \x01(\x08\x12\x13\n\x0bhasEthernet\x18\x06 \x01(\x08\x12;\n\x04role\x18\x07 \x01(\x0e\x32-.meshtastic.protobuf.Config.DeviceConfig.Role\x12\x16\n\x0eposition_flags\x18\x08 \x01(\r\x12\x34\n\x08hw_model\x18\t \x01(\x0e\x32\".meshtastic.protobuf.HardwareModel\x12\x19\n\x11hasRemoteHardware\x18\n \x01(\x08\"\x0b\n\tHeartbeat\"^\n\x15NodeRemoteHardwarePin\x12\x10\n\x08node_num\x18\x01 \x01(\r\x12\x33\n\x03pin\x18\x02 \x01(\x0b\x32&.meshtastic.protobuf.RemoteHardwarePin\"e\n\x0e\x43hunkedPayload\x12\x12\n\npayload_id\x18\x01 \x01(\r\x12\x13\n\x0b\x63hunk_count\x18\x02 \x01(\r\x12\x13\n\x0b\x63hunk_index\x18\x03 \x01(\r\x12\x15\n\rpayload_chunk\x18\x04 \x01(\x0c\"\x1f\n\rresend_chunks\x12\x0e\n\x06\x63hunks\x18\x01 \x03(\r\"\xb3\x01\n\x16\x43hunkedPayloadResponse\x12\x12\n\npayload_id\x18\x01 \x01(\r\x12\x1a\n\x10request_transfer\x18\x02 \x01(\x08H\x00\x12\x19\n\x0f\x61\x63\x63\x65pt_transfer\x18\x03 \x01(\x08H\x00\x12;\n\rresend_chunks\x18\x04 \x01(\x0b\x32\".meshtastic.protobuf.resend_chunksH\x00\x42\x11\n\x0fpayload_variant*\xab\t\n\rHardwareModel\x12\t\n\x05UNSET\x10\x00\x12\x0c\n\x08TLORA_V2\x10\x01\x12\x0c\n\x08TLORA_V1\x10\x02\x12\x12\n\x0eTLORA_V2_1_1P6\x10\x03\x12\t\n\x05TBEAM\x10\x04\x12\x0f\n\x0bHELTEC_V2_0\x10\x05\x12\x0e\n\nTBEAM_V0P7\x10\x06\x12\n\n\x06T_ECHO\x10\x07\x12\x10\n\x0cTLORA_V1_1P3\x10\x08\x12\x0b\n\x07RAK4631\x10\t\x12\x0f\n\x0bHELTEC_V2_1\x10\n\x12\r\n\tHELTEC_V1\x10\x0b\x12\x18\n\x14LILYGO_TBEAM_S3_CORE\x10\x0c\x12\x0c\n\x08RAK11200\x10\r\x12\x0b\n\x07NANO_G1\x10\x0e\x12\x12\n\x0eTLORA_V2_1_1P8\x10\x0f\x12\x0f\n\x0bTLORA_T3_S3\x10\x10\x12\x14\n\x10NANO_G1_EXPLORER\x10\x11\x12\x11\n\rNANO_G2_ULTRA\x10\x12\x12\r\n\tLORA_TYPE\x10\x13\x12\x0b\n\x07WIPHONE\x10\x14\x12\x0e\n\nWIO_WM1110\x10\x15\x12\x0b\n\x07RAK2560\x10\x16\x12\x13\n\x0fHELTEC_HRU_3601\x10\x17\x12\x0e\n\nSTATION_G1\x10\x19\x12\x0c\n\x08RAK11310\x10\x1a\x12\x14\n\x10SENSELORA_RP2040\x10\x1b\x12\x10\n\x0cSENSELORA_S3\x10\x1c\x12\r\n\tCANARYONE\x10\x1d\x12\x0f\n\x0bRP2040_LORA\x10\x1e\x12\x0e\n\nSTATION_G2\x10\x1f\x12\x11\n\rLORA_RELAY_V1\x10 \x12\x0e\n\nNRF52840DK\x10!\x12\x07\n\x03PPR\x10\"\x12\x0f\n\x0bGENIEBLOCKS\x10#\x12\x11\n\rNRF52_UNKNOWN\x10$\x12\r\n\tPORTDUINO\x10%\x12\x0f\n\x0b\x41NDROID_SIM\x10&\x12\n\n\x06\x44IY_V1\x10\'\x12\x15\n\x11NRF52840_PCA10059\x10(\x12\n\n\x06\x44R_DEV\x10)\x12\x0b\n\x07M5STACK\x10*\x12\r\n\tHELTEC_V3\x10+\x12\x11\n\rHELTEC_WSL_V3\x10,\x12\x13\n\x0f\x42\x45TAFPV_2400_TX\x10-\x12\x17\n\x13\x42\x45TAFPV_900_NANO_TX\x10.\x12\x0c\n\x08RPI_PICO\x10/\x12\x1b\n\x17HELTEC_WIRELESS_TRACKER\x10\x30\x12\x19\n\x15HELTEC_WIRELESS_PAPER\x10\x31\x12\n\n\x06T_DECK\x10\x32\x12\x0e\n\nT_WATCH_S3\x10\x33\x12\x11\n\rPICOMPUTER_S3\x10\x34\x12\x0f\n\x0bHELTEC_HT62\x10\x35\x12\x12\n\x0e\x45\x42YTE_ESP32_S3\x10\x36\x12\x11\n\rESP32_S3_PICO\x10\x37\x12\r\n\tCHATTER_2\x10\x38\x12\x1e\n\x1aHELTEC_WIRELESS_PAPER_V1_0\x10\x39\x12 \n\x1cHELTEC_WIRELESS_TRACKER_V1_0\x10:\x12\x0b\n\x07UNPHONE\x10;\x12\x0c\n\x08TD_LORAC\x10<\x12\x13\n\x0f\x43\x44\x45\x42YTE_EORA_S3\x10=\x12\x0f\n\x0bTWC_MESH_V4\x10>\x12\x16\n\x12NRF52_PROMICRO_DIY\x10?\x12\x1f\n\x1bRADIOMASTER_900_BANDIT_NANO\x10@\x12\x1c\n\x18HELTEC_CAPSULE_SENSOR_V3\x10\x41\x12\x0f\n\nPRIVATE_HW\x10\xff\x01*,\n\tConstants\x12\x08\n\x04ZERO\x10\x00\x12\x15\n\x10\x44\x41TA_PAYLOAD_LEN\x10\xed\x01*\xee\x01\n\x11\x43riticalErrorCode\x12\x08\n\x04NONE\x10\x00\x12\x0f\n\x0bTX_WATCHDOG\x10\x01\x12\x14\n\x10SLEEP_ENTER_WAIT\x10\x02\x12\x0c\n\x08NO_RADIO\x10\x03\x12\x0f\n\x0bUNSPECIFIED\x10\x04\x12\x15\n\x11UBLOX_UNIT_FAILED\x10\x05\x12\r\n\tNO_AXP192\x10\x06\x12\x19\n\x15INVALID_RADIO_SETTING\x10\x07\x12\x13\n\x0fTRANSMIT_FAILED\x10\x08\x12\x0c\n\x08\x42ROWNOUT\x10\t\x12\x12\n\x0eSX1262_FAILURE\x10\n\x12\x11\n\rRADIO_SPI_BUG\x10\x0b\x42_\n\x13\x63om.geeksville.meshB\nMeshProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.mesh_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\nMeshProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' _USER.fields_by_name['macaddr']._options = None _USER.fields_by_name['macaddr']._serialized_options = b'\030\001' _MESHPACKET.fields_by_name['delayed']._options = None _MESHPACKET.fields_by_name['delayed']._serialized_options = b'\030\001' _globals['_HARDWAREMODEL']._serialized_start=5442 _globals['_HARDWAREMODEL']._serialized_end=6637 _globals['_CONSTANTS']._serialized_start=6639 _globals['_CONSTANTS']._serialized_end=6683 _globals['_CRITICALERRORCODE']._serialized_start=6686 _globals['_CRITICALERRORCODE']._serialized_end=6924 _globals['_POSITION']._serialized_start=273 _globals['_POSITION']._serialized_end=1032 _globals['_POSITION_LOCSOURCE']._serialized_start=854 _globals['_POSITION_LOCSOURCE']._serialized_end=932 _globals['_POSITION_ALTSOURCE']._serialized_start=934 _globals['_POSITION_ALTSOURCE']._serialized_end=1032 _globals['_USER']._serialized_start=1035 _globals['_USER']._serialized_end=1249 _globals['_ROUTEDISCOVERY']._serialized_start=1251 _globals['_ROUTEDISCOVERY']._serialized_end=1282 _globals['_ROUTING']._serialized_start=1285 _globals['_ROUTING']._serialized_end=1692 _globals['_ROUTING_ERROR']._serialized_start=1479 _globals['_ROUTING_ERROR']._serialized_end=1681 _globals['_DATA']._serialized_start=1695 _globals['_DATA']._serialized_end=1871 _globals['_WAYPOINT']._serialized_start=1874 _globals['_WAYPOINT']._serialized_end=2021 _globals['_MQTTCLIENTPROXYMESSAGE']._serialized_start=2023 _globals['_MQTTCLIENTPROXYMESSAGE']._serialized_end=2131 _globals['_MESHPACKET']._serialized_start=2134 _globals['_MESHPACKET']._serialized_end=2694 _globals['_MESHPACKET_PRIORITY']._serialized_start=2516 _globals['_MESHPACKET_PRIORITY']._serialized_end=2607 _globals['_MESHPACKET_DELAYED']._serialized_start=2609 _globals['_MESHPACKET_DELAYED']._serialized_end=2675 _globals['_NODEINFO']._serialized_start=2697 _globals['_NODEINFO']._serialized_end=2978 _globals['_MYNODEINFO']._serialized_start=2980 _globals['_MYNODEINFO']._serialized_end=3060 _globals['_LOGRECORD']._serialized_start=3063 _globals['_LOGRECORD']._serialized_end=3264 _globals['_LOGRECORD_LEVEL']._serialized_start=3176 _globals['_LOGRECORD_LEVEL']._serialized_end=3264 _globals['_QUEUESTATUS']._serialized_start=3266 _globals['_QUEUESTATUS']._serialized_end=3346 _globals['_FROMRADIO']._serialized_start=3349 _globals['_FROMRADIO']._serialized_end=4051 _globals['_TORADIO']._serialized_start=4054 _globals['_TORADIO']._serialized_end=4366 _globals['_COMPRESSED']._serialized_start=4368 _globals['_COMPRESSED']._serialized_end=4441 _globals['_NEIGHBORINFO']._serialized_start=4444 _globals['_NEIGHBORINFO']._serialized_end=4588 _globals['_NEIGHBOR']._serialized_start=4590 _globals['_NEIGHBOR']._serialized_end=4690 _globals['_DEVICEMETADATA']._serialized_start=4693 _globals['_DEVICEMETADATA']._serialized_end=5012 _globals['_HEARTBEAT']._serialized_start=5014 _globals['_HEARTBEAT']._serialized_end=5025 _globals['_NODEREMOTEHARDWAREPIN']._serialized_start=5027 _globals['_NODEREMOTEHARDWAREPIN']._serialized_end=5121 _globals['_CHUNKEDPAYLOAD']._serialized_start=5123 _globals['_CHUNKEDPAYLOAD']._serialized_end=5224 _globals['_RESEND_CHUNKS']._serialized_start=5226 _globals['_RESEND_CHUNKS']._serialized_end=5257 _globals['_CHUNKEDPAYLOADRESPONSE']._serialized_start=5260 _globals['_CHUNKEDPAYLOADRESPONSE']._serialized_end=5439 # @@protoc_insertion_point(module_scope) python-2.3.14/meshtastic/protobuf/mesh_pb2.pyi000066400000000000000000002640631464266072200213740ustar00rootroot00000000000000""" @generated by mypy-protobuf. Do not edit manually! isort:skip_file """ import builtins import collections.abc import google.protobuf.descriptor import google.protobuf.internal.containers import google.protobuf.internal.enum_type_wrapper import google.protobuf.message import meshtastic.protobuf.channel_pb2 import meshtastic.protobuf.config_pb2 import meshtastic.protobuf.module_config_pb2 import meshtastic.protobuf.portnums_pb2 import meshtastic.protobuf.telemetry_pb2 import meshtastic.protobuf.xmodem_pb2 import sys import typing if sys.version_info >= (3, 10): import typing as typing_extensions else: import typing_extensions DESCRIPTOR: google.protobuf.descriptor.FileDescriptor class _HardwareModel: ValueType = typing.NewType("ValueType", builtins.int) V: typing_extensions.TypeAlias = ValueType class _HardwareModelEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_HardwareModel.ValueType], builtins.type): DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor UNSET: _HardwareModel.ValueType # 0 """ TODO: REPLACE """ TLORA_V2: _HardwareModel.ValueType # 1 """ TODO: REPLACE """ TLORA_V1: _HardwareModel.ValueType # 2 """ TODO: REPLACE """ TLORA_V2_1_1P6: _HardwareModel.ValueType # 3 """ TODO: REPLACE """ TBEAM: _HardwareModel.ValueType # 4 """ TODO: REPLACE """ HELTEC_V2_0: _HardwareModel.ValueType # 5 """ The original heltec WiFi_Lora_32_V2, which had battery voltage sensing hooked to GPIO 13 (see HELTEC_V2 for the new version). """ TBEAM_V0P7: _HardwareModel.ValueType # 6 """ TODO: REPLACE """ T_ECHO: _HardwareModel.ValueType # 7 """ TODO: REPLACE """ TLORA_V1_1P3: _HardwareModel.ValueType # 8 """ TODO: REPLACE """ RAK4631: _HardwareModel.ValueType # 9 """ TODO: REPLACE """ HELTEC_V2_1: _HardwareModel.ValueType # 10 """ The new version of the heltec WiFi_Lora_32_V2 board that has battery sensing hooked to GPIO 37. Sadly they did not update anything on the silkscreen to identify this board """ HELTEC_V1: _HardwareModel.ValueType # 11 """ Ancient heltec WiFi_Lora_32 board """ LILYGO_TBEAM_S3_CORE: _HardwareModel.ValueType # 12 """ New T-BEAM with ESP32-S3 CPU """ RAK11200: _HardwareModel.ValueType # 13 """ RAK WisBlock ESP32 core: https://docs.rakwireless.com/Product-Categories/WisBlock/RAK11200/Overview/ """ NANO_G1: _HardwareModel.ValueType # 14 """ B&Q Consulting Nano Edition G1: https://uniteng.com/wiki/doku.php?id=meshtastic:nano """ TLORA_V2_1_1P8: _HardwareModel.ValueType # 15 """ TODO: REPLACE """ TLORA_T3_S3: _HardwareModel.ValueType # 16 """ TODO: REPLACE """ NANO_G1_EXPLORER: _HardwareModel.ValueType # 17 """ B&Q Consulting Nano G1 Explorer: https://wiki.uniteng.com/en/meshtastic/nano-g1-explorer """ NANO_G2_ULTRA: _HardwareModel.ValueType # 18 """ B&Q Consulting Nano G2 Ultra: https://wiki.uniteng.com/en/meshtastic/nano-g2-ultra """ LORA_TYPE: _HardwareModel.ValueType # 19 """ LoRAType device: https://loratype.org/ """ WIPHONE: _HardwareModel.ValueType # 20 """ wiphone https://www.wiphone.io/ """ WIO_WM1110: _HardwareModel.ValueType # 21 """ WIO Tracker WM1110 family from Seeed Studio. Includes wio-1110-tracker and wio-1110-sdk """ RAK2560: _HardwareModel.ValueType # 22 """ RAK2560 Solar base station based on RAK4630 """ HELTEC_HRU_3601: _HardwareModel.ValueType # 23 """ Heltec HRU-3601: https://heltec.org/project/hru-3601/ """ STATION_G1: _HardwareModel.ValueType # 25 """ B&Q Consulting Station Edition G1: https://uniteng.com/wiki/doku.php?id=meshtastic:station """ RAK11310: _HardwareModel.ValueType # 26 """ RAK11310 (RP2040 + SX1262) """ SENSELORA_RP2040: _HardwareModel.ValueType # 27 """ Makerfabs SenseLoRA Receiver (RP2040 + RFM96) """ SENSELORA_S3: _HardwareModel.ValueType # 28 """ Makerfabs SenseLoRA Industrial Monitor (ESP32-S3 + RFM96) """ CANARYONE: _HardwareModel.ValueType # 29 """ Canary Radio Company - CanaryOne: https://canaryradio.io/products/canaryone """ RP2040_LORA: _HardwareModel.ValueType # 30 """ Waveshare RP2040 LoRa - https://www.waveshare.com/rp2040-lora.htm """ STATION_G2: _HardwareModel.ValueType # 31 """ B&Q Consulting Station G2: https://wiki.uniteng.com/en/meshtastic/station-g2 """ LORA_RELAY_V1: _HardwareModel.ValueType # 32 """ --------------------------------------------------------------------------- Less common/prototype boards listed here (needs one more byte over the air) --------------------------------------------------------------------------- """ NRF52840DK: _HardwareModel.ValueType # 33 """ TODO: REPLACE """ PPR: _HardwareModel.ValueType # 34 """ TODO: REPLACE """ GENIEBLOCKS: _HardwareModel.ValueType # 35 """ TODO: REPLACE """ NRF52_UNKNOWN: _HardwareModel.ValueType # 36 """ TODO: REPLACE """ PORTDUINO: _HardwareModel.ValueType # 37 """ TODO: REPLACE """ ANDROID_SIM: _HardwareModel.ValueType # 38 """ The simulator built into the android app """ DIY_V1: _HardwareModel.ValueType # 39 """ Custom DIY device based on @NanoVHF schematics: https://github.com/NanoVHF/Meshtastic-DIY/tree/main/Schematics """ NRF52840_PCA10059: _HardwareModel.ValueType # 40 """ nRF52840 Dongle : https://www.nordicsemi.com/Products/Development-hardware/nrf52840-dongle/ """ DR_DEV: _HardwareModel.ValueType # 41 """ Custom Disaster Radio esp32 v3 device https://github.com/sudomesh/disaster-radio/tree/master/hardware/board_esp32_v3 """ M5STACK: _HardwareModel.ValueType # 42 """ M5 esp32 based MCU modules with enclosure, TFT and LORA Shields. All Variants (Basic, Core, Fire, Core2, Paper) https://m5stack.com/ """ HELTEC_V3: _HardwareModel.ValueType # 43 """ New Heltec LoRA32 with ESP32-S3 CPU """ HELTEC_WSL_V3: _HardwareModel.ValueType # 44 """ New Heltec Wireless Stick Lite with ESP32-S3 CPU """ BETAFPV_2400_TX: _HardwareModel.ValueType # 45 """ New BETAFPV ELRS Micro TX Module 2.4G with ESP32 CPU """ BETAFPV_900_NANO_TX: _HardwareModel.ValueType # 46 """ BetaFPV ExpressLRS "Nano" TX Module 900MHz with ESP32 CPU """ RPI_PICO: _HardwareModel.ValueType # 47 """ Raspberry Pi Pico (W) with Waveshare SX1262 LoRa Node Module """ HELTEC_WIRELESS_TRACKER: _HardwareModel.ValueType # 48 """ Heltec Wireless Tracker with ESP32-S3 CPU, built-in GPS, and TFT Newer V1.1, version is written on the PCB near the display. """ HELTEC_WIRELESS_PAPER: _HardwareModel.ValueType # 49 """ Heltec Wireless Paper with ESP32-S3 CPU and E-Ink display """ T_DECK: _HardwareModel.ValueType # 50 """ LilyGo T-Deck with ESP32-S3 CPU, Keyboard and IPS display """ T_WATCH_S3: _HardwareModel.ValueType # 51 """ LilyGo T-Watch S3 with ESP32-S3 CPU and IPS display """ PICOMPUTER_S3: _HardwareModel.ValueType # 52 """ Bobricius Picomputer with ESP32-S3 CPU, Keyboard and IPS display """ HELTEC_HT62: _HardwareModel.ValueType # 53 """ Heltec HT-CT62 with ESP32-C3 CPU and SX1262 LoRa """ EBYTE_ESP32_S3: _HardwareModel.ValueType # 54 """ EBYTE SPI LoRa module and ESP32-S3 """ ESP32_S3_PICO: _HardwareModel.ValueType # 55 """ Waveshare ESP32-S3-PICO with PICO LoRa HAT and 2.9inch e-Ink """ CHATTER_2: _HardwareModel.ValueType # 56 """ CircuitMess Chatter 2 LLCC68 Lora Module and ESP32 Wroom Lora module can be swapped out for a Heltec RA-62 which is "almost" pin compatible with one cut and one jumper Meshtastic works """ HELTEC_WIRELESS_PAPER_V1_0: _HardwareModel.ValueType # 57 """ Heltec Wireless Paper, With ESP32-S3 CPU and E-Ink display Older "V1.0" Variant, has no "version sticker" E-Ink model is DEPG0213BNS800 Tab on the screen protector is RED Flex connector marking is FPC-7528B """ HELTEC_WIRELESS_TRACKER_V1_0: _HardwareModel.ValueType # 58 """ Heltec Wireless Tracker with ESP32-S3 CPU, built-in GPS, and TFT Older "V1.0" Variant """ UNPHONE: _HardwareModel.ValueType # 59 """ unPhone with ESP32-S3, TFT touchscreen, LSM6DS3TR-C accelerometer and gyroscope """ TD_LORAC: _HardwareModel.ValueType # 60 """ Teledatics TD-LORAC NRF52840 based M.2 LoRA module Compatible with the TD-WRLS development board """ CDEBYTE_EORA_S3: _HardwareModel.ValueType # 61 """ CDEBYTE EoRa-S3 board using their own MM modules, clone of LILYGO T3S3 """ TWC_MESH_V4: _HardwareModel.ValueType # 62 """ TWC_MESH_V4 Adafruit NRF52840 feather express with SX1262, SSD1306 OLED and NEO6M GPS """ NRF52_PROMICRO_DIY: _HardwareModel.ValueType # 63 """ NRF52_PROMICRO_DIY Promicro NRF52840 with SX1262/LLCC68, SSD1306 OLED and NEO6M GPS """ RADIOMASTER_900_BANDIT_NANO: _HardwareModel.ValueType # 64 """ RadioMaster 900 Bandit Nano, https://www.radiomasterrc.com/products/bandit-nano-expresslrs-rf-module ESP32-D0WDQ6 With SX1276/SKY66122, SSD1306 OLED and No GPS """ HELTEC_CAPSULE_SENSOR_V3: _HardwareModel.ValueType # 65 """ Heltec Capsule Sensor V3 with ESP32-S3 CPU, Portable LoRa device that can replace GNSS modules or sensors """ PRIVATE_HW: _HardwareModel.ValueType # 255 """ ------------------------------------------------------------------------------------------------------------------------------------------ Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits. ------------------------------------------------------------------------------------------------------------------------------------------ """ class HardwareModel(_HardwareModel, metaclass=_HardwareModelEnumTypeWrapper): """ Note: these enum names must EXACTLY match the string used in the device bin/build-all.sh script. Because they will be used to find firmware filenames in the android app for OTA updates. To match the old style filenames, _ is converted to -, p is converted to . """ UNSET: HardwareModel.ValueType # 0 """ TODO: REPLACE """ TLORA_V2: HardwareModel.ValueType # 1 """ TODO: REPLACE """ TLORA_V1: HardwareModel.ValueType # 2 """ TODO: REPLACE """ TLORA_V2_1_1P6: HardwareModel.ValueType # 3 """ TODO: REPLACE """ TBEAM: HardwareModel.ValueType # 4 """ TODO: REPLACE """ HELTEC_V2_0: HardwareModel.ValueType # 5 """ The original heltec WiFi_Lora_32_V2, which had battery voltage sensing hooked to GPIO 13 (see HELTEC_V2 for the new version). """ TBEAM_V0P7: HardwareModel.ValueType # 6 """ TODO: REPLACE """ T_ECHO: HardwareModel.ValueType # 7 """ TODO: REPLACE """ TLORA_V1_1P3: HardwareModel.ValueType # 8 """ TODO: REPLACE """ RAK4631: HardwareModel.ValueType # 9 """ TODO: REPLACE """ HELTEC_V2_1: HardwareModel.ValueType # 10 """ The new version of the heltec WiFi_Lora_32_V2 board that has battery sensing hooked to GPIO 37. Sadly they did not update anything on the silkscreen to identify this board """ HELTEC_V1: HardwareModel.ValueType # 11 """ Ancient heltec WiFi_Lora_32 board """ LILYGO_TBEAM_S3_CORE: HardwareModel.ValueType # 12 """ New T-BEAM with ESP32-S3 CPU """ RAK11200: HardwareModel.ValueType # 13 """ RAK WisBlock ESP32 core: https://docs.rakwireless.com/Product-Categories/WisBlock/RAK11200/Overview/ """ NANO_G1: HardwareModel.ValueType # 14 """ B&Q Consulting Nano Edition G1: https://uniteng.com/wiki/doku.php?id=meshtastic:nano """ TLORA_V2_1_1P8: HardwareModel.ValueType # 15 """ TODO: REPLACE """ TLORA_T3_S3: HardwareModel.ValueType # 16 """ TODO: REPLACE """ NANO_G1_EXPLORER: HardwareModel.ValueType # 17 """ B&Q Consulting Nano G1 Explorer: https://wiki.uniteng.com/en/meshtastic/nano-g1-explorer """ NANO_G2_ULTRA: HardwareModel.ValueType # 18 """ B&Q Consulting Nano G2 Ultra: https://wiki.uniteng.com/en/meshtastic/nano-g2-ultra """ LORA_TYPE: HardwareModel.ValueType # 19 """ LoRAType device: https://loratype.org/ """ WIPHONE: HardwareModel.ValueType # 20 """ wiphone https://www.wiphone.io/ """ WIO_WM1110: HardwareModel.ValueType # 21 """ WIO Tracker WM1110 family from Seeed Studio. Includes wio-1110-tracker and wio-1110-sdk """ RAK2560: HardwareModel.ValueType # 22 """ RAK2560 Solar base station based on RAK4630 """ HELTEC_HRU_3601: HardwareModel.ValueType # 23 """ Heltec HRU-3601: https://heltec.org/project/hru-3601/ """ STATION_G1: HardwareModel.ValueType # 25 """ B&Q Consulting Station Edition G1: https://uniteng.com/wiki/doku.php?id=meshtastic:station """ RAK11310: HardwareModel.ValueType # 26 """ RAK11310 (RP2040 + SX1262) """ SENSELORA_RP2040: HardwareModel.ValueType # 27 """ Makerfabs SenseLoRA Receiver (RP2040 + RFM96) """ SENSELORA_S3: HardwareModel.ValueType # 28 """ Makerfabs SenseLoRA Industrial Monitor (ESP32-S3 + RFM96) """ CANARYONE: HardwareModel.ValueType # 29 """ Canary Radio Company - CanaryOne: https://canaryradio.io/products/canaryone """ RP2040_LORA: HardwareModel.ValueType # 30 """ Waveshare RP2040 LoRa - https://www.waveshare.com/rp2040-lora.htm """ STATION_G2: HardwareModel.ValueType # 31 """ B&Q Consulting Station G2: https://wiki.uniteng.com/en/meshtastic/station-g2 """ LORA_RELAY_V1: HardwareModel.ValueType # 32 """ --------------------------------------------------------------------------- Less common/prototype boards listed here (needs one more byte over the air) --------------------------------------------------------------------------- """ NRF52840DK: HardwareModel.ValueType # 33 """ TODO: REPLACE """ PPR: HardwareModel.ValueType # 34 """ TODO: REPLACE """ GENIEBLOCKS: HardwareModel.ValueType # 35 """ TODO: REPLACE """ NRF52_UNKNOWN: HardwareModel.ValueType # 36 """ TODO: REPLACE """ PORTDUINO: HardwareModel.ValueType # 37 """ TODO: REPLACE """ ANDROID_SIM: HardwareModel.ValueType # 38 """ The simulator built into the android app """ DIY_V1: HardwareModel.ValueType # 39 """ Custom DIY device based on @NanoVHF schematics: https://github.com/NanoVHF/Meshtastic-DIY/tree/main/Schematics """ NRF52840_PCA10059: HardwareModel.ValueType # 40 """ nRF52840 Dongle : https://www.nordicsemi.com/Products/Development-hardware/nrf52840-dongle/ """ DR_DEV: HardwareModel.ValueType # 41 """ Custom Disaster Radio esp32 v3 device https://github.com/sudomesh/disaster-radio/tree/master/hardware/board_esp32_v3 """ M5STACK: HardwareModel.ValueType # 42 """ M5 esp32 based MCU modules with enclosure, TFT and LORA Shields. All Variants (Basic, Core, Fire, Core2, Paper) https://m5stack.com/ """ HELTEC_V3: HardwareModel.ValueType # 43 """ New Heltec LoRA32 with ESP32-S3 CPU """ HELTEC_WSL_V3: HardwareModel.ValueType # 44 """ New Heltec Wireless Stick Lite with ESP32-S3 CPU """ BETAFPV_2400_TX: HardwareModel.ValueType # 45 """ New BETAFPV ELRS Micro TX Module 2.4G with ESP32 CPU """ BETAFPV_900_NANO_TX: HardwareModel.ValueType # 46 """ BetaFPV ExpressLRS "Nano" TX Module 900MHz with ESP32 CPU """ RPI_PICO: HardwareModel.ValueType # 47 """ Raspberry Pi Pico (W) with Waveshare SX1262 LoRa Node Module """ HELTEC_WIRELESS_TRACKER: HardwareModel.ValueType # 48 """ Heltec Wireless Tracker with ESP32-S3 CPU, built-in GPS, and TFT Newer V1.1, version is written on the PCB near the display. """ HELTEC_WIRELESS_PAPER: HardwareModel.ValueType # 49 """ Heltec Wireless Paper with ESP32-S3 CPU and E-Ink display """ T_DECK: HardwareModel.ValueType # 50 """ LilyGo T-Deck with ESP32-S3 CPU, Keyboard and IPS display """ T_WATCH_S3: HardwareModel.ValueType # 51 """ LilyGo T-Watch S3 with ESP32-S3 CPU and IPS display """ PICOMPUTER_S3: HardwareModel.ValueType # 52 """ Bobricius Picomputer with ESP32-S3 CPU, Keyboard and IPS display """ HELTEC_HT62: HardwareModel.ValueType # 53 """ Heltec HT-CT62 with ESP32-C3 CPU and SX1262 LoRa """ EBYTE_ESP32_S3: HardwareModel.ValueType # 54 """ EBYTE SPI LoRa module and ESP32-S3 """ ESP32_S3_PICO: HardwareModel.ValueType # 55 """ Waveshare ESP32-S3-PICO with PICO LoRa HAT and 2.9inch e-Ink """ CHATTER_2: HardwareModel.ValueType # 56 """ CircuitMess Chatter 2 LLCC68 Lora Module and ESP32 Wroom Lora module can be swapped out for a Heltec RA-62 which is "almost" pin compatible with one cut and one jumper Meshtastic works """ HELTEC_WIRELESS_PAPER_V1_0: HardwareModel.ValueType # 57 """ Heltec Wireless Paper, With ESP32-S3 CPU and E-Ink display Older "V1.0" Variant, has no "version sticker" E-Ink model is DEPG0213BNS800 Tab on the screen protector is RED Flex connector marking is FPC-7528B """ HELTEC_WIRELESS_TRACKER_V1_0: HardwareModel.ValueType # 58 """ Heltec Wireless Tracker with ESP32-S3 CPU, built-in GPS, and TFT Older "V1.0" Variant """ UNPHONE: HardwareModel.ValueType # 59 """ unPhone with ESP32-S3, TFT touchscreen, LSM6DS3TR-C accelerometer and gyroscope """ TD_LORAC: HardwareModel.ValueType # 60 """ Teledatics TD-LORAC NRF52840 based M.2 LoRA module Compatible with the TD-WRLS development board """ CDEBYTE_EORA_S3: HardwareModel.ValueType # 61 """ CDEBYTE EoRa-S3 board using their own MM modules, clone of LILYGO T3S3 """ TWC_MESH_V4: HardwareModel.ValueType # 62 """ TWC_MESH_V4 Adafruit NRF52840 feather express with SX1262, SSD1306 OLED and NEO6M GPS """ NRF52_PROMICRO_DIY: HardwareModel.ValueType # 63 """ NRF52_PROMICRO_DIY Promicro NRF52840 with SX1262/LLCC68, SSD1306 OLED and NEO6M GPS """ RADIOMASTER_900_BANDIT_NANO: HardwareModel.ValueType # 64 """ RadioMaster 900 Bandit Nano, https://www.radiomasterrc.com/products/bandit-nano-expresslrs-rf-module ESP32-D0WDQ6 With SX1276/SKY66122, SSD1306 OLED and No GPS """ HELTEC_CAPSULE_SENSOR_V3: HardwareModel.ValueType # 65 """ Heltec Capsule Sensor V3 with ESP32-S3 CPU, Portable LoRa device that can replace GNSS modules or sensors """ PRIVATE_HW: HardwareModel.ValueType # 255 """ ------------------------------------------------------------------------------------------------------------------------------------------ Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits. ------------------------------------------------------------------------------------------------------------------------------------------ """ global___HardwareModel = HardwareModel class _Constants: ValueType = typing.NewType("ValueType", builtins.int) V: typing_extensions.TypeAlias = ValueType class _ConstantsEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_Constants.ValueType], builtins.type): DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor ZERO: _Constants.ValueType # 0 """ First enum must be zero, and we are just using this enum to pass int constants between two very different environments """ DATA_PAYLOAD_LEN: _Constants.ValueType # 237 """ From mesh.options note: this payload length is ONLY the bytes that are sent inside of the Data protobuf (excluding protobuf overhead). The 16 byte header is outside of this envelope """ class Constants(_Constants, metaclass=_ConstantsEnumTypeWrapper): """ Shared constants between device and phone """ ZERO: Constants.ValueType # 0 """ First enum must be zero, and we are just using this enum to pass int constants between two very different environments """ DATA_PAYLOAD_LEN: Constants.ValueType # 237 """ From mesh.options note: this payload length is ONLY the bytes that are sent inside of the Data protobuf (excluding protobuf overhead). The 16 byte header is outside of this envelope """ global___Constants = Constants class _CriticalErrorCode: ValueType = typing.NewType("ValueType", builtins.int) V: typing_extensions.TypeAlias = ValueType class _CriticalErrorCodeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_CriticalErrorCode.ValueType], builtins.type): DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor NONE: _CriticalErrorCode.ValueType # 0 """ TODO: REPLACE """ TX_WATCHDOG: _CriticalErrorCode.ValueType # 1 """ A software bug was detected while trying to send lora """ SLEEP_ENTER_WAIT: _CriticalErrorCode.ValueType # 2 """ A software bug was detected on entry to sleep """ NO_RADIO: _CriticalErrorCode.ValueType # 3 """ No Lora radio hardware could be found """ UNSPECIFIED: _CriticalErrorCode.ValueType # 4 """ Not normally used """ UBLOX_UNIT_FAILED: _CriticalErrorCode.ValueType # 5 """ We failed while configuring a UBlox GPS """ NO_AXP192: _CriticalErrorCode.ValueType # 6 """ This board was expected to have a power management chip and it is missing or broken """ INVALID_RADIO_SETTING: _CriticalErrorCode.ValueType # 7 """ The channel tried to set a radio setting which is not supported by this chipset, radio comms settings are now undefined. """ TRANSMIT_FAILED: _CriticalErrorCode.ValueType # 8 """ Radio transmit hardware failure. We sent data to the radio chip, but it didn't reply with an interrupt. """ BROWNOUT: _CriticalErrorCode.ValueType # 9 """ We detected that the main CPU voltage dropped below the minimum acceptable value """ SX1262_FAILURE: _CriticalErrorCode.ValueType # 10 """Selftest of SX1262 radio chip failed""" RADIO_SPI_BUG: _CriticalErrorCode.ValueType # 11 """ A (likely software but possibly hardware) failure was detected while trying to send packets. If this occurs on your board, please post in the forum so that we can ask you to collect some information to allow fixing this bug """ class CriticalErrorCode(_CriticalErrorCode, metaclass=_CriticalErrorCodeEnumTypeWrapper): """ Error codes for critical errors The device might report these fault codes on the screen. If you encounter a fault code, please post on the meshtastic.discourse.group and we'll try to help. """ NONE: CriticalErrorCode.ValueType # 0 """ TODO: REPLACE """ TX_WATCHDOG: CriticalErrorCode.ValueType # 1 """ A software bug was detected while trying to send lora """ SLEEP_ENTER_WAIT: CriticalErrorCode.ValueType # 2 """ A software bug was detected on entry to sleep """ NO_RADIO: CriticalErrorCode.ValueType # 3 """ No Lora radio hardware could be found """ UNSPECIFIED: CriticalErrorCode.ValueType # 4 """ Not normally used """ UBLOX_UNIT_FAILED: CriticalErrorCode.ValueType # 5 """ We failed while configuring a UBlox GPS """ NO_AXP192: CriticalErrorCode.ValueType # 6 """ This board was expected to have a power management chip and it is missing or broken """ INVALID_RADIO_SETTING: CriticalErrorCode.ValueType # 7 """ The channel tried to set a radio setting which is not supported by this chipset, radio comms settings are now undefined. """ TRANSMIT_FAILED: CriticalErrorCode.ValueType # 8 """ Radio transmit hardware failure. We sent data to the radio chip, but it didn't reply with an interrupt. """ BROWNOUT: CriticalErrorCode.ValueType # 9 """ We detected that the main CPU voltage dropped below the minimum acceptable value """ SX1262_FAILURE: CriticalErrorCode.ValueType # 10 """Selftest of SX1262 radio chip failed""" RADIO_SPI_BUG: CriticalErrorCode.ValueType # 11 """ A (likely software but possibly hardware) failure was detected while trying to send packets. If this occurs on your board, please post in the forum so that we can ask you to collect some information to allow fixing this bug """ global___CriticalErrorCode = CriticalErrorCode @typing.final class Position(google.protobuf.message.Message): """ a gps position """ DESCRIPTOR: google.protobuf.descriptor.Descriptor class _LocSource: ValueType = typing.NewType("ValueType", builtins.int) V: typing_extensions.TypeAlias = ValueType class _LocSourceEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[Position._LocSource.ValueType], builtins.type): DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor LOC_UNSET: Position._LocSource.ValueType # 0 """ TODO: REPLACE """ LOC_MANUAL: Position._LocSource.ValueType # 1 """ TODO: REPLACE """ LOC_INTERNAL: Position._LocSource.ValueType # 2 """ TODO: REPLACE """ LOC_EXTERNAL: Position._LocSource.ValueType # 3 """ TODO: REPLACE """ class LocSource(_LocSource, metaclass=_LocSourceEnumTypeWrapper): """ How the location was acquired: manual, onboard GPS, external (EUD) GPS """ LOC_UNSET: Position.LocSource.ValueType # 0 """ TODO: REPLACE """ LOC_MANUAL: Position.LocSource.ValueType # 1 """ TODO: REPLACE """ LOC_INTERNAL: Position.LocSource.ValueType # 2 """ TODO: REPLACE """ LOC_EXTERNAL: Position.LocSource.ValueType # 3 """ TODO: REPLACE """ class _AltSource: ValueType = typing.NewType("ValueType", builtins.int) V: typing_extensions.TypeAlias = ValueType class _AltSourceEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[Position._AltSource.ValueType], builtins.type): DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor ALT_UNSET: Position._AltSource.ValueType # 0 """ TODO: REPLACE """ ALT_MANUAL: Position._AltSource.ValueType # 1 """ TODO: REPLACE """ ALT_INTERNAL: Position._AltSource.ValueType # 2 """ TODO: REPLACE """ ALT_EXTERNAL: Position._AltSource.ValueType # 3 """ TODO: REPLACE """ ALT_BAROMETRIC: Position._AltSource.ValueType # 4 """ TODO: REPLACE """ class AltSource(_AltSource, metaclass=_AltSourceEnumTypeWrapper): """ How the altitude was acquired: manual, GPS int/ext, etc Default: same as location_source if present """ ALT_UNSET: Position.AltSource.ValueType # 0 """ TODO: REPLACE """ ALT_MANUAL: Position.AltSource.ValueType # 1 """ TODO: REPLACE """ ALT_INTERNAL: Position.AltSource.ValueType # 2 """ TODO: REPLACE """ ALT_EXTERNAL: Position.AltSource.ValueType # 3 """ TODO: REPLACE """ ALT_BAROMETRIC: Position.AltSource.ValueType # 4 """ TODO: REPLACE """ LATITUDE_I_FIELD_NUMBER: builtins.int LONGITUDE_I_FIELD_NUMBER: builtins.int ALTITUDE_FIELD_NUMBER: builtins.int TIME_FIELD_NUMBER: builtins.int LOCATION_SOURCE_FIELD_NUMBER: builtins.int ALTITUDE_SOURCE_FIELD_NUMBER: builtins.int TIMESTAMP_FIELD_NUMBER: builtins.int TIMESTAMP_MILLIS_ADJUST_FIELD_NUMBER: builtins.int ALTITUDE_HAE_FIELD_NUMBER: builtins.int ALTITUDE_GEOIDAL_SEPARATION_FIELD_NUMBER: builtins.int PDOP_FIELD_NUMBER: builtins.int HDOP_FIELD_NUMBER: builtins.int VDOP_FIELD_NUMBER: builtins.int GPS_ACCURACY_FIELD_NUMBER: builtins.int GROUND_SPEED_FIELD_NUMBER: builtins.int GROUND_TRACK_FIELD_NUMBER: builtins.int FIX_QUALITY_FIELD_NUMBER: builtins.int FIX_TYPE_FIELD_NUMBER: builtins.int SATS_IN_VIEW_FIELD_NUMBER: builtins.int SENSOR_ID_FIELD_NUMBER: builtins.int NEXT_UPDATE_FIELD_NUMBER: builtins.int SEQ_NUMBER_FIELD_NUMBER: builtins.int PRECISION_BITS_FIELD_NUMBER: builtins.int latitude_i: builtins.int """ The new preferred location encoding, multiply by 1e-7 to get degrees in floating point """ longitude_i: builtins.int """ TODO: REPLACE """ altitude: builtins.int """ In meters above MSL (but see issue #359) """ time: builtins.int """ This is usually not sent over the mesh (to save space), but it is sent from the phone so that the local device can set its time if it is sent over the mesh (because there are devices on the mesh without GPS or RTC). seconds since 1970 """ location_source: global___Position.LocSource.ValueType """ TODO: REPLACE """ altitude_source: global___Position.AltSource.ValueType """ TODO: REPLACE """ timestamp: builtins.int """ Positional timestamp (actual timestamp of GPS solution) in integer epoch seconds """ timestamp_millis_adjust: builtins.int """ Pos. timestamp milliseconds adjustment (rarely available or required) """ altitude_hae: builtins.int """ HAE altitude in meters - can be used instead of MSL altitude """ altitude_geoidal_separation: builtins.int """ Geoidal separation in meters """ PDOP: builtins.int """ Horizontal, Vertical and Position Dilution of Precision, in 1/100 units - PDOP is sufficient for most cases - for higher precision scenarios, HDOP and VDOP can be used instead, in which case PDOP becomes redundant (PDOP=sqrt(HDOP^2 + VDOP^2)) TODO: REMOVE/INTEGRATE """ HDOP: builtins.int """ TODO: REPLACE """ VDOP: builtins.int """ TODO: REPLACE """ gps_accuracy: builtins.int """ GPS accuracy (a hardware specific constant) in mm multiplied with DOP to calculate positional accuracy Default: "'bout three meters-ish" :) """ ground_speed: builtins.int """ Ground speed in m/s and True North TRACK in 1/100 degrees Clarification of terms: - "track" is the direction of motion (measured in horizontal plane) - "heading" is where the fuselage points (measured in horizontal plane) - "yaw" indicates a relative rotation about the vertical axis TODO: REMOVE/INTEGRATE """ ground_track: builtins.int """ TODO: REPLACE """ fix_quality: builtins.int """ GPS fix quality (from NMEA GxGGA statement or similar) """ fix_type: builtins.int """ GPS fix type 2D/3D (from NMEA GxGSA statement) """ sats_in_view: builtins.int """ GPS "Satellites in View" number """ sensor_id: builtins.int """ Sensor ID - in case multiple positioning sensors are being used """ next_update: builtins.int """ Estimated/expected time (in seconds) until next update: - if we update at fixed intervals of X seconds, use X - if we update at dynamic intervals (based on relative movement etc), but "AT LEAST every Y seconds", use Y """ seq_number: builtins.int """ A sequence number, incremented with each Position message to help detect lost updates if needed """ precision_bits: builtins.int """ Indicates the bits of precision set by the sending node """ def __init__( self, *, latitude_i: builtins.int = ..., longitude_i: builtins.int = ..., altitude: builtins.int = ..., time: builtins.int = ..., location_source: global___Position.LocSource.ValueType = ..., altitude_source: global___Position.AltSource.ValueType = ..., timestamp: builtins.int = ..., timestamp_millis_adjust: builtins.int = ..., altitude_hae: builtins.int = ..., altitude_geoidal_separation: builtins.int = ..., PDOP: builtins.int = ..., HDOP: builtins.int = ..., VDOP: builtins.int = ..., gps_accuracy: builtins.int = ..., ground_speed: builtins.int = ..., ground_track: builtins.int = ..., fix_quality: builtins.int = ..., fix_type: builtins.int = ..., sats_in_view: builtins.int = ..., sensor_id: builtins.int = ..., next_update: builtins.int = ..., seq_number: builtins.int = ..., precision_bits: builtins.int = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["HDOP", b"HDOP", "PDOP", b"PDOP", "VDOP", b"VDOP", "altitude", b"altitude", "altitude_geoidal_separation", b"altitude_geoidal_separation", "altitude_hae", b"altitude_hae", "altitude_source", b"altitude_source", "fix_quality", b"fix_quality", "fix_type", b"fix_type", "gps_accuracy", b"gps_accuracy", "ground_speed", b"ground_speed", "ground_track", b"ground_track", "latitude_i", b"latitude_i", "location_source", b"location_source", "longitude_i", b"longitude_i", "next_update", b"next_update", "precision_bits", b"precision_bits", "sats_in_view", b"sats_in_view", "sensor_id", b"sensor_id", "seq_number", b"seq_number", "time", b"time", "timestamp", b"timestamp", "timestamp_millis_adjust", b"timestamp_millis_adjust"]) -> None: ... global___Position = Position @typing.final class User(google.protobuf.message.Message): """ Broadcast when a newly powered mesh node wants to find a node num it can use Sent from the phone over bluetooth to set the user id for the owner of this node. Also sent from nodes to each other when a new node signs on (so all clients can have this info) The algorithm is as follows: when a node starts up, it broadcasts their user and the normal flow is for all other nodes to reply with their User as well (so the new node can build its nodedb) If a node ever receives a User (not just the first broadcast) message where the sender node number equals our node number, that indicates a collision has occurred and the following steps should happen: If the receiving node (that was already in the mesh)'s macaddr is LOWER than the new User who just tried to sign in: it gets to keep its nodenum. We send a broadcast message of OUR User (we use a broadcast so that the other node can receive our message, considering we have the same id - it also serves to let observers correct their nodedb) - this case is rare so it should be okay. If any node receives a User where the macaddr is GTE than their local macaddr, they have been vetoed and should pick a new random nodenum (filtering against whatever it knows about the nodedb) and rebroadcast their User. A few nodenums are reserved and will never be requested: 0xff - broadcast 0 through 3 - for future use """ DESCRIPTOR: google.protobuf.descriptor.Descriptor ID_FIELD_NUMBER: builtins.int LONG_NAME_FIELD_NUMBER: builtins.int SHORT_NAME_FIELD_NUMBER: builtins.int MACADDR_FIELD_NUMBER: builtins.int HW_MODEL_FIELD_NUMBER: builtins.int IS_LICENSED_FIELD_NUMBER: builtins.int ROLE_FIELD_NUMBER: builtins.int id: builtins.str """ A globally unique ID string for this user. In the case of Signal that would mean +16504442323, for the default macaddr derived id it would be !<8 hexidecimal bytes>. Note: app developers are encouraged to also use the following standard node IDs "^all" (for broadcast), "^local" (for the locally connected node) """ long_name: builtins.str """ A full name for this user, i.e. "Kevin Hester" """ short_name: builtins.str """ A VERY short name, ideally two characters. Suitable for a tiny OLED screen """ macaddr: builtins.bytes """ Deprecated in Meshtastic 2.1.x This is the addr of the radio. Not populated by the phone, but added by the esp32 when broadcasting """ hw_model: global___HardwareModel.ValueType """ TBEAM, HELTEC, etc... Starting in 1.2.11 moved to hw_model enum in the NodeInfo object. Apps will still need the string here for older builds (so OTA update can find the right image), but if the enum is available it will be used instead. """ is_licensed: builtins.bool """ In some regions Ham radio operators have different bandwidth limitations than others. If this user is a licensed operator, set this flag. Also, "long_name" should be their licence number. """ role: meshtastic.protobuf.config_pb2.Config.DeviceConfig.Role.ValueType """ Indicates that the user's role in the mesh """ def __init__( self, *, id: builtins.str = ..., long_name: builtins.str = ..., short_name: builtins.str = ..., macaddr: builtins.bytes = ..., hw_model: global___HardwareModel.ValueType = ..., is_licensed: builtins.bool = ..., role: meshtastic.protobuf.config_pb2.Config.DeviceConfig.Role.ValueType = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["hw_model", b"hw_model", "id", b"id", "is_licensed", b"is_licensed", "long_name", b"long_name", "macaddr", b"macaddr", "role", b"role", "short_name", b"short_name"]) -> None: ... global___User = User @typing.final class RouteDiscovery(google.protobuf.message.Message): """ A message used in our Dynamic Source Routing protocol (RFC 4728 based) """ DESCRIPTOR: google.protobuf.descriptor.Descriptor ROUTE_FIELD_NUMBER: builtins.int @property def route(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.int]: """ The list of nodenums this packet has visited so far """ def __init__( self, *, route: collections.abc.Iterable[builtins.int] | None = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["route", b"route"]) -> None: ... global___RouteDiscovery = RouteDiscovery @typing.final class Routing(google.protobuf.message.Message): """ A Routing control Data packet handled by the routing module """ DESCRIPTOR: google.protobuf.descriptor.Descriptor class _Error: ValueType = typing.NewType("ValueType", builtins.int) V: typing_extensions.TypeAlias = ValueType class _ErrorEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[Routing._Error.ValueType], builtins.type): DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor NONE: Routing._Error.ValueType # 0 """ This message is not a failure """ NO_ROUTE: Routing._Error.ValueType # 1 """ Our node doesn't have a route to the requested destination anymore. """ GOT_NAK: Routing._Error.ValueType # 2 """ We received a nak while trying to forward on your behalf """ TIMEOUT: Routing._Error.ValueType # 3 """ TODO: REPLACE """ NO_INTERFACE: Routing._Error.ValueType # 4 """ No suitable interface could be found for delivering this packet """ MAX_RETRANSMIT: Routing._Error.ValueType # 5 """ We reached the max retransmission count (typically for naive flood routing) """ NO_CHANNEL: Routing._Error.ValueType # 6 """ No suitable channel was found for sending this packet (i.e. was requested channel index disabled?) """ TOO_LARGE: Routing._Error.ValueType # 7 """ The packet was too big for sending (exceeds interface MTU after encoding) """ NO_RESPONSE: Routing._Error.ValueType # 8 """ The request had want_response set, the request reached the destination node, but no service on that node wants to send a response (possibly due to bad channel permissions) """ DUTY_CYCLE_LIMIT: Routing._Error.ValueType # 9 """ Cannot send currently because duty cycle regulations will be violated. """ BAD_REQUEST: Routing._Error.ValueType # 32 """ The application layer service on the remote node received your request, but considered your request somehow invalid """ NOT_AUTHORIZED: Routing._Error.ValueType # 33 """ The application layer service on the remote node received your request, but considered your request not authorized (i.e you did not send the request on the required bound channel) """ class Error(_Error, metaclass=_ErrorEnumTypeWrapper): """ A failure in delivering a message (usually used for routing control messages, but might be provided in addition to ack.fail_id to provide details on the type of failure). """ NONE: Routing.Error.ValueType # 0 """ This message is not a failure """ NO_ROUTE: Routing.Error.ValueType # 1 """ Our node doesn't have a route to the requested destination anymore. """ GOT_NAK: Routing.Error.ValueType # 2 """ We received a nak while trying to forward on your behalf """ TIMEOUT: Routing.Error.ValueType # 3 """ TODO: REPLACE """ NO_INTERFACE: Routing.Error.ValueType # 4 """ No suitable interface could be found for delivering this packet """ MAX_RETRANSMIT: Routing.Error.ValueType # 5 """ We reached the max retransmission count (typically for naive flood routing) """ NO_CHANNEL: Routing.Error.ValueType # 6 """ No suitable channel was found for sending this packet (i.e. was requested channel index disabled?) """ TOO_LARGE: Routing.Error.ValueType # 7 """ The packet was too big for sending (exceeds interface MTU after encoding) """ NO_RESPONSE: Routing.Error.ValueType # 8 """ The request had want_response set, the request reached the destination node, but no service on that node wants to send a response (possibly due to bad channel permissions) """ DUTY_CYCLE_LIMIT: Routing.Error.ValueType # 9 """ Cannot send currently because duty cycle regulations will be violated. """ BAD_REQUEST: Routing.Error.ValueType # 32 """ The application layer service on the remote node received your request, but considered your request somehow invalid """ NOT_AUTHORIZED: Routing.Error.ValueType # 33 """ The application layer service on the remote node received your request, but considered your request not authorized (i.e you did not send the request on the required bound channel) """ ROUTE_REQUEST_FIELD_NUMBER: builtins.int ROUTE_REPLY_FIELD_NUMBER: builtins.int ERROR_REASON_FIELD_NUMBER: builtins.int error_reason: global___Routing.Error.ValueType """ A failure in delivering a message (usually used for routing control messages, but might be provided in addition to ack.fail_id to provide details on the type of failure). """ @property def route_request(self) -> global___RouteDiscovery: """ A route request going from the requester """ @property def route_reply(self) -> global___RouteDiscovery: """ A route reply """ def __init__( self, *, route_request: global___RouteDiscovery | None = ..., route_reply: global___RouteDiscovery | None = ..., error_reason: global___Routing.Error.ValueType = ..., ) -> None: ... def HasField(self, field_name: typing.Literal["error_reason", b"error_reason", "route_reply", b"route_reply", "route_request", b"route_request", "variant", b"variant"]) -> builtins.bool: ... def ClearField(self, field_name: typing.Literal["error_reason", b"error_reason", "route_reply", b"route_reply", "route_request", b"route_request", "variant", b"variant"]) -> None: ... def WhichOneof(self, oneof_group: typing.Literal["variant", b"variant"]) -> typing.Literal["route_request", "route_reply", "error_reason"] | None: ... global___Routing = Routing @typing.final class Data(google.protobuf.message.Message): """ (Formerly called SubPacket) The payload portion fo a packet, this is the actual bytes that are sent inside a radio packet (because from/to are broken out by the comms library) """ DESCRIPTOR: google.protobuf.descriptor.Descriptor PORTNUM_FIELD_NUMBER: builtins.int PAYLOAD_FIELD_NUMBER: builtins.int WANT_RESPONSE_FIELD_NUMBER: builtins.int DEST_FIELD_NUMBER: builtins.int SOURCE_FIELD_NUMBER: builtins.int REQUEST_ID_FIELD_NUMBER: builtins.int REPLY_ID_FIELD_NUMBER: builtins.int EMOJI_FIELD_NUMBER: builtins.int portnum: meshtastic.protobuf.portnums_pb2.PortNum.ValueType """ Formerly named typ and of type Type """ payload: builtins.bytes """ TODO: REPLACE """ want_response: builtins.bool """ Not normally used, but for testing a sender can request that recipient responds in kind (i.e. if it received a position, it should unicast back it's position). Note: that if you set this on a broadcast you will receive many replies. """ dest: builtins.int """ The address of the destination node. This field is is filled in by the mesh radio device software, application layer software should never need it. RouteDiscovery messages _must_ populate this. Other message types might need to if they are doing multihop routing. """ source: builtins.int """ The address of the original sender for this message. This field should _only_ be populated for reliable multihop packets (to keep packets small). """ request_id: builtins.int """ Only used in routing or response messages. Indicates the original message ID that this message is reporting failure on. (formerly called original_id) """ reply_id: builtins.int """ If set, this message is intened to be a reply to a previously sent message with the defined id. """ emoji: builtins.int """ Defaults to false. If true, then what is in the payload should be treated as an emoji like giving a message a heart or poop emoji. """ def __init__( self, *, portnum: meshtastic.protobuf.portnums_pb2.PortNum.ValueType = ..., payload: builtins.bytes = ..., want_response: builtins.bool = ..., dest: builtins.int = ..., source: builtins.int = ..., request_id: builtins.int = ..., reply_id: builtins.int = ..., emoji: builtins.int = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["dest", b"dest", "emoji", b"emoji", "payload", b"payload", "portnum", b"portnum", "reply_id", b"reply_id", "request_id", b"request_id", "source", b"source", "want_response", b"want_response"]) -> None: ... global___Data = Data @typing.final class Waypoint(google.protobuf.message.Message): """ Waypoint message, used to share arbitrary locations across the mesh """ DESCRIPTOR: google.protobuf.descriptor.Descriptor ID_FIELD_NUMBER: builtins.int LATITUDE_I_FIELD_NUMBER: builtins.int LONGITUDE_I_FIELD_NUMBER: builtins.int EXPIRE_FIELD_NUMBER: builtins.int LOCKED_TO_FIELD_NUMBER: builtins.int NAME_FIELD_NUMBER: builtins.int DESCRIPTION_FIELD_NUMBER: builtins.int ICON_FIELD_NUMBER: builtins.int id: builtins.int """ Id of the waypoint """ latitude_i: builtins.int """ latitude_i """ longitude_i: builtins.int """ longitude_i """ expire: builtins.int """ Time the waypoint is to expire (epoch) """ locked_to: builtins.int """ If greater than zero, treat the value as a nodenum only allowing them to update the waypoint. If zero, the waypoint is open to be edited by any member of the mesh. """ name: builtins.str """ Name of the waypoint - max 30 chars """ description: builtins.str """ Description of the waypoint - max 100 chars """ icon: builtins.int """ Designator icon for the waypoint in the form of a unicode emoji """ def __init__( self, *, id: builtins.int = ..., latitude_i: builtins.int = ..., longitude_i: builtins.int = ..., expire: builtins.int = ..., locked_to: builtins.int = ..., name: builtins.str = ..., description: builtins.str = ..., icon: builtins.int = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["description", b"description", "expire", b"expire", "icon", b"icon", "id", b"id", "latitude_i", b"latitude_i", "locked_to", b"locked_to", "longitude_i", b"longitude_i", "name", b"name"]) -> None: ... global___Waypoint = Waypoint @typing.final class MqttClientProxyMessage(google.protobuf.message.Message): """ This message will be proxied over the PhoneAPI for the client to deliver to the MQTT server """ DESCRIPTOR: google.protobuf.descriptor.Descriptor TOPIC_FIELD_NUMBER: builtins.int DATA_FIELD_NUMBER: builtins.int TEXT_FIELD_NUMBER: builtins.int RETAINED_FIELD_NUMBER: builtins.int topic: builtins.str """ The MQTT topic this message will be sent /received on """ data: builtins.bytes """ Bytes """ text: builtins.str """ Text """ retained: builtins.bool """ Whether the message should be retained (or not) """ def __init__( self, *, topic: builtins.str = ..., data: builtins.bytes = ..., text: builtins.str = ..., retained: builtins.bool = ..., ) -> None: ... def HasField(self, field_name: typing.Literal["data", b"data", "payload_variant", b"payload_variant", "text", b"text"]) -> builtins.bool: ... def ClearField(self, field_name: typing.Literal["data", b"data", "payload_variant", b"payload_variant", "retained", b"retained", "text", b"text", "topic", b"topic"]) -> None: ... def WhichOneof(self, oneof_group: typing.Literal["payload_variant", b"payload_variant"]) -> typing.Literal["data", "text"] | None: ... global___MqttClientProxyMessage = MqttClientProxyMessage @typing.final class MeshPacket(google.protobuf.message.Message): """ A packet envelope sent/received over the mesh only payload_variant is sent in the payload portion of the LORA packet. The other fields are either not sent at all, or sent in the special 16 byte LORA header. """ DESCRIPTOR: google.protobuf.descriptor.Descriptor class _Priority: ValueType = typing.NewType("ValueType", builtins.int) V: typing_extensions.TypeAlias = ValueType class _PriorityEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[MeshPacket._Priority.ValueType], builtins.type): DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor UNSET: MeshPacket._Priority.ValueType # 0 """ Treated as Priority.DEFAULT """ MIN: MeshPacket._Priority.ValueType # 1 """ TODO: REPLACE """ BACKGROUND: MeshPacket._Priority.ValueType # 10 """ Background position updates are sent with very low priority - if the link is super congested they might not go out at all """ DEFAULT: MeshPacket._Priority.ValueType # 64 """ This priority is used for most messages that don't have a priority set """ RELIABLE: MeshPacket._Priority.ValueType # 70 """ If priority is unset but the message is marked as want_ack, assume it is important and use a slightly higher priority """ ACK: MeshPacket._Priority.ValueType # 120 """ Ack/naks are sent with very high priority to ensure that retransmission stops as soon as possible """ MAX: MeshPacket._Priority.ValueType # 127 """ TODO: REPLACE """ class Priority(_Priority, metaclass=_PriorityEnumTypeWrapper): """ The priority of this message for sending. Higher priorities are sent first (when managing the transmit queue). This field is never sent over the air, it is only used internally inside of a local device node. API clients (either on the local node or connected directly to the node) can set this parameter if necessary. (values must be <= 127 to keep protobuf field to one byte in size. Detailed background on this field: I noticed a funny side effect of lora being so slow: Usually when making a protocol there isn’t much need to use message priority to change the order of transmission (because interfaces are fairly fast). But for lora where packets can take a few seconds each, it is very important to make sure that critical packets are sent ASAP. In the case of meshtastic that means we want to send protocol acks as soon as possible (to prevent unneeded retransmissions), we want routing messages to be sent next, then messages marked as reliable and finally 'background' packets like periodic position updates. So I bit the bullet and implemented a new (internal - not sent over the air) field in MeshPacket called 'priority'. And the transmission queue in the router object is now a priority queue. """ UNSET: MeshPacket.Priority.ValueType # 0 """ Treated as Priority.DEFAULT """ MIN: MeshPacket.Priority.ValueType # 1 """ TODO: REPLACE """ BACKGROUND: MeshPacket.Priority.ValueType # 10 """ Background position updates are sent with very low priority - if the link is super congested they might not go out at all """ DEFAULT: MeshPacket.Priority.ValueType # 64 """ This priority is used for most messages that don't have a priority set """ RELIABLE: MeshPacket.Priority.ValueType # 70 """ If priority is unset but the message is marked as want_ack, assume it is important and use a slightly higher priority """ ACK: MeshPacket.Priority.ValueType # 120 """ Ack/naks are sent with very high priority to ensure that retransmission stops as soon as possible """ MAX: MeshPacket.Priority.ValueType # 127 """ TODO: REPLACE """ class _Delayed: ValueType = typing.NewType("ValueType", builtins.int) V: typing_extensions.TypeAlias = ValueType class _DelayedEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[MeshPacket._Delayed.ValueType], builtins.type): DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor NO_DELAY: MeshPacket._Delayed.ValueType # 0 """ If unset, the message is being sent in real time. """ DELAYED_BROADCAST: MeshPacket._Delayed.ValueType # 1 """ The message is delayed and was originally a broadcast """ DELAYED_DIRECT: MeshPacket._Delayed.ValueType # 2 """ The message is delayed and was originally a direct message """ class Delayed(_Delayed, metaclass=_DelayedEnumTypeWrapper): """ Identify if this is a delayed packet """ NO_DELAY: MeshPacket.Delayed.ValueType # 0 """ If unset, the message is being sent in real time. """ DELAYED_BROADCAST: MeshPacket.Delayed.ValueType # 1 """ The message is delayed and was originally a broadcast """ DELAYED_DIRECT: MeshPacket.Delayed.ValueType # 2 """ The message is delayed and was originally a direct message """ FROM_FIELD_NUMBER: builtins.int TO_FIELD_NUMBER: builtins.int CHANNEL_FIELD_NUMBER: builtins.int DECODED_FIELD_NUMBER: builtins.int ENCRYPTED_FIELD_NUMBER: builtins.int ID_FIELD_NUMBER: builtins.int RX_TIME_FIELD_NUMBER: builtins.int RX_SNR_FIELD_NUMBER: builtins.int HOP_LIMIT_FIELD_NUMBER: builtins.int WANT_ACK_FIELD_NUMBER: builtins.int PRIORITY_FIELD_NUMBER: builtins.int RX_RSSI_FIELD_NUMBER: builtins.int DELAYED_FIELD_NUMBER: builtins.int VIA_MQTT_FIELD_NUMBER: builtins.int HOP_START_FIELD_NUMBER: builtins.int to: builtins.int """ The (immediate) destination for this packet """ channel: builtins.int """ (Usually) If set, this indicates the index in the secondary_channels table that this packet was sent/received on. If unset, packet was on the primary channel. A particular node might know only a subset of channels in use on the mesh. Therefore channel_index is inherently a local concept and meaningless to send between nodes. Very briefly, while sending and receiving deep inside the device Router code, this field instead contains the 'channel hash' instead of the index. This 'trick' is only used while the payload_variant is an 'encrypted'. """ encrypted: builtins.bytes """ TODO: REPLACE """ id: builtins.int """ A unique ID for this packet. Always 0 for no-ack packets or non broadcast packets (and therefore take zero bytes of space). Otherwise a unique ID for this packet, useful for flooding algorithms. ID only needs to be unique on a _per sender_ basis, and it only needs to be unique for a few minutes (long enough to last for the length of any ACK or the completion of a mesh broadcast flood). Note: Our crypto implementation uses this id as well. See [crypto](/docs/overview/encryption) for details. """ rx_time: builtins.int """ The time this message was received by the esp32 (secs since 1970). Note: this field is _never_ sent on the radio link itself (to save space) Times are typically not sent over the mesh, but they will be added to any Packet (chain of SubPacket) sent to the phone (so the phone can know exact time of reception) """ rx_snr: builtins.float """ *Never* sent over the radio links. Set during reception to indicate the SNR of this packet. Used to collect statistics on current link quality. """ hop_limit: builtins.int """ If unset treated as zero (no forwarding, send to adjacent nodes only) if 1, allow hopping through one node, etc... For our usecase real world topologies probably have a max of about 3. This field is normally placed into a few of bits in the header. """ want_ack: builtins.bool """ This packet is being sent as a reliable message, we would prefer it to arrive at the destination. We would like to receive a ack packet in response. Broadcasts messages treat this flag specially: Since acks for broadcasts would rapidly flood the channel, the normal ack behavior is suppressed. Instead, the original sender listens to see if at least one node is rebroadcasting this packet (because naive flooding algorithm). If it hears that the odds (given typical LoRa topologies) the odds are very high that every node should eventually receive the message. So FloodingRouter.cpp generates an implicit ack which is delivered to the original sender. If after some time we don't hear anyone rebroadcast our packet, we will timeout and retransmit, using the regular resend logic. Note: This flag is normally sent in a flag bit in the header when sent over the wire """ priority: global___MeshPacket.Priority.ValueType """ The priority of this message for sending. See MeshPacket.Priority description for more details. """ rx_rssi: builtins.int """ rssi of received packet. Only sent to phone for dispay purposes. """ delayed: global___MeshPacket.Delayed.ValueType """ Describe if this message is delayed """ via_mqtt: builtins.bool """ Describes whether this packet passed via MQTT somewhere along the path it currently took. """ hop_start: builtins.int """ Hop limit with which the original packet started. Sent via LoRa using three bits in the unencrypted header. When receiving a packet, the difference between hop_start and hop_limit gives how many hops it traveled. """ @property def decoded(self) -> global___Data: """ TODO: REPLACE """ def __init__( self, *, to: builtins.int = ..., channel: builtins.int = ..., decoded: global___Data | None = ..., encrypted: builtins.bytes = ..., id: builtins.int = ..., rx_time: builtins.int = ..., rx_snr: builtins.float = ..., hop_limit: builtins.int = ..., want_ack: builtins.bool = ..., priority: global___MeshPacket.Priority.ValueType = ..., rx_rssi: builtins.int = ..., delayed: global___MeshPacket.Delayed.ValueType = ..., via_mqtt: builtins.bool = ..., hop_start: builtins.int = ..., ) -> None: ... def HasField(self, field_name: typing.Literal["decoded", b"decoded", "encrypted", b"encrypted", "payload_variant", b"payload_variant"]) -> builtins.bool: ... def ClearField(self, field_name: typing.Literal["channel", b"channel", "decoded", b"decoded", "delayed", b"delayed", "encrypted", b"encrypted", "from", b"from", "hop_limit", b"hop_limit", "hop_start", b"hop_start", "id", b"id", "payload_variant", b"payload_variant", "priority", b"priority", "rx_rssi", b"rx_rssi", "rx_snr", b"rx_snr", "rx_time", b"rx_time", "to", b"to", "via_mqtt", b"via_mqtt", "want_ack", b"want_ack"]) -> None: ... def WhichOneof(self, oneof_group: typing.Literal["payload_variant", b"payload_variant"]) -> typing.Literal["decoded", "encrypted"] | None: ... global___MeshPacket = MeshPacket @typing.final class NodeInfo(google.protobuf.message.Message): """ The bluetooth to device link: Old BTLE protocol docs from TODO, merge in above and make real docs... use protocol buffers, and NanoPB messages from device to phone: POSITION_UPDATE (..., time) TEXT_RECEIVED(from, text, time) OPAQUE_RECEIVED(from, payload, time) (for signal messages or other applications) messages from phone to device: SET_MYID(id, human readable long, human readable short) (send down the unique ID string used for this node, a human readable string shown for that id, and a very short human readable string suitable for oled screen) SEND_OPAQUE(dest, payload) (for signal messages or other applications) SEND_TEXT(dest, text) Get all nodes() (returns list of nodes, with full info, last time seen, loc, battery level etc) SET_CONFIG (switches device to a new set of radio params and preshared key, drops all existing nodes, force our node to rejoin this new group) Full information about a node on the mesh """ DESCRIPTOR: google.protobuf.descriptor.Descriptor NUM_FIELD_NUMBER: builtins.int USER_FIELD_NUMBER: builtins.int POSITION_FIELD_NUMBER: builtins.int SNR_FIELD_NUMBER: builtins.int LAST_HEARD_FIELD_NUMBER: builtins.int DEVICE_METRICS_FIELD_NUMBER: builtins.int CHANNEL_FIELD_NUMBER: builtins.int VIA_MQTT_FIELD_NUMBER: builtins.int HOPS_AWAY_FIELD_NUMBER: builtins.int IS_FAVORITE_FIELD_NUMBER: builtins.int num: builtins.int """ The node number """ snr: builtins.float """ Returns the Signal-to-noise ratio (SNR) of the last received message, as measured by the receiver. Return SNR of the last received message in dB """ last_heard: builtins.int """ TODO: REMOVE/INTEGRATE Not currently used (till full DSR deployment?) Our current preferred node node for routing - might be the same as num if we are adjacent Or zero if we don't yet know a route to this node. fixed32 next_hop = 5; Set to indicate the last time we received a packet from this node """ channel: builtins.int """ local channel index we heard that node on. Only populated if its not the default channel. """ via_mqtt: builtins.bool """ True if we witnessed the node over MQTT instead of LoRA transport """ hops_away: builtins.int """ Number of hops away from us this node is (0 if adjacent) """ is_favorite: builtins.bool """ True if node is in our favorites list Persists between NodeDB internal clean ups """ @property def user(self) -> global___User: """ The user info for this node """ @property def position(self) -> global___Position: """ This position data. Note: before 1.2.14 we would also store the last time we've heard from this node in position.time, that is no longer true. Position.time now indicates the last time we received a POSITION from that node. """ @property def device_metrics(self) -> meshtastic.protobuf.telemetry_pb2.DeviceMetrics: """ The latest device metrics for the node. """ def __init__( self, *, num: builtins.int = ..., user: global___User | None = ..., position: global___Position | None = ..., snr: builtins.float = ..., last_heard: builtins.int = ..., device_metrics: meshtastic.protobuf.telemetry_pb2.DeviceMetrics | None = ..., channel: builtins.int = ..., via_mqtt: builtins.bool = ..., hops_away: builtins.int = ..., is_favorite: builtins.bool = ..., ) -> None: ... def HasField(self, field_name: typing.Literal["device_metrics", b"device_metrics", "position", b"position", "user", b"user"]) -> builtins.bool: ... def ClearField(self, field_name: typing.Literal["channel", b"channel", "device_metrics", b"device_metrics", "hops_away", b"hops_away", "is_favorite", b"is_favorite", "last_heard", b"last_heard", "num", b"num", "position", b"position", "snr", b"snr", "user", b"user", "via_mqtt", b"via_mqtt"]) -> None: ... global___NodeInfo = NodeInfo @typing.final class MyNodeInfo(google.protobuf.message.Message): """ Unique local debugging info for this node Note: we don't include position or the user info, because that will come in the Sent to the phone in response to WantNodes. """ DESCRIPTOR: google.protobuf.descriptor.Descriptor MY_NODE_NUM_FIELD_NUMBER: builtins.int REBOOT_COUNT_FIELD_NUMBER: builtins.int MIN_APP_VERSION_FIELD_NUMBER: builtins.int my_node_num: builtins.int """ Tells the phone what our node number is, default starting value is lowbyte of macaddr, but it will be fixed if that is already in use """ reboot_count: builtins.int """ The total number of reboots this node has ever encountered (well - since the last time we discarded preferences) """ min_app_version: builtins.int """ The minimum app version that can talk to this device. Phone/PC apps should compare this to their build number and if too low tell the user they must update their app """ def __init__( self, *, my_node_num: builtins.int = ..., reboot_count: builtins.int = ..., min_app_version: builtins.int = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["min_app_version", b"min_app_version", "my_node_num", b"my_node_num", "reboot_count", b"reboot_count"]) -> None: ... global___MyNodeInfo = MyNodeInfo @typing.final class LogRecord(google.protobuf.message.Message): """ Debug output from the device. To minimize the size of records inside the device code, if a time/source/level is not set on the message it is assumed to be a continuation of the previously sent message. This allows the device code to use fixed maxlen 64 byte strings for messages, and then extend as needed by emitting multiple records. """ DESCRIPTOR: google.protobuf.descriptor.Descriptor class _Level: ValueType = typing.NewType("ValueType", builtins.int) V: typing_extensions.TypeAlias = ValueType class _LevelEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[LogRecord._Level.ValueType], builtins.type): DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor UNSET: LogRecord._Level.ValueType # 0 """ Log levels, chosen to match python logging conventions. """ CRITICAL: LogRecord._Level.ValueType # 50 """ Log levels, chosen to match python logging conventions. """ ERROR: LogRecord._Level.ValueType # 40 """ Log levels, chosen to match python logging conventions. """ WARNING: LogRecord._Level.ValueType # 30 """ Log levels, chosen to match python logging conventions. """ INFO: LogRecord._Level.ValueType # 20 """ Log levels, chosen to match python logging conventions. """ DEBUG: LogRecord._Level.ValueType # 10 """ Log levels, chosen to match python logging conventions. """ TRACE: LogRecord._Level.ValueType # 5 """ Log levels, chosen to match python logging conventions. """ class Level(_Level, metaclass=_LevelEnumTypeWrapper): """ Log levels, chosen to match python logging conventions. """ UNSET: LogRecord.Level.ValueType # 0 """ Log levels, chosen to match python logging conventions. """ CRITICAL: LogRecord.Level.ValueType # 50 """ Log levels, chosen to match python logging conventions. """ ERROR: LogRecord.Level.ValueType # 40 """ Log levels, chosen to match python logging conventions. """ WARNING: LogRecord.Level.ValueType # 30 """ Log levels, chosen to match python logging conventions. """ INFO: LogRecord.Level.ValueType # 20 """ Log levels, chosen to match python logging conventions. """ DEBUG: LogRecord.Level.ValueType # 10 """ Log levels, chosen to match python logging conventions. """ TRACE: LogRecord.Level.ValueType # 5 """ Log levels, chosen to match python logging conventions. """ MESSAGE_FIELD_NUMBER: builtins.int TIME_FIELD_NUMBER: builtins.int SOURCE_FIELD_NUMBER: builtins.int LEVEL_FIELD_NUMBER: builtins.int message: builtins.str """ Log levels, chosen to match python logging conventions. """ time: builtins.int """ Seconds since 1970 - or 0 for unknown/unset """ source: builtins.str """ Usually based on thread name - if known """ level: global___LogRecord.Level.ValueType """ Not yet set """ def __init__( self, *, message: builtins.str = ..., time: builtins.int = ..., source: builtins.str = ..., level: global___LogRecord.Level.ValueType = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["level", b"level", "message", b"message", "source", b"source", "time", b"time"]) -> None: ... global___LogRecord = LogRecord @typing.final class QueueStatus(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor RES_FIELD_NUMBER: builtins.int FREE_FIELD_NUMBER: builtins.int MAXLEN_FIELD_NUMBER: builtins.int MESH_PACKET_ID_FIELD_NUMBER: builtins.int res: builtins.int """Last attempt to queue status, ErrorCode""" free: builtins.int """Free entries in the outgoing queue""" maxlen: builtins.int """Maximum entries in the outgoing queue""" mesh_packet_id: builtins.int """What was mesh packet id that generated this response?""" def __init__( self, *, res: builtins.int = ..., free: builtins.int = ..., maxlen: builtins.int = ..., mesh_packet_id: builtins.int = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["free", b"free", "maxlen", b"maxlen", "mesh_packet_id", b"mesh_packet_id", "res", b"res"]) -> None: ... global___QueueStatus = QueueStatus @typing.final class FromRadio(google.protobuf.message.Message): """ Packets from the radio to the phone will appear on the fromRadio characteristic. It will support READ and NOTIFY. When a new packet arrives the device will BLE notify? It will sit in that descriptor until consumed by the phone, at which point the next item in the FIFO will be populated. """ DESCRIPTOR: google.protobuf.descriptor.Descriptor ID_FIELD_NUMBER: builtins.int PACKET_FIELD_NUMBER: builtins.int MY_INFO_FIELD_NUMBER: builtins.int NODE_INFO_FIELD_NUMBER: builtins.int CONFIG_FIELD_NUMBER: builtins.int LOG_RECORD_FIELD_NUMBER: builtins.int CONFIG_COMPLETE_ID_FIELD_NUMBER: builtins.int REBOOTED_FIELD_NUMBER: builtins.int MODULECONFIG_FIELD_NUMBER: builtins.int CHANNEL_FIELD_NUMBER: builtins.int QUEUESTATUS_FIELD_NUMBER: builtins.int XMODEMPACKET_FIELD_NUMBER: builtins.int METADATA_FIELD_NUMBER: builtins.int MQTTCLIENTPROXYMESSAGE_FIELD_NUMBER: builtins.int id: builtins.int """ The packet id, used to allow the phone to request missing read packets from the FIFO, see our bluetooth docs """ config_complete_id: builtins.int """ Sent as true once the device has finished sending all of the responses to want_config recipient should check if this ID matches our original request nonce, if not, it means your config responses haven't started yet. NOTE: This ID must not change - to keep (minimal) compatibility with <1.2 version of android apps. """ rebooted: builtins.bool """ Sent to tell clients the radio has just rebooted. Set to true if present. Not used on all transports, currently just used for the serial console. NOTE: This ID must not change - to keep (minimal) compatibility with <1.2 version of android apps. """ @property def packet(self) -> global___MeshPacket: """ Log levels, chosen to match python logging conventions. """ @property def my_info(self) -> global___MyNodeInfo: """ Tells the phone what our node number is, can be -1 if we've not yet joined a mesh. NOTE: This ID must not change - to keep (minimal) compatibility with <1.2 version of android apps. """ @property def node_info(self) -> global___NodeInfo: """ One packet is sent for each node in the on radio DB starts over with the first node in our DB """ @property def config(self) -> meshtastic.protobuf.config_pb2.Config: """ Include a part of the config (was: RadioConfig radio) """ @property def log_record(self) -> global___LogRecord: """ Set to send debug console output over our protobuf stream """ @property def moduleConfig(self) -> meshtastic.protobuf.module_config_pb2.ModuleConfig: """ Include module config """ @property def channel(self) -> meshtastic.protobuf.channel_pb2.Channel: """ One packet is sent for each channel """ @property def queueStatus(self) -> global___QueueStatus: """ Queue status info """ @property def xmodemPacket(self) -> meshtastic.protobuf.xmodem_pb2.XModem: """ File Transfer Chunk """ @property def metadata(self) -> global___DeviceMetadata: """ Device metadata message """ @property def mqttClientProxyMessage(self) -> global___MqttClientProxyMessage: """ MQTT Client Proxy Message (device sending to client / phone for publishing to MQTT) """ def __init__( self, *, id: builtins.int = ..., packet: global___MeshPacket | None = ..., my_info: global___MyNodeInfo | None = ..., node_info: global___NodeInfo | None = ..., config: meshtastic.protobuf.config_pb2.Config | None = ..., log_record: global___LogRecord | None = ..., config_complete_id: builtins.int = ..., rebooted: builtins.bool = ..., moduleConfig: meshtastic.protobuf.module_config_pb2.ModuleConfig | None = ..., channel: meshtastic.protobuf.channel_pb2.Channel | None = ..., queueStatus: global___QueueStatus | None = ..., xmodemPacket: meshtastic.protobuf.xmodem_pb2.XModem | None = ..., metadata: global___DeviceMetadata | None = ..., mqttClientProxyMessage: global___MqttClientProxyMessage | None = ..., ) -> None: ... def HasField(self, field_name: typing.Literal["channel", b"channel", "config", b"config", "config_complete_id", b"config_complete_id", "log_record", b"log_record", "metadata", b"metadata", "moduleConfig", b"moduleConfig", "mqttClientProxyMessage", b"mqttClientProxyMessage", "my_info", b"my_info", "node_info", b"node_info", "packet", b"packet", "payload_variant", b"payload_variant", "queueStatus", b"queueStatus", "rebooted", b"rebooted", "xmodemPacket", b"xmodemPacket"]) -> builtins.bool: ... def ClearField(self, field_name: typing.Literal["channel", b"channel", "config", b"config", "config_complete_id", b"config_complete_id", "id", b"id", "log_record", b"log_record", "metadata", b"metadata", "moduleConfig", b"moduleConfig", "mqttClientProxyMessage", b"mqttClientProxyMessage", "my_info", b"my_info", "node_info", b"node_info", "packet", b"packet", "payload_variant", b"payload_variant", "queueStatus", b"queueStatus", "rebooted", b"rebooted", "xmodemPacket", b"xmodemPacket"]) -> None: ... def WhichOneof(self, oneof_group: typing.Literal["payload_variant", b"payload_variant"]) -> typing.Literal["packet", "my_info", "node_info", "config", "log_record", "config_complete_id", "rebooted", "moduleConfig", "channel", "queueStatus", "xmodemPacket", "metadata", "mqttClientProxyMessage"] | None: ... global___FromRadio = FromRadio @typing.final class ToRadio(google.protobuf.message.Message): """ Packets/commands to the radio will be written (reliably) to the toRadio characteristic. Once the write completes the phone can assume it is handled. """ DESCRIPTOR: google.protobuf.descriptor.Descriptor PACKET_FIELD_NUMBER: builtins.int WANT_CONFIG_ID_FIELD_NUMBER: builtins.int DISCONNECT_FIELD_NUMBER: builtins.int XMODEMPACKET_FIELD_NUMBER: builtins.int MQTTCLIENTPROXYMESSAGE_FIELD_NUMBER: builtins.int HEARTBEAT_FIELD_NUMBER: builtins.int want_config_id: builtins.int """ Phone wants radio to send full node db to the phone, This is typically the first packet sent to the radio when the phone gets a bluetooth connection. The radio will respond by sending back a MyNodeInfo, a owner, a radio config and a series of FromRadio.node_infos, and config_complete the integer you write into this field will be reported back in the config_complete_id response this allows clients to never be confused by a stale old partially sent config. """ disconnect: builtins.bool """ Tell API server we are disconnecting now. This is useful for serial links where there is no hardware/protocol based notification that the client has dropped the link. (Sending this message is optional for clients) """ @property def packet(self) -> global___MeshPacket: """ Send this packet on the mesh """ @property def xmodemPacket(self) -> meshtastic.protobuf.xmodem_pb2.XModem: """ File Transfer Chunk """ @property def mqttClientProxyMessage(self) -> global___MqttClientProxyMessage: """ MQTT Client Proxy Message (for client / phone subscribed to MQTT sending to device) """ @property def heartbeat(self) -> global___Heartbeat: """ Heartbeat message (used to keep the device connection awake on serial) """ def __init__( self, *, packet: global___MeshPacket | None = ..., want_config_id: builtins.int = ..., disconnect: builtins.bool = ..., xmodemPacket: meshtastic.protobuf.xmodem_pb2.XModem | None = ..., mqttClientProxyMessage: global___MqttClientProxyMessage | None = ..., heartbeat: global___Heartbeat | None = ..., ) -> None: ... def HasField(self, field_name: typing.Literal["disconnect", b"disconnect", "heartbeat", b"heartbeat", "mqttClientProxyMessage", b"mqttClientProxyMessage", "packet", b"packet", "payload_variant", b"payload_variant", "want_config_id", b"want_config_id", "xmodemPacket", b"xmodemPacket"]) -> builtins.bool: ... def ClearField(self, field_name: typing.Literal["disconnect", b"disconnect", "heartbeat", b"heartbeat", "mqttClientProxyMessage", b"mqttClientProxyMessage", "packet", b"packet", "payload_variant", b"payload_variant", "want_config_id", b"want_config_id", "xmodemPacket", b"xmodemPacket"]) -> None: ... def WhichOneof(self, oneof_group: typing.Literal["payload_variant", b"payload_variant"]) -> typing.Literal["packet", "want_config_id", "disconnect", "xmodemPacket", "mqttClientProxyMessage", "heartbeat"] | None: ... global___ToRadio = ToRadio @typing.final class Compressed(google.protobuf.message.Message): """ Compressed message payload """ DESCRIPTOR: google.protobuf.descriptor.Descriptor PORTNUM_FIELD_NUMBER: builtins.int DATA_FIELD_NUMBER: builtins.int portnum: meshtastic.protobuf.portnums_pb2.PortNum.ValueType """ PortNum to determine the how to handle the compressed payload. """ data: builtins.bytes """ Compressed data. """ def __init__( self, *, portnum: meshtastic.protobuf.portnums_pb2.PortNum.ValueType = ..., data: builtins.bytes = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["data", b"data", "portnum", b"portnum"]) -> None: ... global___Compressed = Compressed @typing.final class NeighborInfo(google.protobuf.message.Message): """ Full info on edges for a single node """ DESCRIPTOR: google.protobuf.descriptor.Descriptor NODE_ID_FIELD_NUMBER: builtins.int LAST_SENT_BY_ID_FIELD_NUMBER: builtins.int NODE_BROADCAST_INTERVAL_SECS_FIELD_NUMBER: builtins.int NEIGHBORS_FIELD_NUMBER: builtins.int node_id: builtins.int """ The node ID of the node sending info on its neighbors """ last_sent_by_id: builtins.int """ Field to pass neighbor info for the next sending cycle """ node_broadcast_interval_secs: builtins.int """ Broadcast interval of the represented node (in seconds) """ @property def neighbors(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Neighbor]: """ The list of out edges from this node """ def __init__( self, *, node_id: builtins.int = ..., last_sent_by_id: builtins.int = ..., node_broadcast_interval_secs: builtins.int = ..., neighbors: collections.abc.Iterable[global___Neighbor] | None = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["last_sent_by_id", b"last_sent_by_id", "neighbors", b"neighbors", "node_broadcast_interval_secs", b"node_broadcast_interval_secs", "node_id", b"node_id"]) -> None: ... global___NeighborInfo = NeighborInfo @typing.final class Neighbor(google.protobuf.message.Message): """ A single edge in the mesh """ DESCRIPTOR: google.protobuf.descriptor.Descriptor NODE_ID_FIELD_NUMBER: builtins.int SNR_FIELD_NUMBER: builtins.int LAST_RX_TIME_FIELD_NUMBER: builtins.int NODE_BROADCAST_INTERVAL_SECS_FIELD_NUMBER: builtins.int node_id: builtins.int """ Node ID of neighbor """ snr: builtins.float """ SNR of last heard message """ last_rx_time: builtins.int """ Reception time (in secs since 1970) of last message that was last sent by this ID. Note: this is for local storage only and will not be sent out over the mesh. """ node_broadcast_interval_secs: builtins.int """ Broadcast interval of this neighbor (in seconds). Note: this is for local storage only and will not be sent out over the mesh. """ def __init__( self, *, node_id: builtins.int = ..., snr: builtins.float = ..., last_rx_time: builtins.int = ..., node_broadcast_interval_secs: builtins.int = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["last_rx_time", b"last_rx_time", "node_broadcast_interval_secs", b"node_broadcast_interval_secs", "node_id", b"node_id", "snr", b"snr"]) -> None: ... global___Neighbor = Neighbor @typing.final class DeviceMetadata(google.protobuf.message.Message): """ Device metadata response """ DESCRIPTOR: google.protobuf.descriptor.Descriptor FIRMWARE_VERSION_FIELD_NUMBER: builtins.int DEVICE_STATE_VERSION_FIELD_NUMBER: builtins.int CANSHUTDOWN_FIELD_NUMBER: builtins.int HASWIFI_FIELD_NUMBER: builtins.int HASBLUETOOTH_FIELD_NUMBER: builtins.int HASETHERNET_FIELD_NUMBER: builtins.int ROLE_FIELD_NUMBER: builtins.int POSITION_FLAGS_FIELD_NUMBER: builtins.int HW_MODEL_FIELD_NUMBER: builtins.int HASREMOTEHARDWARE_FIELD_NUMBER: builtins.int firmware_version: builtins.str """ Device firmware version string """ device_state_version: builtins.int """ Device state version """ canShutdown: builtins.bool """ Indicates whether the device can shutdown CPU natively or via power management chip """ hasWifi: builtins.bool """ Indicates that the device has native wifi capability """ hasBluetooth: builtins.bool """ Indicates that the device has native bluetooth capability """ hasEthernet: builtins.bool """ Indicates that the device has an ethernet peripheral """ role: meshtastic.protobuf.config_pb2.Config.DeviceConfig.Role.ValueType """ Indicates that the device's role in the mesh """ position_flags: builtins.int """ Indicates the device's current enabled position flags """ hw_model: global___HardwareModel.ValueType """ Device hardware model """ hasRemoteHardware: builtins.bool """ Has Remote Hardware enabled """ def __init__( self, *, firmware_version: builtins.str = ..., device_state_version: builtins.int = ..., canShutdown: builtins.bool = ..., hasWifi: builtins.bool = ..., hasBluetooth: builtins.bool = ..., hasEthernet: builtins.bool = ..., role: meshtastic.protobuf.config_pb2.Config.DeviceConfig.Role.ValueType = ..., position_flags: builtins.int = ..., hw_model: global___HardwareModel.ValueType = ..., hasRemoteHardware: builtins.bool = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["canShutdown", b"canShutdown", "device_state_version", b"device_state_version", "firmware_version", b"firmware_version", "hasBluetooth", b"hasBluetooth", "hasEthernet", b"hasEthernet", "hasRemoteHardware", b"hasRemoteHardware", "hasWifi", b"hasWifi", "hw_model", b"hw_model", "position_flags", b"position_flags", "role", b"role"]) -> None: ... global___DeviceMetadata = DeviceMetadata @typing.final class Heartbeat(google.protobuf.message.Message): """ A heartbeat message is sent to the node from the client to keep the connection alive. This is currently only needed to keep serial connections alive, but can be used by any PhoneAPI. """ DESCRIPTOR: google.protobuf.descriptor.Descriptor def __init__( self, ) -> None: ... global___Heartbeat = Heartbeat @typing.final class NodeRemoteHardwarePin(google.protobuf.message.Message): """ RemoteHardwarePins associated with a node """ DESCRIPTOR: google.protobuf.descriptor.Descriptor NODE_NUM_FIELD_NUMBER: builtins.int PIN_FIELD_NUMBER: builtins.int node_num: builtins.int """ The node_num exposing the available gpio pin """ @property def pin(self) -> meshtastic.protobuf.module_config_pb2.RemoteHardwarePin: """ The the available gpio pin for usage with RemoteHardware module """ def __init__( self, *, node_num: builtins.int = ..., pin: meshtastic.protobuf.module_config_pb2.RemoteHardwarePin | None = ..., ) -> None: ... def HasField(self, field_name: typing.Literal["pin", b"pin"]) -> builtins.bool: ... def ClearField(self, field_name: typing.Literal["node_num", b"node_num", "pin", b"pin"]) -> None: ... global___NodeRemoteHardwarePin = NodeRemoteHardwarePin @typing.final class ChunkedPayload(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor PAYLOAD_ID_FIELD_NUMBER: builtins.int CHUNK_COUNT_FIELD_NUMBER: builtins.int CHUNK_INDEX_FIELD_NUMBER: builtins.int PAYLOAD_CHUNK_FIELD_NUMBER: builtins.int payload_id: builtins.int """ The ID of the entire payload """ chunk_count: builtins.int """ The total number of chunks in the payload """ chunk_index: builtins.int """ The current chunk index in the total """ payload_chunk: builtins.bytes """ The binary data of the current chunk """ def __init__( self, *, payload_id: builtins.int = ..., chunk_count: builtins.int = ..., chunk_index: builtins.int = ..., payload_chunk: builtins.bytes = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["chunk_count", b"chunk_count", "chunk_index", b"chunk_index", "payload_chunk", b"payload_chunk", "payload_id", b"payload_id"]) -> None: ... global___ChunkedPayload = ChunkedPayload @typing.final class resend_chunks(google.protobuf.message.Message): """ Wrapper message for broken repeated oneof support """ DESCRIPTOR: google.protobuf.descriptor.Descriptor CHUNKS_FIELD_NUMBER: builtins.int @property def chunks(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.int]: ... def __init__( self, *, chunks: collections.abc.Iterable[builtins.int] | None = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["chunks", b"chunks"]) -> None: ... global___resend_chunks = resend_chunks @typing.final class ChunkedPayloadResponse(google.protobuf.message.Message): """ Responses to a ChunkedPayload request """ DESCRIPTOR: google.protobuf.descriptor.Descriptor PAYLOAD_ID_FIELD_NUMBER: builtins.int REQUEST_TRANSFER_FIELD_NUMBER: builtins.int ACCEPT_TRANSFER_FIELD_NUMBER: builtins.int RESEND_CHUNKS_FIELD_NUMBER: builtins.int payload_id: builtins.int """ The ID of the entire payload """ request_transfer: builtins.bool """ Request to transfer chunked payload """ accept_transfer: builtins.bool """ Accept the transfer chunked payload """ @property def resend_chunks(self) -> global___resend_chunks: """ Request missing indexes in the chunked payload """ def __init__( self, *, payload_id: builtins.int = ..., request_transfer: builtins.bool = ..., accept_transfer: builtins.bool = ..., resend_chunks: global___resend_chunks | None = ..., ) -> None: ... def HasField(self, field_name: typing.Literal["accept_transfer", b"accept_transfer", "payload_variant", b"payload_variant", "request_transfer", b"request_transfer", "resend_chunks", b"resend_chunks"]) -> builtins.bool: ... def ClearField(self, field_name: typing.Literal["accept_transfer", b"accept_transfer", "payload_id", b"payload_id", "payload_variant", b"payload_variant", "request_transfer", b"request_transfer", "resend_chunks", b"resend_chunks"]) -> None: ... def WhichOneof(self, oneof_group: typing.Literal["payload_variant", b"payload_variant"]) -> typing.Literal["request_transfer", "accept_transfer", "resend_chunks"] | None: ... global___ChunkedPayloadResponse = ChunkedPayloadResponse python-2.3.14/meshtastic/protobuf/module_config_pb2.py000066400000000000000000000301221464266072200230640ustar00rootroot00000000000000# -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # source: meshtastic/protobuf/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 symbol_database as _symbol_database from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\'meshtastic/protobuf/module_config.proto\x12\x13meshtastic.protobuf\"\x90$\n\x0cModuleConfig\x12<\n\x04mqtt\x18\x01 \x01(\x0b\x32,.meshtastic.protobuf.ModuleConfig.MQTTConfigH\x00\x12@\n\x06serial\x18\x02 \x01(\x0b\x32..meshtastic.protobuf.ModuleConfig.SerialConfigH\x00\x12]\n\x15\x65xternal_notification\x18\x03 \x01(\x0b\x32<.meshtastic.protobuf.ModuleConfig.ExternalNotificationConfigH\x00\x12M\n\rstore_forward\x18\x04 \x01(\x0b\x32\x34.meshtastic.protobuf.ModuleConfig.StoreForwardConfigH\x00\x12G\n\nrange_test\x18\x05 \x01(\x0b\x32\x31.meshtastic.protobuf.ModuleConfig.RangeTestConfigH\x00\x12\x46\n\ttelemetry\x18\x06 \x01(\x0b\x32\x31.meshtastic.protobuf.ModuleConfig.TelemetryConfigH\x00\x12O\n\x0e\x63\x61nned_message\x18\x07 \x01(\x0b\x32\x35.meshtastic.protobuf.ModuleConfig.CannedMessageConfigH\x00\x12>\n\x05\x61udio\x18\x08 \x01(\x0b\x32-.meshtastic.protobuf.ModuleConfig.AudioConfigH\x00\x12Q\n\x0fremote_hardware\x18\t \x01(\x0b\x32\x36.meshtastic.protobuf.ModuleConfig.RemoteHardwareConfigH\x00\x12M\n\rneighbor_info\x18\n \x01(\x0b\x32\x34.meshtastic.protobuf.ModuleConfig.NeighborInfoConfigH\x00\x12S\n\x10\x61mbient_lighting\x18\x0b \x01(\x0b\x32\x37.meshtastic.protobuf.ModuleConfig.AmbientLightingConfigH\x00\x12S\n\x10\x64\x65tection_sensor\x18\x0c \x01(\x0b\x32\x37.meshtastic.protobuf.ModuleConfig.DetectionSensorConfigH\x00\x12H\n\npaxcounter\x18\r \x01(\x0b\x32\x32.meshtastic.protobuf.ModuleConfig.PaxcounterConfigH\x00\x1a\xb9\x02\n\nMQTTConfig\x12\x0f\n\x07\x65nabled\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\x12\x14\n\x0cjson_enabled\x18\x06 \x01(\x08\x12\x13\n\x0btls_enabled\x18\x07 \x01(\x08\x12\x0c\n\x04root\x18\x08 \x01(\t\x12\x1f\n\x17proxy_to_client_enabled\x18\t \x01(\x08\x12\x1d\n\x15map_reporting_enabled\x18\n \x01(\x08\x12P\n\x13map_report_settings\x18\x0b \x01(\x0b\x32\x33.meshtastic.protobuf.ModuleConfig.MapReportSettings\x1aN\n\x11MapReportSettings\x12\x1d\n\x15publish_interval_secs\x18\x01 \x01(\r\x12\x1a\n\x12position_precision\x18\x02 \x01(\r\x1a\x8b\x01\n\x14RemoteHardwareConfig\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\"\n\x1a\x61llow_undefined_pin_access\x18\x02 \x01(\x08\x12>\n\x0e\x61vailable_pins\x18\x03 \x03(\x0b\x32&.meshtastic.protobuf.RemoteHardwarePin\x1a>\n\x12NeighborInfoConfig\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x17\n\x0fupdate_interval\x18\x02 \x01(\r\x1a\xd2\x01\n\x15\x44\x65tectionSensorConfig\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x1e\n\x16minimum_broadcast_secs\x18\x02 \x01(\r\x12\x1c\n\x14state_broadcast_secs\x18\x03 \x01(\r\x12\x11\n\tsend_bell\x18\x04 \x01(\x08\x12\x0c\n\x04name\x18\x05 \x01(\t\x12\x13\n\x0bmonitor_pin\x18\x06 \x01(\r\x12 \n\x18\x64\x65tection_triggered_high\x18\x07 \x01(\x08\x12\x12\n\nuse_pullup\x18\x08 \x01(\x08\x1a\xed\x02\n\x0b\x41udioConfig\x12\x16\n\x0e\x63odec2_enabled\x18\x01 \x01(\x08\x12\x0f\n\x07ptt_pin\x18\x02 \x01(\r\x12I\n\x07\x62itrate\x18\x03 \x01(\x0e\x32\x38.meshtastic.protobuf.ModuleConfig.AudioConfig.Audio_Baud\x12\x0e\n\x06i2s_ws\x18\x04 \x01(\r\x12\x0e\n\x06i2s_sd\x18\x05 \x01(\r\x12\x0f\n\x07i2s_din\x18\x06 \x01(\r\x12\x0f\n\x07i2s_sck\x18\x07 \x01(\r\"\xa7\x01\n\nAudio_Baud\x12\x12\n\x0e\x43ODEC2_DEFAULT\x10\x00\x12\x0f\n\x0b\x43ODEC2_3200\x10\x01\x12\x0f\n\x0b\x43ODEC2_2400\x10\x02\x12\x0f\n\x0b\x43ODEC2_1600\x10\x03\x12\x0f\n\x0b\x43ODEC2_1400\x10\x04\x12\x0f\n\x0b\x43ODEC2_1300\x10\x05\x12\x0f\n\x0b\x43ODEC2_1200\x10\x06\x12\x0e\n\nCODEC2_700\x10\x07\x12\x0f\n\x0b\x43ODEC2_700B\x10\x08\x1av\n\x10PaxcounterConfig\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\"\n\x1apaxcounter_update_interval\x18\x02 \x01(\r\x12\x16\n\x0ewifi_threshold\x18\x03 \x01(\x05\x12\x15\n\rble_threshold\x18\x04 \x01(\x05\x1a\xf6\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\x12H\n\x04\x62\x61ud\x18\x05 \x01(\x0e\x32:.meshtastic.protobuf.ModuleConfig.SerialConfig.Serial_Baud\x12\x0f\n\x07timeout\x18\x06 \x01(\r\x12H\n\x04mode\x18\x07 \x01(\x0e\x32:.meshtastic.protobuf.ModuleConfig.SerialConfig.Serial_Mode\x12$\n\x1coverride_console_serial_port\x18\x08 \x01(\x08\"\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\"U\n\x0bSerial_Mode\x12\x0b\n\x07\x44\x45\x46\x41ULT\x10\x00\x12\n\n\x06SIMPLE\x10\x01\x12\t\n\x05PROTO\x10\x02\x12\x0b\n\x07TEXTMSG\x10\x03\x12\x08\n\x04NMEA\x10\x04\x12\x0b\n\x07\x43\x41LTOPO\x10\x05\x1a\xe9\x02\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\x14\n\x0coutput_vibra\x18\x08 \x01(\r\x12\x15\n\routput_buzzer\x18\t \x01(\r\x12\x0e\n\x06\x61\x63tive\x18\x04 \x01(\x08\x12\x15\n\ralert_message\x18\x05 \x01(\x08\x12\x1b\n\x13\x61lert_message_vibra\x18\n \x01(\x08\x12\x1c\n\x14\x61lert_message_buzzer\x18\x0b \x01(\x08\x12\x12\n\nalert_bell\x18\x06 \x01(\x08\x12\x18\n\x10\x61lert_bell_vibra\x18\x0c \x01(\x08\x12\x19\n\x11\x61lert_bell_buzzer\x18\r \x01(\x08\x12\x0f\n\x07use_pwm\x18\x07 \x01(\x08\x12\x13\n\x0bnag_timeout\x18\x0e \x01(\r\x12\x19\n\x11use_i2s_as_buzzer\x18\x0f \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\xe6\x02\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\x05 \x01(\x08\x12\x1b\n\x13\x61ir_quality_enabled\x18\x06 \x01(\x08\x12\x1c\n\x14\x61ir_quality_interval\x18\x07 \x01(\r\x12!\n\x19power_measurement_enabled\x18\x08 \x01(\x08\x12\x1d\n\x15power_update_interval\x18\t \x01(\r\x12\x1c\n\x14power_screen_enabled\x18\n \x01(\x08\x1a\xf1\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\x12\x62\n\x14inputbroker_event_cw\x18\x05 \x01(\x0e\x32\x44.meshtastic.protobuf.ModuleConfig.CannedMessageConfig.InputEventChar\x12\x63\n\x15inputbroker_event_ccw\x18\x06 \x01(\x0e\x32\x44.meshtastic.protobuf.ModuleConfig.CannedMessageConfig.InputEventChar\x12\x65\n\x17inputbroker_event_press\x18\x07 \x01(\x0e\x32\x44.meshtastic.protobuf.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\"c\n\x0eInputEventChar\x12\x08\n\x04NONE\x10\x00\x12\x06\n\x02UP\x10\x11\x12\x08\n\x04\x44OWN\x10\x12\x12\x08\n\x04LEFT\x10\x13\x12\t\n\x05RIGHT\x10\x14\x12\n\n\x06SELECT\x10\n\x12\x08\n\x04\x42\x41\x43K\x10\x1b\x12\n\n\x06\x43\x41NCEL\x10\x18\x1a\x65\n\x15\x41mbientLightingConfig\x12\x11\n\tled_state\x18\x01 \x01(\x08\x12\x0f\n\x07\x63urrent\x18\x02 \x01(\r\x12\x0b\n\x03red\x18\x03 \x01(\r\x12\r\n\x05green\x18\x04 \x01(\r\x12\x0c\n\x04\x62lue\x18\x05 \x01(\rB\x11\n\x0fpayload_variant\"m\n\x11RemoteHardwarePin\x12\x10\n\x08gpio_pin\x18\x01 \x01(\r\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x38\n\x04type\x18\x03 \x01(\x0e\x32*.meshtastic.protobuf.RemoteHardwarePinType*I\n\x15RemoteHardwarePinType\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x10\n\x0c\x44IGITAL_READ\x10\x01\x12\x11\n\rDIGITAL_WRITE\x10\x02\x42g\n\x13\x63om.geeksville.meshB\x12ModuleConfigProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.module_config_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\022ModuleConfigProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' _globals['_REMOTEHARDWAREPINTYPE']._serialized_start=4802 _globals['_REMOTEHARDWAREPINTYPE']._serialized_end=4875 _globals['_MODULECONFIG']._serialized_start=65 _globals['_MODULECONFIG']._serialized_end=4689 _globals['_MODULECONFIG_MQTTCONFIG']._serialized_start=1080 _globals['_MODULECONFIG_MQTTCONFIG']._serialized_end=1393 _globals['_MODULECONFIG_MAPREPORTSETTINGS']._serialized_start=1395 _globals['_MODULECONFIG_MAPREPORTSETTINGS']._serialized_end=1473 _globals['_MODULECONFIG_REMOTEHARDWARECONFIG']._serialized_start=1476 _globals['_MODULECONFIG_REMOTEHARDWARECONFIG']._serialized_end=1615 _globals['_MODULECONFIG_NEIGHBORINFOCONFIG']._serialized_start=1617 _globals['_MODULECONFIG_NEIGHBORINFOCONFIG']._serialized_end=1679 _globals['_MODULECONFIG_DETECTIONSENSORCONFIG']._serialized_start=1682 _globals['_MODULECONFIG_DETECTIONSENSORCONFIG']._serialized_end=1892 _globals['_MODULECONFIG_AUDIOCONFIG']._serialized_start=1895 _globals['_MODULECONFIG_AUDIOCONFIG']._serialized_end=2260 _globals['_MODULECONFIG_AUDIOCONFIG_AUDIO_BAUD']._serialized_start=2093 _globals['_MODULECONFIG_AUDIOCONFIG_AUDIO_BAUD']._serialized_end=2260 _globals['_MODULECONFIG_PAXCOUNTERCONFIG']._serialized_start=2262 _globals['_MODULECONFIG_PAXCOUNTERCONFIG']._serialized_end=2380 _globals['_MODULECONFIG_SERIALCONFIG']._serialized_start=2383 _globals['_MODULECONFIG_SERIALCONFIG']._serialized_end=3013 _globals['_MODULECONFIG_SERIALCONFIG_SERIAL_BAUD']._serialized_start=2660 _globals['_MODULECONFIG_SERIALCONFIG_SERIAL_BAUD']._serialized_end=2926 _globals['_MODULECONFIG_SERIALCONFIG_SERIAL_MODE']._serialized_start=2928 _globals['_MODULECONFIG_SERIALCONFIG_SERIAL_MODE']._serialized_end=3013 _globals['_MODULECONFIG_EXTERNALNOTIFICATIONCONFIG']._serialized_start=3016 _globals['_MODULECONFIG_EXTERNALNOTIFICATIONCONFIG']._serialized_end=3377 _globals['_MODULECONFIG_STOREFORWARDCONFIG']._serialized_start=3380 _globals['_MODULECONFIG_STOREFORWARDCONFIG']._serialized_end=3512 _globals['_MODULECONFIG_RANGETESTCONFIG']._serialized_start=3514 _globals['_MODULECONFIG_RANGETESTCONFIG']._serialized_end=3578 _globals['_MODULECONFIG_TELEMETRYCONFIG']._serialized_start=3581 _globals['_MODULECONFIG_TELEMETRYCONFIG']._serialized_end=3939 _globals['_MODULECONFIG_CANNEDMESSAGECONFIG']._serialized_start=3942 _globals['_MODULECONFIG_CANNEDMESSAGECONFIG']._serialized_end=4567 _globals['_MODULECONFIG_CANNEDMESSAGECONFIG_INPUTEVENTCHAR']._serialized_start=4468 _globals['_MODULECONFIG_CANNEDMESSAGECONFIG_INPUTEVENTCHAR']._serialized_end=4567 _globals['_MODULECONFIG_AMBIENTLIGHTINGCONFIG']._serialized_start=4569 _globals['_MODULECONFIG_AMBIENTLIGHTINGCONFIG']._serialized_end=4670 _globals['_REMOTEHARDWAREPIN']._serialized_start=4691 _globals['_REMOTEHARDWAREPIN']._serialized_end=4800 # @@protoc_insertion_point(module_scope) python-2.3.14/meshtastic/protobuf/module_config_pb2.pyi000066400000000000000000001374641464266072200232560ustar00rootroot00000000000000""" @generated by mypy-protobuf. Do not edit manually! isort:skip_file """ import builtins import collections.abc import google.protobuf.descriptor import google.protobuf.internal.containers import google.protobuf.internal.enum_type_wrapper import google.protobuf.message import sys import typing if sys.version_info >= (3, 10): import typing as typing_extensions else: import typing_extensions DESCRIPTOR: google.protobuf.descriptor.FileDescriptor class _RemoteHardwarePinType: ValueType = typing.NewType("ValueType", builtins.int) V: typing_extensions.TypeAlias = ValueType class _RemoteHardwarePinTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_RemoteHardwarePinType.ValueType], builtins.type): DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor UNKNOWN: _RemoteHardwarePinType.ValueType # 0 """ Unset/unused """ DIGITAL_READ: _RemoteHardwarePinType.ValueType # 1 """ GPIO pin can be read (if it is high / low) """ DIGITAL_WRITE: _RemoteHardwarePinType.ValueType # 2 """ GPIO pin can be written to (high / low) """ class RemoteHardwarePinType(_RemoteHardwarePinType, metaclass=_RemoteHardwarePinTypeEnumTypeWrapper): ... UNKNOWN: RemoteHardwarePinType.ValueType # 0 """ Unset/unused """ DIGITAL_READ: RemoteHardwarePinType.ValueType # 1 """ GPIO pin can be read (if it is high / low) """ DIGITAL_WRITE: RemoteHardwarePinType.ValueType # 2 """ GPIO pin can be written to (high / low) """ global___RemoteHardwarePinType = RemoteHardwarePinType @typing.final class ModuleConfig(google.protobuf.message.Message): """ Module Config """ DESCRIPTOR: google.protobuf.descriptor.Descriptor @typing.final class MQTTConfig(google.protobuf.message.Message): """ MQTT Client Config """ DESCRIPTOR: google.protobuf.descriptor.Descriptor ENABLED_FIELD_NUMBER: builtins.int ADDRESS_FIELD_NUMBER: builtins.int USERNAME_FIELD_NUMBER: builtins.int PASSWORD_FIELD_NUMBER: builtins.int ENCRYPTION_ENABLED_FIELD_NUMBER: builtins.int JSON_ENABLED_FIELD_NUMBER: builtins.int TLS_ENABLED_FIELD_NUMBER: builtins.int ROOT_FIELD_NUMBER: builtins.int PROXY_TO_CLIENT_ENABLED_FIELD_NUMBER: builtins.int MAP_REPORTING_ENABLED_FIELD_NUMBER: builtins.int MAP_REPORT_SETTINGS_FIELD_NUMBER: builtins.int enabled: builtins.bool """ If a meshtastic node is able to reach the internet it will normally attempt to gateway any channels that are marked as is_uplink_enabled or is_downlink_enabled. """ address: builtins.str """ The server to use for our MQTT global message gateway feature. If not set, the default server will be used """ username: builtins.str """ MQTT username to use (most useful for a custom MQTT server). If using a custom server, this will be honoured even if empty. If using the default server, this will only be honoured if set, otherwise the device will use the default username """ password: builtins.str """ MQTT password to use (most useful for a custom MQTT server). If using a custom server, this will be honoured even if empty. If using the default server, this will only be honoured if set, otherwise the device will use the default password """ encryption_enabled: builtins.bool """ Whether to send encrypted or decrypted packets to MQTT. This parameter is only honoured if you also set server (the default official mqtt.meshtastic.org server can handle encrypted packets) Decrypted packets may be useful for external systems that want to consume meshtastic packets """ json_enabled: builtins.bool """ Whether to send / consume json packets on MQTT """ tls_enabled: builtins.bool """ If true, we attempt to establish a secure connection using TLS """ root: builtins.str """ The root topic to use for MQTT messages. Default is "msh". This is useful if you want to use a single MQTT server for multiple meshtastic networks and separate them via ACLs """ proxy_to_client_enabled: builtins.bool """ If true, we can use the connected phone / client to proxy messages to MQTT instead of a direct connection """ map_reporting_enabled: builtins.bool """ If true, we will periodically report unencrypted information about our node to a map via MQTT """ @property def map_report_settings(self) -> global___ModuleConfig.MapReportSettings: """ Settings for reporting information about our node to a map via MQTT """ def __init__( self, *, enabled: builtins.bool = ..., address: builtins.str = ..., username: builtins.str = ..., password: builtins.str = ..., encryption_enabled: builtins.bool = ..., json_enabled: builtins.bool = ..., tls_enabled: builtins.bool = ..., root: builtins.str = ..., proxy_to_client_enabled: builtins.bool = ..., map_reporting_enabled: builtins.bool = ..., map_report_settings: global___ModuleConfig.MapReportSettings | None = ..., ) -> None: ... def HasField(self, field_name: typing.Literal["map_report_settings", b"map_report_settings"]) -> builtins.bool: ... def ClearField(self, field_name: typing.Literal["address", b"address", "enabled", b"enabled", "encryption_enabled", b"encryption_enabled", "json_enabled", b"json_enabled", "map_report_settings", b"map_report_settings", "map_reporting_enabled", b"map_reporting_enabled", "password", b"password", "proxy_to_client_enabled", b"proxy_to_client_enabled", "root", b"root", "tls_enabled", b"tls_enabled", "username", b"username"]) -> None: ... @typing.final class MapReportSettings(google.protobuf.message.Message): """ Settings for reporting unencrypted information about our node to a map via MQTT """ DESCRIPTOR: google.protobuf.descriptor.Descriptor PUBLISH_INTERVAL_SECS_FIELD_NUMBER: builtins.int POSITION_PRECISION_FIELD_NUMBER: builtins.int publish_interval_secs: builtins.int """ How often we should report our info to the map (in seconds) """ position_precision: builtins.int """ Bits of precision for the location sent (default of 32 is full precision). """ def __init__( self, *, publish_interval_secs: builtins.int = ..., position_precision: builtins.int = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["position_precision", b"position_precision", "publish_interval_secs", b"publish_interval_secs"]) -> None: ... @typing.final class RemoteHardwareConfig(google.protobuf.message.Message): """ RemoteHardwareModule Config """ DESCRIPTOR: google.protobuf.descriptor.Descriptor ENABLED_FIELD_NUMBER: builtins.int ALLOW_UNDEFINED_PIN_ACCESS_FIELD_NUMBER: builtins.int AVAILABLE_PINS_FIELD_NUMBER: builtins.int enabled: builtins.bool """ Whether the Module is enabled """ allow_undefined_pin_access: builtins.bool """ Whether the Module allows consumers to read / write to pins not defined in available_pins """ @property def available_pins(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___RemoteHardwarePin]: """ Exposes the available pins to the mesh for reading and writing """ def __init__( self, *, enabled: builtins.bool = ..., allow_undefined_pin_access: builtins.bool = ..., available_pins: collections.abc.Iterable[global___RemoteHardwarePin] | None = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["allow_undefined_pin_access", b"allow_undefined_pin_access", "available_pins", b"available_pins", "enabled", b"enabled"]) -> None: ... @typing.final class NeighborInfoConfig(google.protobuf.message.Message): """ NeighborInfoModule Config """ DESCRIPTOR: google.protobuf.descriptor.Descriptor ENABLED_FIELD_NUMBER: builtins.int UPDATE_INTERVAL_FIELD_NUMBER: builtins.int enabled: builtins.bool """ Whether the Module is enabled """ update_interval: builtins.int """ Interval in seconds of how often we should try to send our Neighbor Info to the mesh """ def __init__( self, *, enabled: builtins.bool = ..., update_interval: builtins.int = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["enabled", b"enabled", "update_interval", b"update_interval"]) -> None: ... @typing.final class DetectionSensorConfig(google.protobuf.message.Message): """ Detection Sensor Module Config """ DESCRIPTOR: google.protobuf.descriptor.Descriptor ENABLED_FIELD_NUMBER: builtins.int MINIMUM_BROADCAST_SECS_FIELD_NUMBER: builtins.int STATE_BROADCAST_SECS_FIELD_NUMBER: builtins.int SEND_BELL_FIELD_NUMBER: builtins.int NAME_FIELD_NUMBER: builtins.int MONITOR_PIN_FIELD_NUMBER: builtins.int DETECTION_TRIGGERED_HIGH_FIELD_NUMBER: builtins.int USE_PULLUP_FIELD_NUMBER: builtins.int enabled: builtins.bool """ Whether the Module is enabled """ minimum_broadcast_secs: builtins.int """ Interval in seconds of how often we can send a message to the mesh when a state change is detected """ state_broadcast_secs: builtins.int """ Interval in seconds of how often we should send a message to the mesh with the current state regardless of changes When set to 0, only state changes will be broadcasted Works as a sort of status heartbeat for peace of mind """ send_bell: builtins.bool """ Send ASCII bell with alert message Useful for triggering ext. notification on bell """ name: builtins.str """ Friendly name used to format message sent to mesh Example: A name "Motion" would result in a message "Motion detected" Maximum length of 20 characters """ monitor_pin: builtins.int """ GPIO pin to monitor for state changes """ detection_triggered_high: builtins.bool """ Whether or not the GPIO pin state detection is triggered on HIGH (1) Otherwise LOW (0) """ use_pullup: builtins.bool """ Whether or not use INPUT_PULLUP mode for GPIO pin Only applicable if the board uses pull-up resistors on the pin """ def __init__( self, *, enabled: builtins.bool = ..., minimum_broadcast_secs: builtins.int = ..., state_broadcast_secs: builtins.int = ..., send_bell: builtins.bool = ..., name: builtins.str = ..., monitor_pin: builtins.int = ..., detection_triggered_high: builtins.bool = ..., use_pullup: builtins.bool = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["detection_triggered_high", b"detection_triggered_high", "enabled", b"enabled", "minimum_broadcast_secs", b"minimum_broadcast_secs", "monitor_pin", b"monitor_pin", "name", b"name", "send_bell", b"send_bell", "state_broadcast_secs", b"state_broadcast_secs", "use_pullup", b"use_pullup"]) -> None: ... @typing.final class AudioConfig(google.protobuf.message.Message): """ Audio Config for codec2 voice """ DESCRIPTOR: google.protobuf.descriptor.Descriptor class _Audio_Baud: ValueType = typing.NewType("ValueType", builtins.int) V: typing_extensions.TypeAlias = ValueType class _Audio_BaudEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[ModuleConfig.AudioConfig._Audio_Baud.ValueType], builtins.type): DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor CODEC2_DEFAULT: ModuleConfig.AudioConfig._Audio_Baud.ValueType # 0 CODEC2_3200: ModuleConfig.AudioConfig._Audio_Baud.ValueType # 1 CODEC2_2400: ModuleConfig.AudioConfig._Audio_Baud.ValueType # 2 CODEC2_1600: ModuleConfig.AudioConfig._Audio_Baud.ValueType # 3 CODEC2_1400: ModuleConfig.AudioConfig._Audio_Baud.ValueType # 4 CODEC2_1300: ModuleConfig.AudioConfig._Audio_Baud.ValueType # 5 CODEC2_1200: ModuleConfig.AudioConfig._Audio_Baud.ValueType # 6 CODEC2_700: ModuleConfig.AudioConfig._Audio_Baud.ValueType # 7 CODEC2_700B: ModuleConfig.AudioConfig._Audio_Baud.ValueType # 8 class Audio_Baud(_Audio_Baud, metaclass=_Audio_BaudEnumTypeWrapper): """ Baudrate for codec2 voice """ CODEC2_DEFAULT: ModuleConfig.AudioConfig.Audio_Baud.ValueType # 0 CODEC2_3200: ModuleConfig.AudioConfig.Audio_Baud.ValueType # 1 CODEC2_2400: ModuleConfig.AudioConfig.Audio_Baud.ValueType # 2 CODEC2_1600: ModuleConfig.AudioConfig.Audio_Baud.ValueType # 3 CODEC2_1400: ModuleConfig.AudioConfig.Audio_Baud.ValueType # 4 CODEC2_1300: ModuleConfig.AudioConfig.Audio_Baud.ValueType # 5 CODEC2_1200: ModuleConfig.AudioConfig.Audio_Baud.ValueType # 6 CODEC2_700: ModuleConfig.AudioConfig.Audio_Baud.ValueType # 7 CODEC2_700B: ModuleConfig.AudioConfig.Audio_Baud.ValueType # 8 CODEC2_ENABLED_FIELD_NUMBER: builtins.int PTT_PIN_FIELD_NUMBER: builtins.int BITRATE_FIELD_NUMBER: builtins.int I2S_WS_FIELD_NUMBER: builtins.int I2S_SD_FIELD_NUMBER: builtins.int I2S_DIN_FIELD_NUMBER: builtins.int I2S_SCK_FIELD_NUMBER: builtins.int codec2_enabled: builtins.bool """ Whether Audio is enabled """ ptt_pin: builtins.int """ PTT Pin """ bitrate: global___ModuleConfig.AudioConfig.Audio_Baud.ValueType """ The audio sample rate to use for codec2 """ i2s_ws: builtins.int """ I2S Word Select """ i2s_sd: builtins.int """ I2S Data IN """ i2s_din: builtins.int """ I2S Data OUT """ i2s_sck: builtins.int """ I2S Clock """ def __init__( self, *, codec2_enabled: builtins.bool = ..., ptt_pin: builtins.int = ..., bitrate: global___ModuleConfig.AudioConfig.Audio_Baud.ValueType = ..., i2s_ws: builtins.int = ..., i2s_sd: builtins.int = ..., i2s_din: builtins.int = ..., i2s_sck: builtins.int = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["bitrate", b"bitrate", "codec2_enabled", b"codec2_enabled", "i2s_din", b"i2s_din", "i2s_sck", b"i2s_sck", "i2s_sd", b"i2s_sd", "i2s_ws", b"i2s_ws", "ptt_pin", b"ptt_pin"]) -> None: ... @typing.final class PaxcounterConfig(google.protobuf.message.Message): """ Config for the Paxcounter Module """ DESCRIPTOR: google.protobuf.descriptor.Descriptor ENABLED_FIELD_NUMBER: builtins.int PAXCOUNTER_UPDATE_INTERVAL_FIELD_NUMBER: builtins.int WIFI_THRESHOLD_FIELD_NUMBER: builtins.int BLE_THRESHOLD_FIELD_NUMBER: builtins.int enabled: builtins.bool """ Enable the Paxcounter Module """ paxcounter_update_interval: builtins.int """ Interval in seconds of how often we should try to send our metrics to the mesh """ wifi_threshold: builtins.int """ WiFi RSSI threshold. Defaults to -80 """ ble_threshold: builtins.int """ BLE RSSI threshold. Defaults to -80 """ def __init__( self, *, enabled: builtins.bool = ..., paxcounter_update_interval: builtins.int = ..., wifi_threshold: builtins.int = ..., ble_threshold: builtins.int = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["ble_threshold", b"ble_threshold", "enabled", b"enabled", "paxcounter_update_interval", b"paxcounter_update_interval", "wifi_threshold", b"wifi_threshold"]) -> None: ... @typing.final class SerialConfig(google.protobuf.message.Message): """ Serial Config """ DESCRIPTOR: google.protobuf.descriptor.Descriptor class _Serial_Baud: ValueType = typing.NewType("ValueType", builtins.int) V: typing_extensions.TypeAlias = ValueType class _Serial_BaudEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[ModuleConfig.SerialConfig._Serial_Baud.ValueType], builtins.type): DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor BAUD_DEFAULT: ModuleConfig.SerialConfig._Serial_Baud.ValueType # 0 BAUD_110: ModuleConfig.SerialConfig._Serial_Baud.ValueType # 1 BAUD_300: ModuleConfig.SerialConfig._Serial_Baud.ValueType # 2 BAUD_600: ModuleConfig.SerialConfig._Serial_Baud.ValueType # 3 BAUD_1200: ModuleConfig.SerialConfig._Serial_Baud.ValueType # 4 BAUD_2400: ModuleConfig.SerialConfig._Serial_Baud.ValueType # 5 BAUD_4800: ModuleConfig.SerialConfig._Serial_Baud.ValueType # 6 BAUD_9600: ModuleConfig.SerialConfig._Serial_Baud.ValueType # 7 BAUD_19200: ModuleConfig.SerialConfig._Serial_Baud.ValueType # 8 BAUD_38400: ModuleConfig.SerialConfig._Serial_Baud.ValueType # 9 BAUD_57600: ModuleConfig.SerialConfig._Serial_Baud.ValueType # 10 BAUD_115200: ModuleConfig.SerialConfig._Serial_Baud.ValueType # 11 BAUD_230400: ModuleConfig.SerialConfig._Serial_Baud.ValueType # 12 BAUD_460800: ModuleConfig.SerialConfig._Serial_Baud.ValueType # 13 BAUD_576000: ModuleConfig.SerialConfig._Serial_Baud.ValueType # 14 BAUD_921600: ModuleConfig.SerialConfig._Serial_Baud.ValueType # 15 class Serial_Baud(_Serial_Baud, metaclass=_Serial_BaudEnumTypeWrapper): """ TODO: REPLACE """ BAUD_DEFAULT: ModuleConfig.SerialConfig.Serial_Baud.ValueType # 0 BAUD_110: ModuleConfig.SerialConfig.Serial_Baud.ValueType # 1 BAUD_300: ModuleConfig.SerialConfig.Serial_Baud.ValueType # 2 BAUD_600: ModuleConfig.SerialConfig.Serial_Baud.ValueType # 3 BAUD_1200: ModuleConfig.SerialConfig.Serial_Baud.ValueType # 4 BAUD_2400: ModuleConfig.SerialConfig.Serial_Baud.ValueType # 5 BAUD_4800: ModuleConfig.SerialConfig.Serial_Baud.ValueType # 6 BAUD_9600: ModuleConfig.SerialConfig.Serial_Baud.ValueType # 7 BAUD_19200: ModuleConfig.SerialConfig.Serial_Baud.ValueType # 8 BAUD_38400: ModuleConfig.SerialConfig.Serial_Baud.ValueType # 9 BAUD_57600: ModuleConfig.SerialConfig.Serial_Baud.ValueType # 10 BAUD_115200: ModuleConfig.SerialConfig.Serial_Baud.ValueType # 11 BAUD_230400: ModuleConfig.SerialConfig.Serial_Baud.ValueType # 12 BAUD_460800: ModuleConfig.SerialConfig.Serial_Baud.ValueType # 13 BAUD_576000: ModuleConfig.SerialConfig.Serial_Baud.ValueType # 14 BAUD_921600: ModuleConfig.SerialConfig.Serial_Baud.ValueType # 15 class _Serial_Mode: ValueType = typing.NewType("ValueType", builtins.int) V: typing_extensions.TypeAlias = ValueType class _Serial_ModeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[ModuleConfig.SerialConfig._Serial_Mode.ValueType], builtins.type): DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor DEFAULT: ModuleConfig.SerialConfig._Serial_Mode.ValueType # 0 SIMPLE: ModuleConfig.SerialConfig._Serial_Mode.ValueType # 1 PROTO: ModuleConfig.SerialConfig._Serial_Mode.ValueType # 2 TEXTMSG: ModuleConfig.SerialConfig._Serial_Mode.ValueType # 3 NMEA: ModuleConfig.SerialConfig._Serial_Mode.ValueType # 4 CALTOPO: ModuleConfig.SerialConfig._Serial_Mode.ValueType # 5 """NMEA messages specifically tailored for CalTopo""" class Serial_Mode(_Serial_Mode, metaclass=_Serial_ModeEnumTypeWrapper): """ TODO: REPLACE """ DEFAULT: ModuleConfig.SerialConfig.Serial_Mode.ValueType # 0 SIMPLE: ModuleConfig.SerialConfig.Serial_Mode.ValueType # 1 PROTO: ModuleConfig.SerialConfig.Serial_Mode.ValueType # 2 TEXTMSG: ModuleConfig.SerialConfig.Serial_Mode.ValueType # 3 NMEA: ModuleConfig.SerialConfig.Serial_Mode.ValueType # 4 CALTOPO: ModuleConfig.SerialConfig.Serial_Mode.ValueType # 5 """NMEA messages specifically tailored for CalTopo""" ENABLED_FIELD_NUMBER: builtins.int ECHO_FIELD_NUMBER: builtins.int RXD_FIELD_NUMBER: builtins.int TXD_FIELD_NUMBER: builtins.int BAUD_FIELD_NUMBER: builtins.int TIMEOUT_FIELD_NUMBER: builtins.int MODE_FIELD_NUMBER: builtins.int OVERRIDE_CONSOLE_SERIAL_PORT_FIELD_NUMBER: builtins.int enabled: builtins.bool """ Preferences for the SerialModule """ echo: builtins.bool """ TODO: REPLACE """ rxd: builtins.int """ RX pin (should match Arduino gpio pin number) """ txd: builtins.int """ TX pin (should match Arduino gpio pin number) """ baud: global___ModuleConfig.SerialConfig.Serial_Baud.ValueType """ Serial baud rate """ timeout: builtins.int """ TODO: REPLACE """ mode: global___ModuleConfig.SerialConfig.Serial_Mode.ValueType """ Mode for serial module operation """ override_console_serial_port: builtins.bool """ Overrides the platform's defacto Serial port instance to use with Serial module config settings This is currently only usable in output modes like NMEA / CalTopo and may behave strangely or not work at all in other modes Existing logging over the Serial Console will still be present """ def __init__( self, *, enabled: builtins.bool = ..., echo: builtins.bool = ..., rxd: builtins.int = ..., txd: builtins.int = ..., baud: global___ModuleConfig.SerialConfig.Serial_Baud.ValueType = ..., timeout: builtins.int = ..., mode: global___ModuleConfig.SerialConfig.Serial_Mode.ValueType = ..., override_console_serial_port: builtins.bool = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["baud", b"baud", "echo", b"echo", "enabled", b"enabled", "mode", b"mode", "override_console_serial_port", b"override_console_serial_port", "rxd", b"rxd", "timeout", b"timeout", "txd", b"txd"]) -> None: ... @typing.final class ExternalNotificationConfig(google.protobuf.message.Message): """ External Notifications Config """ DESCRIPTOR: google.protobuf.descriptor.Descriptor ENABLED_FIELD_NUMBER: builtins.int OUTPUT_MS_FIELD_NUMBER: builtins.int OUTPUT_FIELD_NUMBER: builtins.int OUTPUT_VIBRA_FIELD_NUMBER: builtins.int OUTPUT_BUZZER_FIELD_NUMBER: builtins.int ACTIVE_FIELD_NUMBER: builtins.int ALERT_MESSAGE_FIELD_NUMBER: builtins.int ALERT_MESSAGE_VIBRA_FIELD_NUMBER: builtins.int ALERT_MESSAGE_BUZZER_FIELD_NUMBER: builtins.int ALERT_BELL_FIELD_NUMBER: builtins.int ALERT_BELL_VIBRA_FIELD_NUMBER: builtins.int ALERT_BELL_BUZZER_FIELD_NUMBER: builtins.int USE_PWM_FIELD_NUMBER: builtins.int NAG_TIMEOUT_FIELD_NUMBER: builtins.int USE_I2S_AS_BUZZER_FIELD_NUMBER: builtins.int enabled: builtins.bool """ Enable the ExternalNotificationModule """ output_ms: builtins.int """ When using in On/Off mode, keep the output on for this many milliseconds. Default 1000ms (1 second). """ output: builtins.int """ Define the output pin GPIO setting Defaults to EXT_NOTIFY_OUT if set for the board. In standalone devices this pin should drive the LED to match the UI. """ output_vibra: builtins.int """ Optional: Define a secondary output pin for a vibra motor This is used in standalone devices to match the UI. """ output_buzzer: builtins.int """ Optional: Define a tertiary output pin for an active buzzer This is used in standalone devices to to match the UI. """ active: builtins.bool """ IF this is true, the 'output' Pin will be pulled active high, false means active low. """ alert_message: builtins.bool """ True: Alert when a text message arrives (output) """ alert_message_vibra: builtins.bool """ True: Alert when a text message arrives (output_vibra) """ alert_message_buzzer: builtins.bool """ True: Alert when a text message arrives (output_buzzer) """ alert_bell: builtins.bool """ True: Alert when the bell character is received (output) """ alert_bell_vibra: builtins.bool """ True: Alert when the bell character is received (output_vibra) """ alert_bell_buzzer: builtins.bool """ True: Alert when the bell character is received (output_buzzer) """ use_pwm: builtins.bool """ use a PWM output instead of a simple on/off output. This will ignore the 'output', 'output_ms' and 'active' settings and use the device.buzzer_gpio instead. """ nag_timeout: builtins.int """ The notification will toggle with 'output_ms' for this time of seconds. Default is 0 which means don't repeat at all. 60 would mean blink and/or beep for 60 seconds """ use_i2s_as_buzzer: builtins.bool """ When true, enables devices with native I2S audio output to use the RTTTL over speaker like a buzzer T-Watch S3 and T-Deck for example have this capability """ def __init__( self, *, enabled: builtins.bool = ..., output_ms: builtins.int = ..., output: builtins.int = ..., output_vibra: builtins.int = ..., output_buzzer: builtins.int = ..., active: builtins.bool = ..., alert_message: builtins.bool = ..., alert_message_vibra: builtins.bool = ..., alert_message_buzzer: builtins.bool = ..., alert_bell: builtins.bool = ..., alert_bell_vibra: builtins.bool = ..., alert_bell_buzzer: builtins.bool = ..., use_pwm: builtins.bool = ..., nag_timeout: builtins.int = ..., use_i2s_as_buzzer: builtins.bool = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["active", b"active", "alert_bell", b"alert_bell", "alert_bell_buzzer", b"alert_bell_buzzer", "alert_bell_vibra", b"alert_bell_vibra", "alert_message", b"alert_message", "alert_message_buzzer", b"alert_message_buzzer", "alert_message_vibra", b"alert_message_vibra", "enabled", b"enabled", "nag_timeout", b"nag_timeout", "output", b"output", "output_buzzer", b"output_buzzer", "output_ms", b"output_ms", "output_vibra", b"output_vibra", "use_i2s_as_buzzer", b"use_i2s_as_buzzer", "use_pwm", b"use_pwm"]) -> None: ... @typing.final class StoreForwardConfig(google.protobuf.message.Message): """ Store and Forward Module Config """ DESCRIPTOR: google.protobuf.descriptor.Descriptor ENABLED_FIELD_NUMBER: builtins.int HEARTBEAT_FIELD_NUMBER: builtins.int RECORDS_FIELD_NUMBER: builtins.int HISTORY_RETURN_MAX_FIELD_NUMBER: builtins.int HISTORY_RETURN_WINDOW_FIELD_NUMBER: builtins.int enabled: builtins.bool """ Enable the Store and Forward Module """ heartbeat: builtins.bool """ TODO: REPLACE """ records: builtins.int """ TODO: REPLACE """ history_return_max: builtins.int """ TODO: REPLACE """ history_return_window: builtins.int """ TODO: REPLACE """ def __init__( self, *, enabled: builtins.bool = ..., heartbeat: builtins.bool = ..., records: builtins.int = ..., history_return_max: builtins.int = ..., history_return_window: builtins.int = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["enabled", b"enabled", "heartbeat", b"heartbeat", "history_return_max", b"history_return_max", "history_return_window", b"history_return_window", "records", b"records"]) -> None: ... @typing.final class RangeTestConfig(google.protobuf.message.Message): """ Preferences for the RangeTestModule """ DESCRIPTOR: google.protobuf.descriptor.Descriptor ENABLED_FIELD_NUMBER: builtins.int SENDER_FIELD_NUMBER: builtins.int SAVE_FIELD_NUMBER: builtins.int enabled: builtins.bool """ Enable the Range Test Module """ sender: builtins.int """ Send out range test messages from this node """ save: builtins.bool """ Bool value indicating that this node should save a RangeTest.csv file. ESP32 Only """ def __init__( self, *, enabled: builtins.bool = ..., sender: builtins.int = ..., save: builtins.bool = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["enabled", b"enabled", "save", b"save", "sender", b"sender"]) -> None: ... @typing.final class TelemetryConfig(google.protobuf.message.Message): """ Configuration for both device and environment metrics """ DESCRIPTOR: google.protobuf.descriptor.Descriptor DEVICE_UPDATE_INTERVAL_FIELD_NUMBER: builtins.int ENVIRONMENT_UPDATE_INTERVAL_FIELD_NUMBER: builtins.int ENVIRONMENT_MEASUREMENT_ENABLED_FIELD_NUMBER: builtins.int ENVIRONMENT_SCREEN_ENABLED_FIELD_NUMBER: builtins.int ENVIRONMENT_DISPLAY_FAHRENHEIT_FIELD_NUMBER: builtins.int AIR_QUALITY_ENABLED_FIELD_NUMBER: builtins.int AIR_QUALITY_INTERVAL_FIELD_NUMBER: builtins.int POWER_MEASUREMENT_ENABLED_FIELD_NUMBER: builtins.int POWER_UPDATE_INTERVAL_FIELD_NUMBER: builtins.int POWER_SCREEN_ENABLED_FIELD_NUMBER: builtins.int device_update_interval: builtins.int """ Interval in seconds of how often we should try to send our device metrics to the mesh """ environment_update_interval: builtins.int """ Interval in seconds of how often we should try to send our environment measurements to the mesh """ environment_measurement_enabled: builtins.bool """ Preferences for the Telemetry Module (Environment) Enable/Disable the telemetry measurement module measurement collection """ environment_screen_enabled: builtins.bool """ Enable/Disable the telemetry measurement module on-device display """ environment_display_fahrenheit: builtins.bool """ We'll always read the sensor in Celsius, but sometimes we might want to display the results in Fahrenheit as a "user preference". """ air_quality_enabled: builtins.bool """ Enable/Disable the air quality metrics """ air_quality_interval: builtins.int """ Interval in seconds of how often we should try to send our air quality metrics to the mesh """ power_measurement_enabled: builtins.bool """ Interval in seconds of how often we should try to send our air quality metrics to the mesh """ power_update_interval: builtins.int """ Interval in seconds of how often we should try to send our air quality metrics to the mesh """ power_screen_enabled: builtins.bool """ Interval in seconds of how often we should try to send our air quality metrics to the mesh """ def __init__( self, *, device_update_interval: builtins.int = ..., environment_update_interval: builtins.int = ..., environment_measurement_enabled: builtins.bool = ..., environment_screen_enabled: builtins.bool = ..., environment_display_fahrenheit: builtins.bool = ..., air_quality_enabled: builtins.bool = ..., air_quality_interval: builtins.int = ..., power_measurement_enabled: builtins.bool = ..., power_update_interval: builtins.int = ..., power_screen_enabled: builtins.bool = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["air_quality_enabled", b"air_quality_enabled", "air_quality_interval", b"air_quality_interval", "device_update_interval", b"device_update_interval", "environment_display_fahrenheit", b"environment_display_fahrenheit", "environment_measurement_enabled", b"environment_measurement_enabled", "environment_screen_enabled", b"environment_screen_enabled", "environment_update_interval", b"environment_update_interval", "power_measurement_enabled", b"power_measurement_enabled", "power_screen_enabled", b"power_screen_enabled", "power_update_interval", b"power_update_interval"]) -> None: ... @typing.final class CannedMessageConfig(google.protobuf.message.Message): """ TODO: REPLACE """ DESCRIPTOR: google.protobuf.descriptor.Descriptor class _InputEventChar: ValueType = typing.NewType("ValueType", builtins.int) V: typing_extensions.TypeAlias = ValueType class _InputEventCharEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[ModuleConfig.CannedMessageConfig._InputEventChar.ValueType], builtins.type): DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor NONE: ModuleConfig.CannedMessageConfig._InputEventChar.ValueType # 0 """ TODO: REPLACE """ UP: ModuleConfig.CannedMessageConfig._InputEventChar.ValueType # 17 """ TODO: REPLACE """ DOWN: ModuleConfig.CannedMessageConfig._InputEventChar.ValueType # 18 """ TODO: REPLACE """ LEFT: ModuleConfig.CannedMessageConfig._InputEventChar.ValueType # 19 """ TODO: REPLACE """ RIGHT: ModuleConfig.CannedMessageConfig._InputEventChar.ValueType # 20 """ TODO: REPLACE """ SELECT: ModuleConfig.CannedMessageConfig._InputEventChar.ValueType # 10 """ '\\n' """ BACK: ModuleConfig.CannedMessageConfig._InputEventChar.ValueType # 27 """ TODO: REPLACE """ CANCEL: ModuleConfig.CannedMessageConfig._InputEventChar.ValueType # 24 """ TODO: REPLACE """ class InputEventChar(_InputEventChar, metaclass=_InputEventCharEnumTypeWrapper): """ TODO: REPLACE """ NONE: ModuleConfig.CannedMessageConfig.InputEventChar.ValueType # 0 """ TODO: REPLACE """ UP: ModuleConfig.CannedMessageConfig.InputEventChar.ValueType # 17 """ TODO: REPLACE """ DOWN: ModuleConfig.CannedMessageConfig.InputEventChar.ValueType # 18 """ TODO: REPLACE """ LEFT: ModuleConfig.CannedMessageConfig.InputEventChar.ValueType # 19 """ TODO: REPLACE """ RIGHT: ModuleConfig.CannedMessageConfig.InputEventChar.ValueType # 20 """ TODO: REPLACE """ SELECT: ModuleConfig.CannedMessageConfig.InputEventChar.ValueType # 10 """ '\\n' """ BACK: ModuleConfig.CannedMessageConfig.InputEventChar.ValueType # 27 """ TODO: REPLACE """ CANCEL: ModuleConfig.CannedMessageConfig.InputEventChar.ValueType # 24 """ TODO: REPLACE """ ROTARY1_ENABLED_FIELD_NUMBER: builtins.int INPUTBROKER_PIN_A_FIELD_NUMBER: builtins.int INPUTBROKER_PIN_B_FIELD_NUMBER: builtins.int INPUTBROKER_PIN_PRESS_FIELD_NUMBER: builtins.int INPUTBROKER_EVENT_CW_FIELD_NUMBER: builtins.int INPUTBROKER_EVENT_CCW_FIELD_NUMBER: builtins.int INPUTBROKER_EVENT_PRESS_FIELD_NUMBER: builtins.int UPDOWN1_ENABLED_FIELD_NUMBER: builtins.int ENABLED_FIELD_NUMBER: builtins.int ALLOW_INPUT_SOURCE_FIELD_NUMBER: builtins.int SEND_BELL_FIELD_NUMBER: builtins.int rotary1_enabled: builtins.bool """ Enable the rotary encoder #1. This is a 'dumb' encoder sending pulses on both A and B pins while rotating. """ inputbroker_pin_a: builtins.int """ GPIO pin for rotary encoder A port. """ inputbroker_pin_b: builtins.int """ GPIO pin for rotary encoder B port. """ inputbroker_pin_press: builtins.int """ GPIO pin for rotary encoder Press port. """ inputbroker_event_cw: global___ModuleConfig.CannedMessageConfig.InputEventChar.ValueType """ Generate input event on CW of this kind. """ inputbroker_event_ccw: global___ModuleConfig.CannedMessageConfig.InputEventChar.ValueType """ Generate input event on CCW of this kind. """ inputbroker_event_press: global___ModuleConfig.CannedMessageConfig.InputEventChar.ValueType """ Generate input event on Press of this kind. """ updown1_enabled: builtins.bool """ Enable the Up/Down/Select input device. Can be RAK rotary encoder or 3 buttons. Uses the a/b/press definitions from inputbroker. """ enabled: builtins.bool """ Enable/disable CannedMessageModule. """ allow_input_source: builtins.str """ Input event origin accepted by the canned message module. Can be e.g. "rotEnc1", "upDownEnc1" or keyword "_any" """ send_bell: builtins.bool """ CannedMessageModule also sends a bell character with the messages. ExternalNotificationModule can benefit from this feature. """ def __init__( self, *, rotary1_enabled: builtins.bool = ..., inputbroker_pin_a: builtins.int = ..., inputbroker_pin_b: builtins.int = ..., inputbroker_pin_press: builtins.int = ..., inputbroker_event_cw: global___ModuleConfig.CannedMessageConfig.InputEventChar.ValueType = ..., inputbroker_event_ccw: global___ModuleConfig.CannedMessageConfig.InputEventChar.ValueType = ..., inputbroker_event_press: global___ModuleConfig.CannedMessageConfig.InputEventChar.ValueType = ..., updown1_enabled: builtins.bool = ..., enabled: builtins.bool = ..., allow_input_source: builtins.str = ..., send_bell: builtins.bool = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["allow_input_source", b"allow_input_source", "enabled", b"enabled", "inputbroker_event_ccw", b"inputbroker_event_ccw", "inputbroker_event_cw", b"inputbroker_event_cw", "inputbroker_event_press", b"inputbroker_event_press", "inputbroker_pin_a", b"inputbroker_pin_a", "inputbroker_pin_b", b"inputbroker_pin_b", "inputbroker_pin_press", b"inputbroker_pin_press", "rotary1_enabled", b"rotary1_enabled", "send_bell", b"send_bell", "updown1_enabled", b"updown1_enabled"]) -> None: ... @typing.final class AmbientLightingConfig(google.protobuf.message.Message): """ Ambient Lighting Module - Settings for control of onboard LEDs to allow users to adjust the brightness levels and respective color levels. Initially created for the RAK14001 RGB LED module. """ DESCRIPTOR: google.protobuf.descriptor.Descriptor LED_STATE_FIELD_NUMBER: builtins.int CURRENT_FIELD_NUMBER: builtins.int RED_FIELD_NUMBER: builtins.int GREEN_FIELD_NUMBER: builtins.int BLUE_FIELD_NUMBER: builtins.int led_state: builtins.bool """ Sets LED to on or off. """ current: builtins.int """ Sets the current for the LED output. Default is 10. """ red: builtins.int """ Sets the red LED level. Values are 0-255. """ green: builtins.int """ Sets the green LED level. Values are 0-255. """ blue: builtins.int """ Sets the blue LED level. Values are 0-255. """ def __init__( self, *, led_state: builtins.bool = ..., current: builtins.int = ..., red: builtins.int = ..., green: builtins.int = ..., blue: builtins.int = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["blue", b"blue", "current", b"current", "green", b"green", "led_state", b"led_state", "red", b"red"]) -> None: ... MQTT_FIELD_NUMBER: builtins.int SERIAL_FIELD_NUMBER: builtins.int EXTERNAL_NOTIFICATION_FIELD_NUMBER: builtins.int STORE_FORWARD_FIELD_NUMBER: builtins.int RANGE_TEST_FIELD_NUMBER: builtins.int TELEMETRY_FIELD_NUMBER: builtins.int CANNED_MESSAGE_FIELD_NUMBER: builtins.int AUDIO_FIELD_NUMBER: builtins.int REMOTE_HARDWARE_FIELD_NUMBER: builtins.int NEIGHBOR_INFO_FIELD_NUMBER: builtins.int AMBIENT_LIGHTING_FIELD_NUMBER: builtins.int DETECTION_SENSOR_FIELD_NUMBER: builtins.int PAXCOUNTER_FIELD_NUMBER: builtins.int @property def mqtt(self) -> global___ModuleConfig.MQTTConfig: """ TODO: REPLACE """ @property def serial(self) -> global___ModuleConfig.SerialConfig: """ TODO: REPLACE """ @property def external_notification(self) -> global___ModuleConfig.ExternalNotificationConfig: """ TODO: REPLACE """ @property def store_forward(self) -> global___ModuleConfig.StoreForwardConfig: """ TODO: REPLACE """ @property def range_test(self) -> global___ModuleConfig.RangeTestConfig: """ TODO: REPLACE """ @property def telemetry(self) -> global___ModuleConfig.TelemetryConfig: """ TODO: REPLACE """ @property def canned_message(self) -> global___ModuleConfig.CannedMessageConfig: """ TODO: REPLACE """ @property def audio(self) -> global___ModuleConfig.AudioConfig: """ TODO: REPLACE """ @property def remote_hardware(self) -> global___ModuleConfig.RemoteHardwareConfig: """ TODO: REPLACE """ @property def neighbor_info(self) -> global___ModuleConfig.NeighborInfoConfig: """ TODO: REPLACE """ @property def ambient_lighting(self) -> global___ModuleConfig.AmbientLightingConfig: """ TODO: REPLACE """ @property def detection_sensor(self) -> global___ModuleConfig.DetectionSensorConfig: """ TODO: REPLACE """ @property def paxcounter(self) -> global___ModuleConfig.PaxcounterConfig: """ TODO: REPLACE """ def __init__( self, *, mqtt: global___ModuleConfig.MQTTConfig | None = ..., serial: global___ModuleConfig.SerialConfig | None = ..., external_notification: global___ModuleConfig.ExternalNotificationConfig | None = ..., store_forward: global___ModuleConfig.StoreForwardConfig | None = ..., range_test: global___ModuleConfig.RangeTestConfig | None = ..., telemetry: global___ModuleConfig.TelemetryConfig | None = ..., canned_message: global___ModuleConfig.CannedMessageConfig | None = ..., audio: global___ModuleConfig.AudioConfig | None = ..., remote_hardware: global___ModuleConfig.RemoteHardwareConfig | None = ..., neighbor_info: global___ModuleConfig.NeighborInfoConfig | None = ..., ambient_lighting: global___ModuleConfig.AmbientLightingConfig | None = ..., detection_sensor: global___ModuleConfig.DetectionSensorConfig | None = ..., paxcounter: global___ModuleConfig.PaxcounterConfig | None = ..., ) -> None: ... def HasField(self, field_name: typing.Literal["ambient_lighting", b"ambient_lighting", "audio", b"audio", "canned_message", b"canned_message", "detection_sensor", b"detection_sensor", "external_notification", b"external_notification", "mqtt", b"mqtt", "neighbor_info", b"neighbor_info", "paxcounter", b"paxcounter", "payload_variant", b"payload_variant", "range_test", b"range_test", "remote_hardware", b"remote_hardware", "serial", b"serial", "store_forward", b"store_forward", "telemetry", b"telemetry"]) -> builtins.bool: ... def ClearField(self, field_name: typing.Literal["ambient_lighting", b"ambient_lighting", "audio", b"audio", "canned_message", b"canned_message", "detection_sensor", b"detection_sensor", "external_notification", b"external_notification", "mqtt", b"mqtt", "neighbor_info", b"neighbor_info", "paxcounter", b"paxcounter", "payload_variant", b"payload_variant", "range_test", b"range_test", "remote_hardware", b"remote_hardware", "serial", b"serial", "store_forward", b"store_forward", "telemetry", b"telemetry"]) -> None: ... def WhichOneof(self, oneof_group: typing.Literal["payload_variant", b"payload_variant"]) -> typing.Literal["mqtt", "serial", "external_notification", "store_forward", "range_test", "telemetry", "canned_message", "audio", "remote_hardware", "neighbor_info", "ambient_lighting", "detection_sensor", "paxcounter"] | None: ... global___ModuleConfig = ModuleConfig @typing.final class RemoteHardwarePin(google.protobuf.message.Message): """ A GPIO pin definition for remote hardware module """ DESCRIPTOR: google.protobuf.descriptor.Descriptor GPIO_PIN_FIELD_NUMBER: builtins.int NAME_FIELD_NUMBER: builtins.int TYPE_FIELD_NUMBER: builtins.int gpio_pin: builtins.int """ GPIO Pin number (must match Arduino) """ name: builtins.str """ Name for the GPIO pin (i.e. Front gate, mailbox, etc) """ type: global___RemoteHardwarePinType.ValueType """ Type of GPIO access available to consumers on the mesh """ def __init__( self, *, gpio_pin: builtins.int = ..., name: builtins.str = ..., type: global___RemoteHardwarePinType.ValueType = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["gpio_pin", b"gpio_pin", "name", b"name", "type", b"type"]) -> None: ... global___RemoteHardwarePin = RemoteHardwarePin python-2.3.14/meshtastic/protobuf/mqtt_pb2.py000066400000000000000000000050251464266072200212430ustar00rootroot00000000000000# -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # source: meshtastic/protobuf/mqtt.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 symbol_database as _symbol_database from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() from meshtastic.protobuf import config_pb2 as meshtastic_dot_protobuf_dot_config__pb2 from meshtastic.protobuf import mesh_pb2 as meshtastic_dot_protobuf_dot_mesh__pb2 DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1emeshtastic/protobuf/mqtt.proto\x12\x13meshtastic.protobuf\x1a meshtastic/protobuf/config.proto\x1a\x1emeshtastic/protobuf/mesh.proto\"j\n\x0fServiceEnvelope\x12/\n\x06packet\x18\x01 \x01(\x0b\x32\x1f.meshtastic.protobuf.MeshPacket\x12\x12\n\nchannel_id\x18\x02 \x01(\t\x12\x12\n\ngateway_id\x18\x03 \x01(\t\"\xe0\x03\n\tMapReport\x12\x11\n\tlong_name\x18\x01 \x01(\t\x12\x12\n\nshort_name\x18\x02 \x01(\t\x12;\n\x04role\x18\x03 \x01(\x0e\x32-.meshtastic.protobuf.Config.DeviceConfig.Role\x12\x34\n\x08hw_model\x18\x04 \x01(\x0e\x32\".meshtastic.protobuf.HardwareModel\x12\x18\n\x10\x66irmware_version\x18\x05 \x01(\t\x12\x41\n\x06region\x18\x06 \x01(\x0e\x32\x31.meshtastic.protobuf.Config.LoRaConfig.RegionCode\x12H\n\x0cmodem_preset\x18\x07 \x01(\x0e\x32\x32.meshtastic.protobuf.Config.LoRaConfig.ModemPreset\x12\x1b\n\x13has_default_channel\x18\x08 \x01(\x08\x12\x12\n\nlatitude_i\x18\t \x01(\x0f\x12\x13\n\x0blongitude_i\x18\n \x01(\x0f\x12\x10\n\x08\x61ltitude\x18\x0b \x01(\x05\x12\x1a\n\x12position_precision\x18\x0c \x01(\r\x12\x1e\n\x16num_online_local_nodes\x18\r \x01(\rB_\n\x13\x63om.geeksville.meshB\nMQTTProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.mqtt_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\nMQTTProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' _globals['_SERVICEENVELOPE']._serialized_start=121 _globals['_SERVICEENVELOPE']._serialized_end=227 _globals['_MAPREPORT']._serialized_start=230 _globals['_MAPREPORT']._serialized_end=710 # @@protoc_insertion_point(module_scope) python-2.3.14/meshtastic/protobuf/mqtt_pb2.pyi000066400000000000000000000127011464266072200214130ustar00rootroot00000000000000""" @generated by mypy-protobuf. Do not edit manually! isort:skip_file """ import builtins import google.protobuf.descriptor import google.protobuf.message import meshtastic.protobuf.config_pb2 import meshtastic.protobuf.mesh_pb2 import typing DESCRIPTOR: google.protobuf.descriptor.FileDescriptor @typing.final class ServiceEnvelope(google.protobuf.message.Message): """ This message wraps a MeshPacket with extra metadata about the sender and how it arrived. """ DESCRIPTOR: google.protobuf.descriptor.Descriptor PACKET_FIELD_NUMBER: builtins.int CHANNEL_ID_FIELD_NUMBER: builtins.int GATEWAY_ID_FIELD_NUMBER: builtins.int channel_id: builtins.str """ The global channel ID it was sent on """ gateway_id: builtins.str """ The sending gateway node ID. Can we use this to authenticate/prevent fake nodeid impersonation for senders? - i.e. use gateway/mesh id (which is authenticated) + local node id as the globally trusted nodenum """ @property def packet(self) -> meshtastic.protobuf.mesh_pb2.MeshPacket: """ The (probably encrypted) packet """ def __init__( self, *, packet: meshtastic.protobuf.mesh_pb2.MeshPacket | None = ..., channel_id: builtins.str = ..., gateway_id: builtins.str = ..., ) -> None: ... def HasField(self, field_name: typing.Literal["packet", b"packet"]) -> builtins.bool: ... def ClearField(self, field_name: typing.Literal["channel_id", b"channel_id", "gateway_id", b"gateway_id", "packet", b"packet"]) -> None: ... global___ServiceEnvelope = ServiceEnvelope @typing.final class MapReport(google.protobuf.message.Message): """ Information about a node intended to be reported unencrypted to a map using MQTT. """ DESCRIPTOR: google.protobuf.descriptor.Descriptor LONG_NAME_FIELD_NUMBER: builtins.int SHORT_NAME_FIELD_NUMBER: builtins.int ROLE_FIELD_NUMBER: builtins.int HW_MODEL_FIELD_NUMBER: builtins.int FIRMWARE_VERSION_FIELD_NUMBER: builtins.int REGION_FIELD_NUMBER: builtins.int MODEM_PRESET_FIELD_NUMBER: builtins.int HAS_DEFAULT_CHANNEL_FIELD_NUMBER: builtins.int LATITUDE_I_FIELD_NUMBER: builtins.int LONGITUDE_I_FIELD_NUMBER: builtins.int ALTITUDE_FIELD_NUMBER: builtins.int POSITION_PRECISION_FIELD_NUMBER: builtins.int NUM_ONLINE_LOCAL_NODES_FIELD_NUMBER: builtins.int long_name: builtins.str """ A full name for this user, i.e. "Kevin Hester" """ short_name: builtins.str """ A VERY short name, ideally two characters. Suitable for a tiny OLED screen """ role: meshtastic.protobuf.config_pb2.Config.DeviceConfig.Role.ValueType """ Role of the node that applies specific settings for a particular use-case """ hw_model: meshtastic.protobuf.mesh_pb2.HardwareModel.ValueType """ Hardware model of the node, i.e. T-Beam, Heltec V3, etc... """ firmware_version: builtins.str """ Device firmware version string """ region: meshtastic.protobuf.config_pb2.Config.LoRaConfig.RegionCode.ValueType """ The region code for the radio (US, CN, EU433, etc...) """ modem_preset: meshtastic.protobuf.config_pb2.Config.LoRaConfig.ModemPreset.ValueType """ Modem preset used by the radio (LongFast, MediumSlow, etc...) """ has_default_channel: builtins.bool """ Whether the node has a channel with default PSK and name (LongFast, MediumSlow, etc...) and it uses the default frequency slot given the region and modem preset. """ latitude_i: builtins.int """ Latitude: multiply by 1e-7 to get degrees in floating point """ longitude_i: builtins.int """ Longitude: multiply by 1e-7 to get degrees in floating point """ altitude: builtins.int """ Altitude in meters above MSL """ position_precision: builtins.int """ Indicates the bits of precision for latitude and longitude set by the sending node """ num_online_local_nodes: builtins.int """ Number of online nodes (heard in the last 2 hours) this node has in its list that were received locally (not via MQTT) """ def __init__( self, *, long_name: builtins.str = ..., short_name: builtins.str = ..., role: meshtastic.protobuf.config_pb2.Config.DeviceConfig.Role.ValueType = ..., hw_model: meshtastic.protobuf.mesh_pb2.HardwareModel.ValueType = ..., firmware_version: builtins.str = ..., region: meshtastic.protobuf.config_pb2.Config.LoRaConfig.RegionCode.ValueType = ..., modem_preset: meshtastic.protobuf.config_pb2.Config.LoRaConfig.ModemPreset.ValueType = ..., has_default_channel: builtins.bool = ..., latitude_i: builtins.int = ..., longitude_i: builtins.int = ..., altitude: builtins.int = ..., position_precision: builtins.int = ..., num_online_local_nodes: builtins.int = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["altitude", b"altitude", "firmware_version", b"firmware_version", "has_default_channel", b"has_default_channel", "hw_model", b"hw_model", "latitude_i", b"latitude_i", "long_name", b"long_name", "longitude_i", b"longitude_i", "modem_preset", b"modem_preset", "num_online_local_nodes", b"num_online_local_nodes", "position_precision", b"position_precision", "region", b"region", "role", b"role", "short_name", b"short_name"]) -> None: ... global___MapReport = MapReport python-2.3.14/meshtastic/protobuf/paxcount_pb2.py000066400000000000000000000026231464266072200221200ustar00rootroot00000000000000# -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # source: meshtastic/protobuf/paxcount.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 symbol_database as _symbol_database from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\"meshtastic/protobuf/paxcount.proto\x12\x13meshtastic.protobuf\"5\n\x08Paxcount\x12\x0c\n\x04wifi\x18\x01 \x01(\r\x12\x0b\n\x03\x62le\x18\x02 \x01(\r\x12\x0e\n\x06uptime\x18\x03 \x01(\rBc\n\x13\x63om.geeksville.meshB\x0ePaxcountProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.paxcount_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\016PaxcountProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' _globals['_PAXCOUNT']._serialized_start=59 _globals['_PAXCOUNT']._serialized_end=112 # @@protoc_insertion_point(module_scope) python-2.3.14/meshtastic/protobuf/paxcount_pb2.pyi000066400000000000000000000017331464266072200222720ustar00rootroot00000000000000""" @generated by mypy-protobuf. Do not edit manually! isort:skip_file """ import builtins import google.protobuf.descriptor import google.protobuf.message import typing DESCRIPTOR: google.protobuf.descriptor.FileDescriptor @typing.final class Paxcount(google.protobuf.message.Message): """ TODO: REPLACE """ DESCRIPTOR: google.protobuf.descriptor.Descriptor WIFI_FIELD_NUMBER: builtins.int BLE_FIELD_NUMBER: builtins.int UPTIME_FIELD_NUMBER: builtins.int wifi: builtins.int """ seen Wifi devices """ ble: builtins.int """ Seen BLE devices """ uptime: builtins.int """ Uptime in seconds """ def __init__( self, *, wifi: builtins.int = ..., ble: builtins.int = ..., uptime: builtins.int = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["ble", b"ble", "uptime", b"uptime", "wifi", b"wifi"]) -> None: ... global___Paxcount = Paxcount python-2.3.14/meshtastic/protobuf/portnums_pb2.py000066400000000000000000000043001464266072200221400ustar00rootroot00000000000000# -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # source: meshtastic/protobuf/portnums.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 symbol_database as _symbol_database from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\"meshtastic/protobuf/portnums.proto\x12\x13meshtastic.protobuf*\x8d\x04\n\x07PortNum\x12\x0f\n\x0bUNKNOWN_APP\x10\x00\x12\x14\n\x10TEXT_MESSAGE_APP\x10\x01\x12\x17\n\x13REMOTE_HARDWARE_APP\x10\x02\x12\x10\n\x0cPOSITION_APP\x10\x03\x12\x10\n\x0cNODEINFO_APP\x10\x04\x12\x0f\n\x0bROUTING_APP\x10\x05\x12\r\n\tADMIN_APP\x10\x06\x12\x1f\n\x1bTEXT_MESSAGE_COMPRESSED_APP\x10\x07\x12\x10\n\x0cWAYPOINT_APP\x10\x08\x12\r\n\tAUDIO_APP\x10\t\x12\x18\n\x14\x44\x45TECTION_SENSOR_APP\x10\n\x12\r\n\tREPLY_APP\x10 \x12\x11\n\rIP_TUNNEL_APP\x10!\x12\x12\n\x0ePAXCOUNTER_APP\x10\"\x12\x0e\n\nSERIAL_APP\x10@\x12\x15\n\x11STORE_FORWARD_APP\x10\x41\x12\x12\n\x0eRANGE_TEST_APP\x10\x42\x12\x11\n\rTELEMETRY_APP\x10\x43\x12\x0b\n\x07ZPS_APP\x10\x44\x12\x11\n\rSIMULATOR_APP\x10\x45\x12\x12\n\x0eTRACEROUTE_APP\x10\x46\x12\x14\n\x10NEIGHBORINFO_APP\x10G\x12\x0f\n\x0b\x41TAK_PLUGIN\x10H\x12\x12\n\x0eMAP_REPORT_APP\x10I\x12\x10\n\x0bPRIVATE_APP\x10\x80\x02\x12\x13\n\x0e\x41TAK_FORWARDER\x10\x81\x02\x12\x08\n\x03MAX\x10\xff\x03\x42]\n\x13\x63om.geeksville.meshB\x08PortnumsZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.portnums_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\010PortnumsZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' _globals['_PORTNUM']._serialized_start=60 _globals['_PORTNUM']._serialized_end=585 # @@protoc_insertion_point(module_scope) python-2.3.14/meshtastic/protobuf/portnums_pb2.pyi000066400000000000000000000325001464266072200223140ustar00rootroot00000000000000""" @generated by mypy-protobuf. Do not edit manually! isort:skip_file """ import builtins import google.protobuf.descriptor import google.protobuf.internal.enum_type_wrapper import sys import typing if sys.version_info >= (3, 10): import typing as typing_extensions else: import typing_extensions DESCRIPTOR: google.protobuf.descriptor.FileDescriptor class _PortNum: ValueType = typing.NewType("ValueType", builtins.int) V: typing_extensions.TypeAlias = ValueType class _PortNumEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_PortNum.ValueType], builtins.type): DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor UNKNOWN_APP: _PortNum.ValueType # 0 """ Deprecated: do not use in new code (formerly called OPAQUE) A message sent from a device outside of the mesh, in a form the mesh does not understand NOTE: This must be 0, because it is documented in IMeshService.aidl to be so ENCODING: binary undefined """ TEXT_MESSAGE_APP: _PortNum.ValueType # 1 """ A simple UTF-8 text message, which even the little micros in the mesh can understand and show on their screen eventually in some circumstances even signal might send messages in this form (see below) ENCODING: UTF-8 Plaintext (?) """ REMOTE_HARDWARE_APP: _PortNum.ValueType # 2 """ Reserved for built-in GPIO/example app. See remote_hardware.proto/HardwareMessage for details on the message sent/received to this port number ENCODING: Protobuf """ POSITION_APP: _PortNum.ValueType # 3 """ The built-in position messaging app. Payload is a Position message. ENCODING: Protobuf """ NODEINFO_APP: _PortNum.ValueType # 4 """ The built-in user info app. Payload is a User message. ENCODING: Protobuf """ ROUTING_APP: _PortNum.ValueType # 5 """ Protocol control packets for mesh protocol use. Payload is a Routing message. ENCODING: Protobuf """ ADMIN_APP: _PortNum.ValueType # 6 """ Admin control packets. Payload is a AdminMessage message. ENCODING: Protobuf """ TEXT_MESSAGE_COMPRESSED_APP: _PortNum.ValueType # 7 """ Compressed TEXT_MESSAGE payloads. ENCODING: UTF-8 Plaintext (?) with Unishox2 Compression NOTE: The Device Firmware converts a TEXT_MESSAGE_APP to TEXT_MESSAGE_COMPRESSED_APP if the compressed payload is shorter. There's no need for app developers to do this themselves. Also the firmware will decompress any incoming TEXT_MESSAGE_COMPRESSED_APP payload and convert to TEXT_MESSAGE_APP. """ WAYPOINT_APP: _PortNum.ValueType # 8 """ Waypoint payloads. Payload is a Waypoint message. ENCODING: Protobuf """ AUDIO_APP: _PortNum.ValueType # 9 """ Audio Payloads. Encapsulated codec2 packets. On 2.4 GHZ Bandwidths only for now ENCODING: codec2 audio frames NOTE: audio frames contain a 3 byte header (0xc0 0xde 0xc2) and a one byte marker for the decompressed bitrate. This marker comes from the 'moduleConfig.audio.bitrate' enum minus one. """ DETECTION_SENSOR_APP: _PortNum.ValueType # 10 """ Same as Text Message but originating from Detection Sensor Module. NOTE: This portnum traffic is not sent to the public MQTT starting at firmware version 2.2.9 """ REPLY_APP: _PortNum.ValueType # 32 """ Provides a 'ping' service that replies to any packet it receives. Also serves as a small example module. ENCODING: ASCII Plaintext """ IP_TUNNEL_APP: _PortNum.ValueType # 33 """ Used for the python IP tunnel feature ENCODING: IP Packet. Handled by the python API, firmware ignores this one and pases on. """ PAXCOUNTER_APP: _PortNum.ValueType # 34 """ Paxcounter lib included in the firmware ENCODING: protobuf """ SERIAL_APP: _PortNum.ValueType # 64 """ Provides a hardware serial interface to send and receive from the Meshtastic network. Connect to the RX/TX pins of a device with 38400 8N1. Packets received from the Meshtastic network is forwarded to the RX pin while sending a packet to TX will go out to the Mesh network. Maximum packet size of 240 bytes. Module is disabled by default can be turned on by setting SERIAL_MODULE_ENABLED = 1 in SerialPlugh.cpp. ENCODING: binary undefined """ STORE_FORWARD_APP: _PortNum.ValueType # 65 """ STORE_FORWARD_APP (Work in Progress) Maintained by Jm Casler (MC Hamster) : jm@casler.org ENCODING: Protobuf """ RANGE_TEST_APP: _PortNum.ValueType # 66 """ Optional port for messages for the range test module. ENCODING: ASCII Plaintext NOTE: This portnum traffic is not sent to the public MQTT starting at firmware version 2.2.9 """ TELEMETRY_APP: _PortNum.ValueType # 67 """ Provides a format to send and receive telemetry data from the Meshtastic network. Maintained by Charles Crossan (crossan007) : crossan007@gmail.com ENCODING: Protobuf """ ZPS_APP: _PortNum.ValueType # 68 """ Experimental tools for estimating node position without a GPS Maintained by Github user a-f-G-U-C (a Meshtastic contributor) Project files at https://github.com/a-f-G-U-C/Meshtastic-ZPS ENCODING: arrays of int64 fields """ SIMULATOR_APP: _PortNum.ValueType # 69 """ Used to let multiple instances of Linux native applications communicate as if they did using their LoRa chip. Maintained by GitHub user GUVWAF. Project files at https://github.com/GUVWAF/Meshtasticator ENCODING: Protobuf (?) """ TRACEROUTE_APP: _PortNum.ValueType # 70 """ Provides a traceroute functionality to show the route a packet towards a certain destination would take on the mesh. ENCODING: Protobuf """ NEIGHBORINFO_APP: _PortNum.ValueType # 71 """ Aggregates edge info for the network by sending out a list of each node's neighbors ENCODING: Protobuf """ ATAK_PLUGIN: _PortNum.ValueType # 72 """ ATAK Plugin Portnum for payloads from the official Meshtastic ATAK plugin """ MAP_REPORT_APP: _PortNum.ValueType # 73 """ Provides unencrypted information about a node for consumption by a map via MQTT """ PRIVATE_APP: _PortNum.ValueType # 256 """ Private applications should use portnums >= 256. To simplify initial development and testing you can use "PRIVATE_APP" in your code without needing to rebuild protobuf files (via [regen-protos.sh](https://github.com/meshtastic/firmware/blob/master/bin/regen-protos.sh)) """ ATAK_FORWARDER: _PortNum.ValueType # 257 """ ATAK Forwarder Module https://github.com/paulmandal/atak-forwarder ENCODING: libcotshrink """ MAX: _PortNum.ValueType # 511 """ Currently we limit port nums to no higher than this value """ class PortNum(_PortNum, metaclass=_PortNumEnumTypeWrapper): """ For any new 'apps' that run on the device or via sister apps on phones/PCs they should pick and use a unique 'portnum' for their application. If you are making a new app using meshtastic, please send in a pull request to add your 'portnum' to this master table. PortNums should be assigned in the following range: 0-63 Core Meshtastic use, do not use for third party apps 64-127 Registered 3rd party apps, send in a pull request that adds a new entry to portnums.proto to register your application 256-511 Use one of these portnums for your private applications that you don't want to register publically All other values are reserved. Note: This was formerly a Type enum named 'typ' with the same id # We have change to this 'portnum' based scheme for specifying app handlers for particular payloads. This change is backwards compatible by treating the legacy OPAQUE/CLEAR_TEXT values identically. """ UNKNOWN_APP: PortNum.ValueType # 0 """ Deprecated: do not use in new code (formerly called OPAQUE) A message sent from a device outside of the mesh, in a form the mesh does not understand NOTE: This must be 0, because it is documented in IMeshService.aidl to be so ENCODING: binary undefined """ TEXT_MESSAGE_APP: PortNum.ValueType # 1 """ A simple UTF-8 text message, which even the little micros in the mesh can understand and show on their screen eventually in some circumstances even signal might send messages in this form (see below) ENCODING: UTF-8 Plaintext (?) """ REMOTE_HARDWARE_APP: PortNum.ValueType # 2 """ Reserved for built-in GPIO/example app. See remote_hardware.proto/HardwareMessage for details on the message sent/received to this port number ENCODING: Protobuf """ POSITION_APP: PortNum.ValueType # 3 """ The built-in position messaging app. Payload is a Position message. ENCODING: Protobuf """ NODEINFO_APP: PortNum.ValueType # 4 """ The built-in user info app. Payload is a User message. ENCODING: Protobuf """ ROUTING_APP: PortNum.ValueType # 5 """ Protocol control packets for mesh protocol use. Payload is a Routing message. ENCODING: Protobuf """ ADMIN_APP: PortNum.ValueType # 6 """ Admin control packets. Payload is a AdminMessage message. ENCODING: Protobuf """ TEXT_MESSAGE_COMPRESSED_APP: PortNum.ValueType # 7 """ Compressed TEXT_MESSAGE payloads. ENCODING: UTF-8 Plaintext (?) with Unishox2 Compression NOTE: The Device Firmware converts a TEXT_MESSAGE_APP to TEXT_MESSAGE_COMPRESSED_APP if the compressed payload is shorter. There's no need for app developers to do this themselves. Also the firmware will decompress any incoming TEXT_MESSAGE_COMPRESSED_APP payload and convert to TEXT_MESSAGE_APP. """ WAYPOINT_APP: PortNum.ValueType # 8 """ Waypoint payloads. Payload is a Waypoint message. ENCODING: Protobuf """ AUDIO_APP: PortNum.ValueType # 9 """ Audio Payloads. Encapsulated codec2 packets. On 2.4 GHZ Bandwidths only for now ENCODING: codec2 audio frames NOTE: audio frames contain a 3 byte header (0xc0 0xde 0xc2) and a one byte marker for the decompressed bitrate. This marker comes from the 'moduleConfig.audio.bitrate' enum minus one. """ DETECTION_SENSOR_APP: PortNum.ValueType # 10 """ Same as Text Message but originating from Detection Sensor Module. NOTE: This portnum traffic is not sent to the public MQTT starting at firmware version 2.2.9 """ REPLY_APP: PortNum.ValueType # 32 """ Provides a 'ping' service that replies to any packet it receives. Also serves as a small example module. ENCODING: ASCII Plaintext """ IP_TUNNEL_APP: PortNum.ValueType # 33 """ Used for the python IP tunnel feature ENCODING: IP Packet. Handled by the python API, firmware ignores this one and pases on. """ PAXCOUNTER_APP: PortNum.ValueType # 34 """ Paxcounter lib included in the firmware ENCODING: protobuf """ SERIAL_APP: PortNum.ValueType # 64 """ Provides a hardware serial interface to send and receive from the Meshtastic network. Connect to the RX/TX pins of a device with 38400 8N1. Packets received from the Meshtastic network is forwarded to the RX pin while sending a packet to TX will go out to the Mesh network. Maximum packet size of 240 bytes. Module is disabled by default can be turned on by setting SERIAL_MODULE_ENABLED = 1 in SerialPlugh.cpp. ENCODING: binary undefined """ STORE_FORWARD_APP: PortNum.ValueType # 65 """ STORE_FORWARD_APP (Work in Progress) Maintained by Jm Casler (MC Hamster) : jm@casler.org ENCODING: Protobuf """ RANGE_TEST_APP: PortNum.ValueType # 66 """ Optional port for messages for the range test module. ENCODING: ASCII Plaintext NOTE: This portnum traffic is not sent to the public MQTT starting at firmware version 2.2.9 """ TELEMETRY_APP: PortNum.ValueType # 67 """ Provides a format to send and receive telemetry data from the Meshtastic network. Maintained by Charles Crossan (crossan007) : crossan007@gmail.com ENCODING: Protobuf """ ZPS_APP: PortNum.ValueType # 68 """ Experimental tools for estimating node position without a GPS Maintained by Github user a-f-G-U-C (a Meshtastic contributor) Project files at https://github.com/a-f-G-U-C/Meshtastic-ZPS ENCODING: arrays of int64 fields """ SIMULATOR_APP: PortNum.ValueType # 69 """ Used to let multiple instances of Linux native applications communicate as if they did using their LoRa chip. Maintained by GitHub user GUVWAF. Project files at https://github.com/GUVWAF/Meshtasticator ENCODING: Protobuf (?) """ TRACEROUTE_APP: PortNum.ValueType # 70 """ Provides a traceroute functionality to show the route a packet towards a certain destination would take on the mesh. ENCODING: Protobuf """ NEIGHBORINFO_APP: PortNum.ValueType # 71 """ Aggregates edge info for the network by sending out a list of each node's neighbors ENCODING: Protobuf """ ATAK_PLUGIN: PortNum.ValueType # 72 """ ATAK Plugin Portnum for payloads from the official Meshtastic ATAK plugin """ MAP_REPORT_APP: PortNum.ValueType # 73 """ Provides unencrypted information about a node for consumption by a map via MQTT """ PRIVATE_APP: PortNum.ValueType # 256 """ Private applications should use portnums >= 256. To simplify initial development and testing you can use "PRIVATE_APP" in your code without needing to rebuild protobuf files (via [regen-protos.sh](https://github.com/meshtastic/firmware/blob/master/bin/regen-protos.sh)) """ ATAK_FORWARDER: PortNum.ValueType # 257 """ ATAK Forwarder Module https://github.com/paulmandal/atak-forwarder ENCODING: libcotshrink """ MAX: PortNum.ValueType # 511 """ Currently we limit port nums to no higher than this value """ global___PortNum = PortNum python-2.3.14/meshtastic/protobuf/powermon_pb2.py000066400000000000000000000035111464266072200221220ustar00rootroot00000000000000# -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # source: meshtastic/protobuf/powermon.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 symbol_database as _symbol_database from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\"meshtastic/protobuf/powermon.proto\x12\x13meshtastic.protobuf\"\xe0\x01\n\x08PowerMon\"\xd3\x01\n\x05State\x12\x08\n\x04None\x10\x00\x12\x11\n\rCPU_DeepSleep\x10\x01\x12\x12\n\x0e\x43PU_LightSleep\x10\x02\x12\x0c\n\x08Vext1_On\x10\x04\x12\r\n\tLora_RXOn\x10\x08\x12\r\n\tLora_TXOn\x10\x10\x12\x11\n\rLora_RXActive\x10 \x12\t\n\x05\x42T_On\x10@\x12\x0b\n\x06LED_On\x10\x80\x01\x12\x0e\n\tScreen_On\x10\x80\x02\x12\x13\n\x0eScreen_Drawing\x10\x80\x04\x12\x0c\n\x07Wifi_On\x10\x80\x08\x12\x0f\n\nGPS_Active\x10\x80\x10\x42\x63\n\x13\x63om.geeksville.meshB\x0ePowerMonProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.powermon_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\016PowerMonProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' _globals['_POWERMON']._serialized_start=60 _globals['_POWERMON']._serialized_end=284 _globals['_POWERMON_STATE']._serialized_start=73 _globals['_POWERMON_STATE']._serialized_end=284 # @@protoc_insertion_point(module_scope) python-2.3.14/meshtastic/protobuf/powermon_pb2.pyi000066400000000000000000000102361464266072200222750ustar00rootroot00000000000000""" @generated by mypy-protobuf. Do not edit manually! isort:skip_file """ import builtins import google.protobuf.descriptor import google.protobuf.internal.enum_type_wrapper import google.protobuf.message import sys import typing if sys.version_info >= (3, 10): import typing as typing_extensions else: import typing_extensions DESCRIPTOR: google.protobuf.descriptor.FileDescriptor @typing.final class PowerMon(google.protobuf.message.Message): """Note: There are no 'PowerMon' messages normally in use (PowerMons are sent only as structured logs - slogs). But we wrap our State enum in this message to effectively nest a namespace (without our linter yelling at us) """ DESCRIPTOR: google.protobuf.descriptor.Descriptor class _State: ValueType = typing.NewType("ValueType", builtins.int) V: typing_extensions.TypeAlias = ValueType class _StateEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[PowerMon._State.ValueType], builtins.type): DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor CPU_DeepSleep: PowerMon._State.ValueType # 1 CPU_LightSleep: PowerMon._State.ValueType # 2 Vext1_On: PowerMon._State.ValueType # 4 """ The external Vext1 power is on. Many boards have auxillary power rails that the CPU turns on only occasionally. In cases where that rail has multiple devices on it we usually want to have logging on the state of that rail as an independent record. For instance on the Heltec Tracker 1.1 board, this rail is the power source for the GPS and screen. The log messages will be short and complete (see PowerMon.Event in the protobufs for details). something like "S:PM:C,0x00001234,REASON" where the hex number is the bitmask of all current states. (We use a bitmask for states so that if a log message gets lost it won't be fatal) """ Lora_RXOn: PowerMon._State.ValueType # 8 Lora_TXOn: PowerMon._State.ValueType # 16 Lora_RXActive: PowerMon._State.ValueType # 32 BT_On: PowerMon._State.ValueType # 64 LED_On: PowerMon._State.ValueType # 128 Screen_On: PowerMon._State.ValueType # 256 Screen_Drawing: PowerMon._State.ValueType # 512 Wifi_On: PowerMon._State.ValueType # 1024 GPS_Active: PowerMon._State.ValueType # 2048 """ GPS is actively trying to find our location See GPSPowerState for more details """ class State(_State, metaclass=_StateEnumTypeWrapper): """Any significant power changing event in meshtastic should be tagged with a powermon state transition. If you are making new meshtastic features feel free to add new entries at the end of this definition. """ CPU_DeepSleep: PowerMon.State.ValueType # 1 CPU_LightSleep: PowerMon.State.ValueType # 2 Vext1_On: PowerMon.State.ValueType # 4 """ The external Vext1 power is on. Many boards have auxillary power rails that the CPU turns on only occasionally. In cases where that rail has multiple devices on it we usually want to have logging on the state of that rail as an independent record. For instance on the Heltec Tracker 1.1 board, this rail is the power source for the GPS and screen. The log messages will be short and complete (see PowerMon.Event in the protobufs for details). something like "S:PM:C,0x00001234,REASON" where the hex number is the bitmask of all current states. (We use a bitmask for states so that if a log message gets lost it won't be fatal) """ Lora_RXOn: PowerMon.State.ValueType # 8 Lora_TXOn: PowerMon.State.ValueType # 16 Lora_RXActive: PowerMon.State.ValueType # 32 BT_On: PowerMon.State.ValueType # 64 LED_On: PowerMon.State.ValueType # 128 Screen_On: PowerMon.State.ValueType # 256 Screen_Drawing: PowerMon.State.ValueType # 512 Wifi_On: PowerMon.State.ValueType # 1024 GPS_Active: PowerMon.State.ValueType # 2048 """ GPS is actively trying to find our location See GPSPowerState for more details """ def __init__( self, ) -> None: ... global___PowerMon = PowerMon python-2.3.14/meshtastic/protobuf/remote_hardware_pb2.py000066400000000000000000000034771464266072200234370ustar00rootroot00000000000000# -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # source: meshtastic/protobuf/remote_hardware.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 symbol_database as _symbol_database from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n)meshtastic/protobuf/remote_hardware.proto\x12\x13meshtastic.protobuf\"\xdf\x01\n\x0fHardwareMessage\x12\x37\n\x04type\x18\x01 \x01(\x0e\x32).meshtastic.protobuf.HardwareMessage.Type\x12\x11\n\tgpio_mask\x18\x02 \x01(\x04\x12\x12\n\ngpio_value\x18\x03 \x01(\x04\"l\n\x04Type\x12\t\n\x05UNSET\x10\x00\x12\x0f\n\x0bWRITE_GPIOS\x10\x01\x12\x0f\n\x0bWATCH_GPIOS\x10\x02\x12\x11\n\rGPIOS_CHANGED\x10\x03\x12\x0e\n\nREAD_GPIOS\x10\x04\x12\x14\n\x10READ_GPIOS_REPLY\x10\x05\x42\x63\n\x13\x63om.geeksville.meshB\x0eRemoteHardwareZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.remote_hardware_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\016RemoteHardwareZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' _globals['_HARDWAREMESSAGE']._serialized_start=67 _globals['_HARDWAREMESSAGE']._serialized_end=290 _globals['_HARDWAREMESSAGE_TYPE']._serialized_start=182 _globals['_HARDWAREMESSAGE_TYPE']._serialized_end=290 # @@protoc_insertion_point(module_scope) python-2.3.14/meshtastic/protobuf/remote_hardware_pb2.pyi000066400000000000000000000105771464266072200236070ustar00rootroot00000000000000""" @generated by mypy-protobuf. Do not edit manually! isort:skip_file """ import builtins import google.protobuf.descriptor import google.protobuf.internal.enum_type_wrapper import google.protobuf.message import sys import typing if sys.version_info >= (3, 10): import typing as typing_extensions else: import typing_extensions DESCRIPTOR: google.protobuf.descriptor.FileDescriptor @typing.final class HardwareMessage(google.protobuf.message.Message): """ An example app to show off the module system. This message is used for REMOTE_HARDWARE_APP PortNums. Also provides easy remote access to any GPIO. In the future other remote hardware operations can be added based on user interest (i.e. serial output, spi/i2c input/output). FIXME - currently this feature is turned on by default which is dangerous because no security yet (beyond the channel mechanism). It should be off by default and then protected based on some TBD mechanism (a special channel once multichannel support is included?) """ DESCRIPTOR: google.protobuf.descriptor.Descriptor class _Type: ValueType = typing.NewType("ValueType", builtins.int) V: typing_extensions.TypeAlias = ValueType class _TypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[HardwareMessage._Type.ValueType], builtins.type): DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor UNSET: HardwareMessage._Type.ValueType # 0 """ Unset/unused """ WRITE_GPIOS: HardwareMessage._Type.ValueType # 1 """ Set gpio gpios based on gpio_mask/gpio_value """ WATCH_GPIOS: HardwareMessage._Type.ValueType # 2 """ We are now interested in watching the gpio_mask gpios. If the selected gpios change, please broadcast GPIOS_CHANGED. Will implicitly change the gpios requested to be INPUT gpios. """ GPIOS_CHANGED: HardwareMessage._Type.ValueType # 3 """ The gpios listed in gpio_mask have changed, the new values are listed in gpio_value """ READ_GPIOS: HardwareMessage._Type.ValueType # 4 """ Read the gpios specified in gpio_mask, send back a READ_GPIOS_REPLY reply with gpio_value populated """ READ_GPIOS_REPLY: HardwareMessage._Type.ValueType # 5 """ A reply to READ_GPIOS. gpio_mask and gpio_value will be populated """ class Type(_Type, metaclass=_TypeEnumTypeWrapper): """ TODO: REPLACE """ UNSET: HardwareMessage.Type.ValueType # 0 """ Unset/unused """ WRITE_GPIOS: HardwareMessage.Type.ValueType # 1 """ Set gpio gpios based on gpio_mask/gpio_value """ WATCH_GPIOS: HardwareMessage.Type.ValueType # 2 """ We are now interested in watching the gpio_mask gpios. If the selected gpios change, please broadcast GPIOS_CHANGED. Will implicitly change the gpios requested to be INPUT gpios. """ GPIOS_CHANGED: HardwareMessage.Type.ValueType # 3 """ The gpios listed in gpio_mask have changed, the new values are listed in gpio_value """ READ_GPIOS: HardwareMessage.Type.ValueType # 4 """ Read the gpios specified in gpio_mask, send back a READ_GPIOS_REPLY reply with gpio_value populated """ READ_GPIOS_REPLY: HardwareMessage.Type.ValueType # 5 """ A reply to READ_GPIOS. gpio_mask and gpio_value will be populated """ TYPE_FIELD_NUMBER: builtins.int GPIO_MASK_FIELD_NUMBER: builtins.int GPIO_VALUE_FIELD_NUMBER: builtins.int type: global___HardwareMessage.Type.ValueType """ What type of HardwareMessage is this? """ gpio_mask: builtins.int """ What gpios are we changing. Not used for all MessageTypes, see MessageType for details """ gpio_value: builtins.int """ For gpios that were listed in gpio_mask as valid, what are the signal levels for those gpios. Not used for all MessageTypes, see MessageType for details """ def __init__( self, *, type: global___HardwareMessage.Type.ValueType = ..., gpio_mask: builtins.int = ..., gpio_value: builtins.int = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["gpio_mask", b"gpio_mask", "gpio_value", b"gpio_value", "type", b"type"]) -> None: ... global___HardwareMessage = HardwareMessage python-2.3.14/meshtastic/protobuf/rtttl_pb2.py000066400000000000000000000025311464266072200214260ustar00rootroot00000000000000# -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # source: meshtastic/protobuf/rtttl.proto """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1fmeshtastic/protobuf/rtttl.proto\x12\x13meshtastic.protobuf\"\x1f\n\x0bRTTTLConfig\x12\x10\n\x08ringtone\x18\x01 \x01(\tBf\n\x13\x63om.geeksville.meshB\x11RTTTLConfigProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.rtttl_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\021RTTTLConfigProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' _globals['_RTTTLCONFIG']._serialized_start=56 _globals['_RTTTLCONFIG']._serialized_end=87 # @@protoc_insertion_point(module_scope) python-2.3.14/meshtastic/protobuf/rtttl_pb2.pyi000066400000000000000000000013721464266072200216010ustar00rootroot00000000000000""" @generated by mypy-protobuf. Do not edit manually! isort:skip_file """ import builtins import google.protobuf.descriptor import google.protobuf.message import typing DESCRIPTOR: google.protobuf.descriptor.FileDescriptor @typing.final class RTTTLConfig(google.protobuf.message.Message): """ Canned message module configuration. """ DESCRIPTOR: google.protobuf.descriptor.Descriptor RINGTONE_FIELD_NUMBER: builtins.int ringtone: builtins.str """ Ringtone for PWM Buzzer in RTTTL Format. """ def __init__( self, *, ringtone: builtins.str = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["ringtone", b"ringtone"]) -> None: ... global___RTTTLConfig = RTTTLConfig python-2.3.14/meshtastic/protobuf/storeforward_pb2.py000066400000000000000000000067061464266072200230060ustar00rootroot00000000000000# -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # source: meshtastic/protobuf/storeforward.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 symbol_database as _symbol_database from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n&meshtastic/protobuf/storeforward.proto\x12\x13meshtastic.protobuf\"\xc0\x07\n\x0fStoreAndForward\x12@\n\x02rr\x18\x01 \x01(\x0e\x32\x34.meshtastic.protobuf.StoreAndForward.RequestResponse\x12@\n\x05stats\x18\x02 \x01(\x0b\x32/.meshtastic.protobuf.StoreAndForward.StatisticsH\x00\x12?\n\x07history\x18\x03 \x01(\x0b\x32,.meshtastic.protobuf.StoreAndForward.HistoryH\x00\x12\x43\n\theartbeat\x18\x04 \x01(\x0b\x32..meshtastic.protobuf.StoreAndForward.HeartbeatH\x00\x12\x0e\n\x04text\x18\x05 \x01(\x0cH\x00\x1a\xcd\x01\n\nStatistics\x12\x16\n\x0emessages_total\x18\x01 \x01(\r\x12\x16\n\x0emessages_saved\x18\x02 \x01(\r\x12\x14\n\x0cmessages_max\x18\x03 \x01(\r\x12\x0f\n\x07up_time\x18\x04 \x01(\r\x12\x10\n\x08requests\x18\x05 \x01(\r\x12\x18\n\x10requests_history\x18\x06 \x01(\r\x12\x11\n\theartbeat\x18\x07 \x01(\x08\x12\x12\n\nreturn_max\x18\x08 \x01(\r\x12\x15\n\rreturn_window\x18\t \x01(\r\x1aI\n\x07History\x12\x18\n\x10history_messages\x18\x01 \x01(\r\x12\x0e\n\x06window\x18\x02 \x01(\r\x12\x14\n\x0clast_request\x18\x03 \x01(\r\x1a.\n\tHeartbeat\x12\x0e\n\x06period\x18\x01 \x01(\r\x12\x11\n\tsecondary\x18\x02 \x01(\r\"\xbc\x02\n\x0fRequestResponse\x12\t\n\x05UNSET\x10\x00\x12\x10\n\x0cROUTER_ERROR\x10\x01\x12\x14\n\x10ROUTER_HEARTBEAT\x10\x02\x12\x0f\n\x0bROUTER_PING\x10\x03\x12\x0f\n\x0bROUTER_PONG\x10\x04\x12\x0f\n\x0bROUTER_BUSY\x10\x05\x12\x12\n\x0eROUTER_HISTORY\x10\x06\x12\x10\n\x0cROUTER_STATS\x10\x07\x12\x16\n\x12ROUTER_TEXT_DIRECT\x10\x08\x12\x19\n\x15ROUTER_TEXT_BROADCAST\x10\t\x12\x10\n\x0c\x43LIENT_ERROR\x10@\x12\x12\n\x0e\x43LIENT_HISTORY\x10\x41\x12\x10\n\x0c\x43LIENT_STATS\x10\x42\x12\x0f\n\x0b\x43LIENT_PING\x10\x43\x12\x0f\n\x0b\x43LIENT_PONG\x10\x44\x12\x10\n\x0c\x43LIENT_ABORT\x10jB\t\n\x07variantBj\n\x13\x63om.geeksville.meshB\x15StoreAndForwardProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.storeforward_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\025StoreAndForwardProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' _globals['_STOREANDFORWARD']._serialized_start=64 _globals['_STOREANDFORWARD']._serialized_end=1024 _globals['_STOREANDFORWARD_STATISTICS']._serialized_start=366 _globals['_STOREANDFORWARD_STATISTICS']._serialized_end=571 _globals['_STOREANDFORWARD_HISTORY']._serialized_start=573 _globals['_STOREANDFORWARD_HISTORY']._serialized_end=646 _globals['_STOREANDFORWARD_HEARTBEAT']._serialized_start=648 _globals['_STOREANDFORWARD_HEARTBEAT']._serialized_end=694 _globals['_STOREANDFORWARD_REQUESTRESPONSE']._serialized_start=697 _globals['_STOREANDFORWARD_REQUESTRESPONSE']._serialized_end=1013 # @@protoc_insertion_point(module_scope) python-2.3.14/meshtastic/protobuf/storeforward_pb2.pyi000066400000000000000000000271311464266072200231520ustar00rootroot00000000000000""" @generated by mypy-protobuf. Do not edit manually! isort:skip_file """ import builtins import google.protobuf.descriptor import google.protobuf.internal.enum_type_wrapper import google.protobuf.message import sys import typing if sys.version_info >= (3, 10): import typing as typing_extensions else: import typing_extensions DESCRIPTOR: google.protobuf.descriptor.FileDescriptor @typing.final class StoreAndForward(google.protobuf.message.Message): """ TODO: REPLACE """ DESCRIPTOR: google.protobuf.descriptor.Descriptor class _RequestResponse: ValueType = typing.NewType("ValueType", builtins.int) V: typing_extensions.TypeAlias = ValueType class _RequestResponseEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[StoreAndForward._RequestResponse.ValueType], builtins.type): DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor UNSET: StoreAndForward._RequestResponse.ValueType # 0 """ Unset/unused """ ROUTER_ERROR: StoreAndForward._RequestResponse.ValueType # 1 """ Router is an in error state. """ ROUTER_HEARTBEAT: StoreAndForward._RequestResponse.ValueType # 2 """ Router heartbeat """ ROUTER_PING: StoreAndForward._RequestResponse.ValueType # 3 """ Router has requested the client respond. This can work as a "are you there" message. """ ROUTER_PONG: StoreAndForward._RequestResponse.ValueType # 4 """ The response to a "Ping" """ ROUTER_BUSY: StoreAndForward._RequestResponse.ValueType # 5 """ Router is currently busy. Please try again later. """ ROUTER_HISTORY: StoreAndForward._RequestResponse.ValueType # 6 """ Router is responding to a request for history. """ ROUTER_STATS: StoreAndForward._RequestResponse.ValueType # 7 """ Router is responding to a request for stats. """ ROUTER_TEXT_DIRECT: StoreAndForward._RequestResponse.ValueType # 8 """ Router sends a text message from its history that was a direct message. """ ROUTER_TEXT_BROADCAST: StoreAndForward._RequestResponse.ValueType # 9 """ Router sends a text message from its history that was a broadcast. """ CLIENT_ERROR: StoreAndForward._RequestResponse.ValueType # 64 """ Client is an in error state. """ CLIENT_HISTORY: StoreAndForward._RequestResponse.ValueType # 65 """ Client has requested a replay from the router. """ CLIENT_STATS: StoreAndForward._RequestResponse.ValueType # 66 """ Client has requested stats from the router. """ CLIENT_PING: StoreAndForward._RequestResponse.ValueType # 67 """ Client has requested the router respond. This can work as a "are you there" message. """ CLIENT_PONG: StoreAndForward._RequestResponse.ValueType # 68 """ The response to a "Ping" """ CLIENT_ABORT: StoreAndForward._RequestResponse.ValueType # 106 """ Client has requested that the router abort processing the client's request """ class RequestResponse(_RequestResponse, metaclass=_RequestResponseEnumTypeWrapper): """ 001 - 063 = From Router 064 - 127 = From Client """ UNSET: StoreAndForward.RequestResponse.ValueType # 0 """ Unset/unused """ ROUTER_ERROR: StoreAndForward.RequestResponse.ValueType # 1 """ Router is an in error state. """ ROUTER_HEARTBEAT: StoreAndForward.RequestResponse.ValueType # 2 """ Router heartbeat """ ROUTER_PING: StoreAndForward.RequestResponse.ValueType # 3 """ Router has requested the client respond. This can work as a "are you there" message. """ ROUTER_PONG: StoreAndForward.RequestResponse.ValueType # 4 """ The response to a "Ping" """ ROUTER_BUSY: StoreAndForward.RequestResponse.ValueType # 5 """ Router is currently busy. Please try again later. """ ROUTER_HISTORY: StoreAndForward.RequestResponse.ValueType # 6 """ Router is responding to a request for history. """ ROUTER_STATS: StoreAndForward.RequestResponse.ValueType # 7 """ Router is responding to a request for stats. """ ROUTER_TEXT_DIRECT: StoreAndForward.RequestResponse.ValueType # 8 """ Router sends a text message from its history that was a direct message. """ ROUTER_TEXT_BROADCAST: StoreAndForward.RequestResponse.ValueType # 9 """ Router sends a text message from its history that was a broadcast. """ CLIENT_ERROR: StoreAndForward.RequestResponse.ValueType # 64 """ Client is an in error state. """ CLIENT_HISTORY: StoreAndForward.RequestResponse.ValueType # 65 """ Client has requested a replay from the router. """ CLIENT_STATS: StoreAndForward.RequestResponse.ValueType # 66 """ Client has requested stats from the router. """ CLIENT_PING: StoreAndForward.RequestResponse.ValueType # 67 """ Client has requested the router respond. This can work as a "are you there" message. """ CLIENT_PONG: StoreAndForward.RequestResponse.ValueType # 68 """ The response to a "Ping" """ CLIENT_ABORT: StoreAndForward.RequestResponse.ValueType # 106 """ Client has requested that the router abort processing the client's request """ @typing.final class Statistics(google.protobuf.message.Message): """ TODO: REPLACE """ DESCRIPTOR: google.protobuf.descriptor.Descriptor MESSAGES_TOTAL_FIELD_NUMBER: builtins.int MESSAGES_SAVED_FIELD_NUMBER: builtins.int MESSAGES_MAX_FIELD_NUMBER: builtins.int UP_TIME_FIELD_NUMBER: builtins.int REQUESTS_FIELD_NUMBER: builtins.int REQUESTS_HISTORY_FIELD_NUMBER: builtins.int HEARTBEAT_FIELD_NUMBER: builtins.int RETURN_MAX_FIELD_NUMBER: builtins.int RETURN_WINDOW_FIELD_NUMBER: builtins.int messages_total: builtins.int """ Number of messages we have ever seen """ messages_saved: builtins.int """ Number of messages we have currently saved our history. """ messages_max: builtins.int """ Maximum number of messages we will save """ up_time: builtins.int """ Router uptime in seconds """ requests: builtins.int """ Number of times any client sent a request to the S&F. """ requests_history: builtins.int """ Number of times the history was requested. """ heartbeat: builtins.bool """ Is the heartbeat enabled on the server? """ return_max: builtins.int """ Maximum number of messages the server will return. """ return_window: builtins.int """ Maximum history window in minutes the server will return messages from. """ def __init__( self, *, messages_total: builtins.int = ..., messages_saved: builtins.int = ..., messages_max: builtins.int = ..., up_time: builtins.int = ..., requests: builtins.int = ..., requests_history: builtins.int = ..., heartbeat: builtins.bool = ..., return_max: builtins.int = ..., return_window: builtins.int = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["heartbeat", b"heartbeat", "messages_max", b"messages_max", "messages_saved", b"messages_saved", "messages_total", b"messages_total", "requests", b"requests", "requests_history", b"requests_history", "return_max", b"return_max", "return_window", b"return_window", "up_time", b"up_time"]) -> None: ... @typing.final class History(google.protobuf.message.Message): """ TODO: REPLACE """ DESCRIPTOR: google.protobuf.descriptor.Descriptor HISTORY_MESSAGES_FIELD_NUMBER: builtins.int WINDOW_FIELD_NUMBER: builtins.int LAST_REQUEST_FIELD_NUMBER: builtins.int history_messages: builtins.int """ Number of that will be sent to the client """ window: builtins.int """ The window of messages that was used to filter the history client requested """ last_request: builtins.int """ Index in the packet history of the last message sent in a previous request to the server. Will be sent to the client before sending the history and can be set in a subsequent request to avoid getting packets the server already sent to the client. """ def __init__( self, *, history_messages: builtins.int = ..., window: builtins.int = ..., last_request: builtins.int = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["history_messages", b"history_messages", "last_request", b"last_request", "window", b"window"]) -> None: ... @typing.final class Heartbeat(google.protobuf.message.Message): """ TODO: REPLACE """ DESCRIPTOR: google.protobuf.descriptor.Descriptor PERIOD_FIELD_NUMBER: builtins.int SECONDARY_FIELD_NUMBER: builtins.int period: builtins.int """ Period in seconds that the heartbeat is sent out that will be sent to the client """ secondary: builtins.int """ If set, this is not the primary Store & Forward router on the mesh """ def __init__( self, *, period: builtins.int = ..., secondary: builtins.int = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["period", b"period", "secondary", b"secondary"]) -> None: ... RR_FIELD_NUMBER: builtins.int STATS_FIELD_NUMBER: builtins.int HISTORY_FIELD_NUMBER: builtins.int HEARTBEAT_FIELD_NUMBER: builtins.int TEXT_FIELD_NUMBER: builtins.int rr: global___StoreAndForward.RequestResponse.ValueType """ TODO: REPLACE """ text: builtins.bytes """ Text from history message. """ @property def stats(self) -> global___StoreAndForward.Statistics: """ TODO: REPLACE """ @property def history(self) -> global___StoreAndForward.History: """ TODO: REPLACE """ @property def heartbeat(self) -> global___StoreAndForward.Heartbeat: """ TODO: REPLACE """ def __init__( self, *, rr: global___StoreAndForward.RequestResponse.ValueType = ..., stats: global___StoreAndForward.Statistics | None = ..., history: global___StoreAndForward.History | None = ..., heartbeat: global___StoreAndForward.Heartbeat | None = ..., text: builtins.bytes = ..., ) -> None: ... def HasField(self, field_name: typing.Literal["heartbeat", b"heartbeat", "history", b"history", "stats", b"stats", "text", b"text", "variant", b"variant"]) -> builtins.bool: ... def ClearField(self, field_name: typing.Literal["heartbeat", b"heartbeat", "history", b"history", "rr", b"rr", "stats", b"stats", "text", b"text", "variant", b"variant"]) -> None: ... def WhichOneof(self, oneof_group: typing.Literal["variant", b"variant"]) -> typing.Literal["stats", "history", "heartbeat", "text"] | None: ... global___StoreAndForward = StoreAndForward python-2.3.14/meshtastic/protobuf/telemetry_pb2.py000066400000000000000000000117351464266072200222750ustar00rootroot00000000000000# -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # source: meshtastic/protobuf/telemetry.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 symbol_database as _symbol_database from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n#meshtastic/protobuf/telemetry.proto\x12\x13meshtastic.protobuf\"\x81\x01\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\x12\x16\n\x0euptime_seconds\x18\x05 \x01(\r\"\xb6\x02\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\x12\x0b\n\x03iaq\x18\x07 \x01(\r\x12\x10\n\x08\x64istance\x18\x08 \x01(\x02\x12\x0b\n\x03lux\x18\t \x01(\x02\x12\x11\n\twhite_lux\x18\n \x01(\x02\x12\x0e\n\x06ir_lux\x18\x0b \x01(\x02\x12\x0e\n\x06uv_lux\x18\x0c \x01(\x02\x12\x16\n\x0ewind_direction\x18\r \x01(\r\x12\x12\n\nwind_speed\x18\x0e \x01(\x02\x12\x0e\n\x06weight\x18\x0f \x01(\x02\"\x8c\x01\n\x0cPowerMetrics\x12\x13\n\x0b\x63h1_voltage\x18\x01 \x01(\x02\x12\x13\n\x0b\x63h1_current\x18\x02 \x01(\x02\x12\x13\n\x0b\x63h2_voltage\x18\x03 \x01(\x02\x12\x13\n\x0b\x63h2_current\x18\x04 \x01(\x02\x12\x13\n\x0b\x63h3_voltage\x18\x05 \x01(\x02\x12\x13\n\x0b\x63h3_current\x18\x06 \x01(\x02\"\xbf\x02\n\x11\x41irQualityMetrics\x12\x15\n\rpm10_standard\x18\x01 \x01(\r\x12\x15\n\rpm25_standard\x18\x02 \x01(\r\x12\x16\n\x0epm100_standard\x18\x03 \x01(\r\x12\x1a\n\x12pm10_environmental\x18\x04 \x01(\r\x12\x1a\n\x12pm25_environmental\x18\x05 \x01(\r\x12\x1b\n\x13pm100_environmental\x18\x06 \x01(\r\x12\x16\n\x0eparticles_03um\x18\x07 \x01(\r\x12\x16\n\x0eparticles_05um\x18\x08 \x01(\r\x12\x16\n\x0eparticles_10um\x18\t \x01(\r\x12\x16\n\x0eparticles_25um\x18\n \x01(\r\x12\x16\n\x0eparticles_50um\x18\x0b \x01(\r\x12\x17\n\x0fparticles_100um\x18\x0c \x01(\r\"\xad\x02\n\tTelemetry\x12\x0c\n\x04time\x18\x01 \x01(\x07\x12<\n\x0e\x64\x65vice_metrics\x18\x02 \x01(\x0b\x32\".meshtastic.protobuf.DeviceMetricsH\x00\x12\x46\n\x13\x65nvironment_metrics\x18\x03 \x01(\x0b\x32\'.meshtastic.protobuf.EnvironmentMetricsH\x00\x12\x45\n\x13\x61ir_quality_metrics\x18\x04 \x01(\x0b\x32&.meshtastic.protobuf.AirQualityMetricsH\x00\x12:\n\rpower_metrics\x18\x05 \x01(\x0b\x32!.meshtastic.protobuf.PowerMetricsH\x00\x42\t\n\x07variant\">\n\rNau7802Config\x12\x12\n\nzeroOffset\x18\x01 \x01(\x05\x12\x19\n\x11\x63\x61librationFactor\x18\x02 \x01(\x02*\xea\x02\n\x13TelemetrySensorType\x12\x10\n\x0cSENSOR_UNSET\x10\x00\x12\n\n\x06\x42ME280\x10\x01\x12\n\n\x06\x42ME680\x10\x02\x12\x0b\n\x07MCP9808\x10\x03\x12\n\n\x06INA260\x10\x04\x12\n\n\x06INA219\x10\x05\x12\n\n\x06\x42MP280\x10\x06\x12\t\n\x05SHTC3\x10\x07\x12\t\n\x05LPS22\x10\x08\x12\x0b\n\x07QMC6310\x10\t\x12\x0b\n\x07QMI8658\x10\n\x12\x0c\n\x08QMC5883L\x10\x0b\x12\t\n\x05SHT31\x10\x0c\x12\x0c\n\x08PMSA003I\x10\r\x12\x0b\n\x07INA3221\x10\x0e\x12\n\n\x06\x42MP085\x10\x0f\x12\x0c\n\x08RCWL9620\x10\x10\x12\t\n\x05SHT4X\x10\x11\x12\x0c\n\x08VEML7700\x10\x12\x12\x0c\n\x08MLX90632\x10\x13\x12\x0b\n\x07OPT3001\x10\x14\x12\x0c\n\x08LTR390UV\x10\x15\x12\x0e\n\nTSL25911FN\x10\x16\x12\t\n\x05\x41HT10\x10\x17\x12\x10\n\x0c\x44\x46ROBOT_LARK\x10\x18\x12\x0b\n\x07NAU7802\x10\x19\x42\x64\n\x13\x63om.geeksville.meshB\x0fTelemetryProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.telemetry_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\017TelemetryProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' _globals['_TELEMETRYSENSORTYPE']._serialized_start=1339 _globals['_TELEMETRYSENSORTYPE']._serialized_end=1701 _globals['_DEVICEMETRICS']._serialized_start=61 _globals['_DEVICEMETRICS']._serialized_end=190 _globals['_ENVIRONMENTMETRICS']._serialized_start=193 _globals['_ENVIRONMENTMETRICS']._serialized_end=503 _globals['_POWERMETRICS']._serialized_start=506 _globals['_POWERMETRICS']._serialized_end=646 _globals['_AIRQUALITYMETRICS']._serialized_start=649 _globals['_AIRQUALITYMETRICS']._serialized_end=968 _globals['_TELEMETRY']._serialized_start=971 _globals['_TELEMETRY']._serialized_end=1272 _globals['_NAU7802CONFIG']._serialized_start=1274 _globals['_NAU7802CONFIG']._serialized_end=1336 # @@protoc_insertion_point(module_scope) python-2.3.14/meshtastic/protobuf/telemetry_pb2.pyi000066400000000000000000000447661464266072200224600ustar00rootroot00000000000000""" @generated by mypy-protobuf. Do not edit manually! isort:skip_file """ import builtins import google.protobuf.descriptor import google.protobuf.internal.enum_type_wrapper import google.protobuf.message import sys import typing if sys.version_info >= (3, 10): import typing as typing_extensions else: import typing_extensions DESCRIPTOR: google.protobuf.descriptor.FileDescriptor class _TelemetrySensorType: ValueType = typing.NewType("ValueType", builtins.int) V: typing_extensions.TypeAlias = ValueType class _TelemetrySensorTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_TelemetrySensorType.ValueType], builtins.type): DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor SENSOR_UNSET: _TelemetrySensorType.ValueType # 0 """ No external telemetry sensor explicitly set """ BME280: _TelemetrySensorType.ValueType # 1 """ High accuracy temperature, pressure, humidity """ BME680: _TelemetrySensorType.ValueType # 2 """ High accuracy temperature, pressure, humidity, and air resistance """ MCP9808: _TelemetrySensorType.ValueType # 3 """ Very high accuracy temperature """ INA260: _TelemetrySensorType.ValueType # 4 """ Moderate accuracy current and voltage """ INA219: _TelemetrySensorType.ValueType # 5 """ Moderate accuracy current and voltage """ BMP280: _TelemetrySensorType.ValueType # 6 """ High accuracy temperature and pressure """ SHTC3: _TelemetrySensorType.ValueType # 7 """ High accuracy temperature and humidity """ LPS22: _TelemetrySensorType.ValueType # 8 """ High accuracy pressure """ QMC6310: _TelemetrySensorType.ValueType # 9 """ 3-Axis magnetic sensor """ QMI8658: _TelemetrySensorType.ValueType # 10 """ 6-Axis inertial measurement sensor """ QMC5883L: _TelemetrySensorType.ValueType # 11 """ 3-Axis magnetic sensor """ SHT31: _TelemetrySensorType.ValueType # 12 """ High accuracy temperature and humidity """ PMSA003I: _TelemetrySensorType.ValueType # 13 """ PM2.5 air quality sensor """ INA3221: _TelemetrySensorType.ValueType # 14 """ INA3221 3 Channel Voltage / Current Sensor """ BMP085: _TelemetrySensorType.ValueType # 15 """ BMP085/BMP180 High accuracy temperature and pressure (older Version of BMP280) """ RCWL9620: _TelemetrySensorType.ValueType # 16 """ RCWL-9620 Doppler Radar Distance Sensor, used for water level detection """ SHT4X: _TelemetrySensorType.ValueType # 17 """ Sensirion High accuracy temperature and humidity """ VEML7700: _TelemetrySensorType.ValueType # 18 """ VEML7700 high accuracy ambient light(Lux) digital 16-bit resolution sensor. """ MLX90632: _TelemetrySensorType.ValueType # 19 """ MLX90632 non-contact IR temperature sensor. """ OPT3001: _TelemetrySensorType.ValueType # 20 """ TI OPT3001 Ambient Light Sensor """ LTR390UV: _TelemetrySensorType.ValueType # 21 """ Lite On LTR-390UV-01 UV Light Sensor """ TSL25911FN: _TelemetrySensorType.ValueType # 22 """ AMS TSL25911FN RGB Light Sensor """ AHT10: _TelemetrySensorType.ValueType # 23 """ AHT10 Integrated temperature and humidity sensor """ DFROBOT_LARK: _TelemetrySensorType.ValueType # 24 """ DFRobot Lark Weather station (temperature, humidity, pressure, wind speed and direction) """ NAU7802: _TelemetrySensorType.ValueType # 25 """ NAU7802 Scale Chip or compatible """ class TelemetrySensorType(_TelemetrySensorType, metaclass=_TelemetrySensorTypeEnumTypeWrapper): """ Supported I2C Sensors for telemetry in Meshtastic """ SENSOR_UNSET: TelemetrySensorType.ValueType # 0 """ No external telemetry sensor explicitly set """ BME280: TelemetrySensorType.ValueType # 1 """ High accuracy temperature, pressure, humidity """ BME680: TelemetrySensorType.ValueType # 2 """ High accuracy temperature, pressure, humidity, and air resistance """ MCP9808: TelemetrySensorType.ValueType # 3 """ Very high accuracy temperature """ INA260: TelemetrySensorType.ValueType # 4 """ Moderate accuracy current and voltage """ INA219: TelemetrySensorType.ValueType # 5 """ Moderate accuracy current and voltage """ BMP280: TelemetrySensorType.ValueType # 6 """ High accuracy temperature and pressure """ SHTC3: TelemetrySensorType.ValueType # 7 """ High accuracy temperature and humidity """ LPS22: TelemetrySensorType.ValueType # 8 """ High accuracy pressure """ QMC6310: TelemetrySensorType.ValueType # 9 """ 3-Axis magnetic sensor """ QMI8658: TelemetrySensorType.ValueType # 10 """ 6-Axis inertial measurement sensor """ QMC5883L: TelemetrySensorType.ValueType # 11 """ 3-Axis magnetic sensor """ SHT31: TelemetrySensorType.ValueType # 12 """ High accuracy temperature and humidity """ PMSA003I: TelemetrySensorType.ValueType # 13 """ PM2.5 air quality sensor """ INA3221: TelemetrySensorType.ValueType # 14 """ INA3221 3 Channel Voltage / Current Sensor """ BMP085: TelemetrySensorType.ValueType # 15 """ BMP085/BMP180 High accuracy temperature and pressure (older Version of BMP280) """ RCWL9620: TelemetrySensorType.ValueType # 16 """ RCWL-9620 Doppler Radar Distance Sensor, used for water level detection """ SHT4X: TelemetrySensorType.ValueType # 17 """ Sensirion High accuracy temperature and humidity """ VEML7700: TelemetrySensorType.ValueType # 18 """ VEML7700 high accuracy ambient light(Lux) digital 16-bit resolution sensor. """ MLX90632: TelemetrySensorType.ValueType # 19 """ MLX90632 non-contact IR temperature sensor. """ OPT3001: TelemetrySensorType.ValueType # 20 """ TI OPT3001 Ambient Light Sensor """ LTR390UV: TelemetrySensorType.ValueType # 21 """ Lite On LTR-390UV-01 UV Light Sensor """ TSL25911FN: TelemetrySensorType.ValueType # 22 """ AMS TSL25911FN RGB Light Sensor """ AHT10: TelemetrySensorType.ValueType # 23 """ AHT10 Integrated temperature and humidity sensor """ DFROBOT_LARK: TelemetrySensorType.ValueType # 24 """ DFRobot Lark Weather station (temperature, humidity, pressure, wind speed and direction) """ NAU7802: TelemetrySensorType.ValueType # 25 """ NAU7802 Scale Chip or compatible """ global___TelemetrySensorType = TelemetrySensorType @typing.final class DeviceMetrics(google.protobuf.message.Message): """ Key native device metrics such as battery level """ DESCRIPTOR: google.protobuf.descriptor.Descriptor BATTERY_LEVEL_FIELD_NUMBER: builtins.int VOLTAGE_FIELD_NUMBER: builtins.int CHANNEL_UTILIZATION_FIELD_NUMBER: builtins.int AIR_UTIL_TX_FIELD_NUMBER: builtins.int UPTIME_SECONDS_FIELD_NUMBER: builtins.int battery_level: builtins.int """ 0-100 (>100 means powered) """ voltage: builtins.float """ Voltage measured """ channel_utilization: builtins.float """ Utilization for the current channel, including well formed TX, RX and malformed RX (aka noise). """ air_util_tx: builtins.float """ Percent of airtime for transmission used within the last hour. """ uptime_seconds: builtins.int """ How long the device has been running since the last reboot (in seconds) """ def __init__( self, *, battery_level: builtins.int = ..., voltage: builtins.float = ..., channel_utilization: builtins.float = ..., air_util_tx: builtins.float = ..., uptime_seconds: builtins.int = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["air_util_tx", b"air_util_tx", "battery_level", b"battery_level", "channel_utilization", b"channel_utilization", "uptime_seconds", b"uptime_seconds", "voltage", b"voltage"]) -> None: ... global___DeviceMetrics = DeviceMetrics @typing.final class EnvironmentMetrics(google.protobuf.message.Message): """ Weather station or other environmental metrics """ DESCRIPTOR: google.protobuf.descriptor.Descriptor TEMPERATURE_FIELD_NUMBER: builtins.int RELATIVE_HUMIDITY_FIELD_NUMBER: builtins.int BAROMETRIC_PRESSURE_FIELD_NUMBER: builtins.int GAS_RESISTANCE_FIELD_NUMBER: builtins.int VOLTAGE_FIELD_NUMBER: builtins.int CURRENT_FIELD_NUMBER: builtins.int IAQ_FIELD_NUMBER: builtins.int DISTANCE_FIELD_NUMBER: builtins.int LUX_FIELD_NUMBER: builtins.int WHITE_LUX_FIELD_NUMBER: builtins.int IR_LUX_FIELD_NUMBER: builtins.int UV_LUX_FIELD_NUMBER: builtins.int WIND_DIRECTION_FIELD_NUMBER: builtins.int WIND_SPEED_FIELD_NUMBER: builtins.int WEIGHT_FIELD_NUMBER: builtins.int temperature: builtins.float """ Temperature measured """ relative_humidity: builtins.float """ Relative humidity percent measured """ barometric_pressure: builtins.float """ Barometric pressure in hPA measured """ gas_resistance: builtins.float """ Gas resistance in MOhm measured """ voltage: builtins.float """ Voltage measured (To be depreciated in favor of PowerMetrics in Meshtastic 3.x) """ current: builtins.float """ Current measured (To be depreciated in favor of PowerMetrics in Meshtastic 3.x) """ iaq: builtins.int """ relative scale IAQ value as measured by Bosch BME680 . value 0-500. Belongs to Air Quality but is not particle but VOC measurement. Other VOC values can also be put in here. """ distance: builtins.float """ RCWL9620 Doppler Radar Distance Sensor, used for water level detection. Float value in mm. """ lux: builtins.float """ VEML7700 high accuracy ambient light(Lux) digital 16-bit resolution sensor. """ white_lux: builtins.float """ VEML7700 high accuracy white light(irradiance) not calibrated digital 16-bit resolution sensor. """ ir_lux: builtins.float """ Infrared lux """ uv_lux: builtins.float """ Ultraviolet lux """ wind_direction: builtins.int """ Wind direction in degrees 0 degrees = North, 90 = East, etc... """ wind_speed: builtins.float """ Wind speed in m/s """ weight: builtins.float """ Weight in KG """ def __init__( self, *, temperature: builtins.float = ..., relative_humidity: builtins.float = ..., barometric_pressure: builtins.float = ..., gas_resistance: builtins.float = ..., voltage: builtins.float = ..., current: builtins.float = ..., iaq: builtins.int = ..., distance: builtins.float = ..., lux: builtins.float = ..., white_lux: builtins.float = ..., ir_lux: builtins.float = ..., uv_lux: builtins.float = ..., wind_direction: builtins.int = ..., wind_speed: builtins.float = ..., weight: builtins.float = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["barometric_pressure", b"barometric_pressure", "current", b"current", "distance", b"distance", "gas_resistance", b"gas_resistance", "iaq", b"iaq", "ir_lux", b"ir_lux", "lux", b"lux", "relative_humidity", b"relative_humidity", "temperature", b"temperature", "uv_lux", b"uv_lux", "voltage", b"voltage", "weight", b"weight", "white_lux", b"white_lux", "wind_direction", b"wind_direction", "wind_speed", b"wind_speed"]) -> None: ... global___EnvironmentMetrics = EnvironmentMetrics @typing.final class PowerMetrics(google.protobuf.message.Message): """ Power Metrics (voltage / current / etc) """ DESCRIPTOR: google.protobuf.descriptor.Descriptor CH1_VOLTAGE_FIELD_NUMBER: builtins.int CH1_CURRENT_FIELD_NUMBER: builtins.int CH2_VOLTAGE_FIELD_NUMBER: builtins.int CH2_CURRENT_FIELD_NUMBER: builtins.int CH3_VOLTAGE_FIELD_NUMBER: builtins.int CH3_CURRENT_FIELD_NUMBER: builtins.int ch1_voltage: builtins.float """ Voltage (Ch1) """ ch1_current: builtins.float """ Current (Ch1) """ ch2_voltage: builtins.float """ Voltage (Ch2) """ ch2_current: builtins.float """ Current (Ch2) """ ch3_voltage: builtins.float """ Voltage (Ch3) """ ch3_current: builtins.float """ Current (Ch3) """ def __init__( self, *, ch1_voltage: builtins.float = ..., ch1_current: builtins.float = ..., ch2_voltage: builtins.float = ..., ch2_current: builtins.float = ..., ch3_voltage: builtins.float = ..., ch3_current: builtins.float = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["ch1_current", b"ch1_current", "ch1_voltage", b"ch1_voltage", "ch2_current", b"ch2_current", "ch2_voltage", b"ch2_voltage", "ch3_current", b"ch3_current", "ch3_voltage", b"ch3_voltage"]) -> None: ... global___PowerMetrics = PowerMetrics @typing.final class AirQualityMetrics(google.protobuf.message.Message): """ Air quality metrics """ DESCRIPTOR: google.protobuf.descriptor.Descriptor PM10_STANDARD_FIELD_NUMBER: builtins.int PM25_STANDARD_FIELD_NUMBER: builtins.int PM100_STANDARD_FIELD_NUMBER: builtins.int PM10_ENVIRONMENTAL_FIELD_NUMBER: builtins.int PM25_ENVIRONMENTAL_FIELD_NUMBER: builtins.int PM100_ENVIRONMENTAL_FIELD_NUMBER: builtins.int PARTICLES_03UM_FIELD_NUMBER: builtins.int PARTICLES_05UM_FIELD_NUMBER: builtins.int PARTICLES_10UM_FIELD_NUMBER: builtins.int PARTICLES_25UM_FIELD_NUMBER: builtins.int PARTICLES_50UM_FIELD_NUMBER: builtins.int PARTICLES_100UM_FIELD_NUMBER: builtins.int pm10_standard: builtins.int """ Concentration Units Standard PM1.0 """ pm25_standard: builtins.int """ Concentration Units Standard PM2.5 """ pm100_standard: builtins.int """ Concentration Units Standard PM10.0 """ pm10_environmental: builtins.int """ Concentration Units Environmental PM1.0 """ pm25_environmental: builtins.int """ Concentration Units Environmental PM2.5 """ pm100_environmental: builtins.int """ Concentration Units Environmental PM10.0 """ particles_03um: builtins.int """ 0.3um Particle Count """ particles_05um: builtins.int """ 0.5um Particle Count """ particles_10um: builtins.int """ 1.0um Particle Count """ particles_25um: builtins.int """ 2.5um Particle Count """ particles_50um: builtins.int """ 5.0um Particle Count """ particles_100um: builtins.int """ 10.0um Particle Count """ def __init__( self, *, pm10_standard: builtins.int = ..., pm25_standard: builtins.int = ..., pm100_standard: builtins.int = ..., pm10_environmental: builtins.int = ..., pm25_environmental: builtins.int = ..., pm100_environmental: builtins.int = ..., particles_03um: builtins.int = ..., particles_05um: builtins.int = ..., particles_10um: builtins.int = ..., particles_25um: builtins.int = ..., particles_50um: builtins.int = ..., particles_100um: builtins.int = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["particles_03um", b"particles_03um", "particles_05um", b"particles_05um", "particles_100um", b"particles_100um", "particles_10um", b"particles_10um", "particles_25um", b"particles_25um", "particles_50um", b"particles_50um", "pm100_environmental", b"pm100_environmental", "pm100_standard", b"pm100_standard", "pm10_environmental", b"pm10_environmental", "pm10_standard", b"pm10_standard", "pm25_environmental", b"pm25_environmental", "pm25_standard", b"pm25_standard"]) -> None: ... global___AirQualityMetrics = AirQualityMetrics @typing.final class Telemetry(google.protobuf.message.Message): """ Types of Measurements the telemetry module is equipped to handle """ DESCRIPTOR: google.protobuf.descriptor.Descriptor TIME_FIELD_NUMBER: builtins.int DEVICE_METRICS_FIELD_NUMBER: builtins.int ENVIRONMENT_METRICS_FIELD_NUMBER: builtins.int AIR_QUALITY_METRICS_FIELD_NUMBER: builtins.int POWER_METRICS_FIELD_NUMBER: builtins.int time: builtins.int """ Seconds since 1970 - or 0 for unknown/unset """ @property def device_metrics(self) -> global___DeviceMetrics: """ Key native device metrics such as battery level """ @property def environment_metrics(self) -> global___EnvironmentMetrics: """ Weather station or other environmental metrics """ @property def air_quality_metrics(self) -> global___AirQualityMetrics: """ Air quality metrics """ @property def power_metrics(self) -> global___PowerMetrics: """ Power Metrics """ def __init__( self, *, time: builtins.int = ..., device_metrics: global___DeviceMetrics | None = ..., environment_metrics: global___EnvironmentMetrics | None = ..., air_quality_metrics: global___AirQualityMetrics | None = ..., power_metrics: global___PowerMetrics | None = ..., ) -> None: ... def HasField(self, field_name: typing.Literal["air_quality_metrics", b"air_quality_metrics", "device_metrics", b"device_metrics", "environment_metrics", b"environment_metrics", "power_metrics", b"power_metrics", "variant", b"variant"]) -> builtins.bool: ... def ClearField(self, field_name: typing.Literal["air_quality_metrics", b"air_quality_metrics", "device_metrics", b"device_metrics", "environment_metrics", b"environment_metrics", "power_metrics", b"power_metrics", "time", b"time", "variant", b"variant"]) -> None: ... def WhichOneof(self, oneof_group: typing.Literal["variant", b"variant"]) -> typing.Literal["device_metrics", "environment_metrics", "air_quality_metrics", "power_metrics"] | None: ... global___Telemetry = Telemetry @typing.final class Nau7802Config(google.protobuf.message.Message): """ NAU7802 Telemetry configuration, for saving to flash """ DESCRIPTOR: google.protobuf.descriptor.Descriptor ZEROOFFSET_FIELD_NUMBER: builtins.int CALIBRATIONFACTOR_FIELD_NUMBER: builtins.int zeroOffset: builtins.int """ The offset setting for the NAU7802 """ calibrationFactor: builtins.float """ The calibration factor for the NAU7802 """ def __init__( self, *, zeroOffset: builtins.int = ..., calibrationFactor: builtins.float = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["calibrationFactor", b"calibrationFactor", "zeroOffset", b"zeroOffset"]) -> None: ... global___Nau7802Config = Nau7802Config python-2.3.14/meshtastic/protobuf/xmodem_pb2.py000066400000000000000000000034701464266072200215510ustar00rootroot00000000000000# -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # source: meshtastic/protobuf/xmodem.proto """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n meshtastic/protobuf/xmodem.proto\x12\x13meshtastic.protobuf\"\xbf\x01\n\x06XModem\x12\x34\n\x07\x63ontrol\x18\x01 \x01(\x0e\x32#.meshtastic.protobuf.XModem.Control\x12\x0b\n\x03seq\x18\x02 \x01(\r\x12\r\n\x05\x63rc16\x18\x03 \x01(\r\x12\x0e\n\x06\x62uffer\x18\x04 \x01(\x0c\"S\n\x07\x43ontrol\x12\x07\n\x03NUL\x10\x00\x12\x07\n\x03SOH\x10\x01\x12\x07\n\x03STX\x10\x02\x12\x07\n\x03\x45OT\x10\x04\x12\x07\n\x03\x41\x43K\x10\x06\x12\x07\n\x03NAK\x10\x15\x12\x07\n\x03\x43\x41N\x10\x18\x12\t\n\x05\x43TRLZ\x10\x1a\x42\x61\n\x13\x63om.geeksville.meshB\x0cXmodemProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.xmodem_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\014XmodemProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' _globals['_XMODEM']._serialized_start=58 _globals['_XMODEM']._serialized_end=249 _globals['_XMODEM_CONTROL']._serialized_start=166 _globals['_XMODEM_CONTROL']._serialized_end=249 # @@protoc_insertion_point(module_scope) python-2.3.14/meshtastic/protobuf/xmodem_pb2.pyi000066400000000000000000000043171464266072200217230ustar00rootroot00000000000000""" @generated by mypy-protobuf. Do not edit manually! isort:skip_file """ import builtins import google.protobuf.descriptor import google.protobuf.internal.enum_type_wrapper import google.protobuf.message import sys import typing if sys.version_info >= (3, 10): import typing as typing_extensions else: import typing_extensions DESCRIPTOR: google.protobuf.descriptor.FileDescriptor @typing.final class XModem(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor class _Control: ValueType = typing.NewType("ValueType", builtins.int) V: typing_extensions.TypeAlias = ValueType class _ControlEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[XModem._Control.ValueType], builtins.type): DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor NUL: XModem._Control.ValueType # 0 SOH: XModem._Control.ValueType # 1 STX: XModem._Control.ValueType # 2 EOT: XModem._Control.ValueType # 4 ACK: XModem._Control.ValueType # 6 NAK: XModem._Control.ValueType # 21 CAN: XModem._Control.ValueType # 24 CTRLZ: XModem._Control.ValueType # 26 class Control(_Control, metaclass=_ControlEnumTypeWrapper): ... NUL: XModem.Control.ValueType # 0 SOH: XModem.Control.ValueType # 1 STX: XModem.Control.ValueType # 2 EOT: XModem.Control.ValueType # 4 ACK: XModem.Control.ValueType # 6 NAK: XModem.Control.ValueType # 21 CAN: XModem.Control.ValueType # 24 CTRLZ: XModem.Control.ValueType # 26 CONTROL_FIELD_NUMBER: builtins.int SEQ_FIELD_NUMBER: builtins.int CRC16_FIELD_NUMBER: builtins.int BUFFER_FIELD_NUMBER: builtins.int control: global___XModem.Control.ValueType seq: builtins.int crc16: builtins.int buffer: builtins.bytes def __init__( self, *, control: global___XModem.Control.ValueType = ..., seq: builtins.int = ..., crc16: builtins.int = ..., buffer: builtins.bytes = ..., ) -> None: ... def ClearField(self, field_name: typing.Literal["buffer", b"buffer", "control", b"control", "crc16", b"crc16", "seq", b"seq"]) -> None: ... global___XModem = XModem python-2.3.14/meshtastic/remote_hardware.py000066400000000000000000000073031464266072200210240ustar00rootroot00000000000000"""Remote hardware """ import logging from pubsub import pub # type: ignore[import-untyped] from meshtastic.protobuf import portnums_pb2, remote_hardware_pb2 from meshtastic.util import our_exit def onGPIOreceive(packet, interface): """Callback for received GPIO responses""" logging.debug(f"packet:{packet} interface:{interface}") gpioValue = 0 hw = packet["decoded"]["remotehw"] 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}') value = int(gpioValue) & int(interface.mask) print( f'Received RemoteHardware type={hw["type"]}, gpio_value={gpioValue} value={value}' ) interface.gotResponse = True class RemoteHardwareClient: """ This is the client code to control/monitor simple hardware built into the meshtastic devices. It is intended to be both a useful API/service and example code for how you can connect to your own custom meshtastic services """ def __init__(self, iface): """ Constructor iface is the already open MeshInterface instance """ self.iface = iface ch = iface.localNode.getChannelByName("gpio") if not ch: our_exit( "Warning: No channel named 'gpio' was found.\n" "On the sending and receive nodes create a channel named 'gpio'.\n" "For example, run '--ch-add gpio' on one device, then '--seturl' on\n" "the other devices using the url from the device where the channel was added." ) self.channelIndex = ch.index pub.subscribe(onGPIOreceive, "meshtastic.receive.remotehw") def _sendHardware(self, nodeid, r, wantResponse=False, onResponse=None): if not nodeid: our_exit( r"Warning: Must use a destination node ID for this operation (use --dest \!xxxxxxxxx)" ) return self.iface.sendData( r, nodeid, portnums_pb2.REMOTE_HARDWARE_APP, wantAck=True, channelIndex=self.channelIndex, wantResponse=wantResponse, onResponse=onResponse, ) def writeGPIOs(self, nodeid, mask, vals): """ Write the specified vals bits to the device GPIOs. Only bits in mask that are 1 will be changed """ logging.debug(f"writeGPIOs nodeid:{nodeid} mask:{mask} vals:{vals}") r = remote_hardware_pb2.HardwareMessage() r.type = remote_hardware_pb2.HardwareMessage.Type.WRITE_GPIOS r.gpio_mask = mask r.gpio_value = vals return self._sendHardware(nodeid, r) def readGPIOs(self, nodeid, mask, onResponse=None): """Read the specified bits from GPIO inputs on the device""" logging.debug(f"readGPIOs nodeid:{nodeid} mask:{mask}") r = remote_hardware_pb2.HardwareMessage() r.type = remote_hardware_pb2.HardwareMessage.Type.READ_GPIOS r.gpio_mask = mask return self._sendHardware(nodeid, r, wantResponse=True, onResponse=onResponse) def watchGPIOs(self, nodeid, mask): """Watch the specified bits from GPIO inputs on the device for changes""" logging.debug(f"watchGPIOs nodeid:{nodeid} mask:{mask}") r = remote_hardware_pb2.HardwareMessage() r.type = remote_hardware_pb2.HardwareMessage.Type.WATCH_GPIOS r.gpio_mask = mask self.iface.mask = mask return self._sendHardware(nodeid, r) python-2.3.14/meshtastic/serial_interface.py000066400000000000000000000052571464266072200211610ustar00rootroot00000000000000""" Serial interface class """ import logging import platform import time from typing import Optional import serial # type: ignore[import-untyped] import meshtastic.util from meshtastic.stream_interface import StreamInterface if platform.system() != "Windows": import termios class SerialInterface(StreamInterface): """Interface class for meshtastic devices over a serial link""" def __init__(self, devPath: Optional[str]=None, debugOut=None, noProto=False, connectNow=True, noNodes: bool=False): """Constructor, opens a connection to a specified serial port, or if unspecified try to find one Meshtastic device by probing Keyword Arguments: devPath {string} -- A filepath to a device, i.e. /dev/ttyUSB0 (default: {None}) debugOut {stream} -- If a stream is provided, any debug serial output from the device will be emitted to that stream. (default: {None}) """ self.noProto = noProto self.devPath: Optional[str] = devPath if self.devPath is None: ports = meshtastic.util.findPorts(True) logging.debug(f"ports:{ports}") if len(ports) == 0: print("No Serial Meshtastic device detected, attempting TCP connection on localhost.") return elif len(ports) > 1: message = "Warning: Multiple serial ports were detected so one serial port must be specified with the '--port'.\n" message += f" Ports detected:{ports}" meshtastic.util.our_exit(message) else: self.devPath = ports[0] 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(self.devPath, encoding="utf8") as f: attrs = termios.tcgetattr(f) attrs[2] = attrs[2] & ~termios.HUPCL termios.tcsetattr(f, termios.TCSAFLUSH, attrs) f.close() time.sleep(0.1) self.stream = serial.Serial( self.devPath, 115200, exclusive=True, timeout=0.5, write_timeout=0 ) self.stream.flush() time.sleep(0.1) StreamInterface.__init__( self, debugOut=debugOut, noProto=noProto, connectNow=connectNow, noNodes=noNodes ) def close(self): """Close a connection to the device""" self.stream.flush() time.sleep(0.1) self.stream.flush() time.sleep(0.1) logging.debug("Closing Serial stream") StreamInterface.close(self) python-2.3.14/meshtastic/stream_interface.py000066400000000000000000000173321464266072200211720ustar00rootroot00000000000000"""Stream Interface base class """ import logging import threading import time import traceback import serial # type: ignore[import-untyped] from meshtastic.mesh_interface import MeshInterface from meshtastic.util import is_windows11, stripnl START1 = 0x94 START2 = 0xC3 HEADER_LEN = 4 MAX_TO_FROM_RADIO_SIZE = 512 class StreamInterface(MeshInterface): """Interface class for meshtastic devices over a stream link (serial, TCP, etc)""" def __init__(self, debugOut=None, noProto=False, connectNow=True, noNodes=False): """Constructor, opens a connection to self.stream Keyword Arguments: debugOut {stream} -- If a stream is provided, any debug serial output from the device will be emitted to that stream. (default: {None}) Raises: Exception: [description] Exception: [description] """ if not hasattr(self, "stream") and not noProto: raise Exception( # pylint: disable=W0719 "StreamInterface is now abstract (to update existing code create SerialInterface instead)" ) 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) MeshInterface.__init__(self, debugOut=debugOut, noProto=noProto, noNodes=noNodes) # Start the reader thread after superclass constructor completes init if connectNow: self.connect() if not noProto: self.waitForConfig() def connect(self): """Connect to our radio Normally this is called automatically by the constructor, but if you passed in connectNow=False you can manually start the reading thread later. """ # Send some bogus UART characters to force a sleeping device to wake, and # if the reading statemachine was parsing a bad packet make sure # we write enough start bytes to force it to resync (we don't use START1 # because we want to ensure it is looking for START1) p = bytearray([START2] * 32) self._writeBytes(p) time.sleep(0.1) # wait 100ms to give device time to start running self._rxThread.start() self._startConfig() if not self.noProto: # Wait for the db download if using the protocol self._waitConnected() def _disconnected(self): """We override the superclass implementation to close our port""" MeshInterface._disconnected(self) logging.debug("Closing our port") # pylint: disable=E0203 if not self.stream is None: # pylint: disable=E0203 self.stream.close() # pylint: disable=W0201 self.stream = None def _writeBytes(self, b): """Write an array of bytes to our stream and flush""" if self.stream: # ignore writes when stream is closed self.stream.write(b) self.stream.flush() # 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""" if self.stream: return self.stream.read(length) else: return None def _sendToRadioImpl(self, toRadio): """Send a ToRadio protobuf to the device""" logging.debug(f"Sending: {stripnl(toRadio)}") b = toRadio.SerializeToString() bufLen = len(b) # We convert into a string, because the TCP code doesn't work with byte arrays header = bytes([START1, START2, (bufLen >> 8) & 0xFF, bufLen & 0xFF]) logging.debug(f"sending header:{header} b:{b}") self._writeBytes(header + b) def close(self): """Close a connection to the device""" logging.debug("Closing stream") MeshInterface.close(self) # pyserial cancel_read doesn't seem to work, therefore we ask the # reader thread to close things for us self._wantExit = True if self._rxThread != threading.current_thread(): self._rxThread.join() # wait for it to exit def __reader(self): """The reader thread that reads bytes from our stream""" logging.debug("in __reader()") empty = bytes() try: while not self._wantExit: # logging.debug("reading character") b = self._readBytes(1) # logging.debug("In reader loop") # logging.debug(f"read returned {b}") if len(b) > 0: c = b[0] # logging.debug(f'c:{c}') ptr = len(self._rxBuf) # Assume we want to append this byte, fixme use bytearray instead self._rxBuf = self._rxBuf + b if ptr == 0: # looking for START1 if c != START1: self._rxBuf = empty # failed to find start if self.debugOut is not None: try: self.debugOut.write(b.decode("utf-8")) except: self.debugOut.write("?") elif ptr == 1: # looking for START2 if c != START2: self._rxBuf = empty # failed to find start2 elif ptr >= HEADER_LEN - 1: # we've at least got a header # logging.debug('at least we received a header') # big endian length follows header packetlen = (self._rxBuf[2] << 8) + self._rxBuf[3] if ( ptr == HEADER_LEN - 1 ): # we _just_ finished reading the header, validate length if packetlen > MAX_TO_FROM_RADIO_SIZE: self._rxBuf = ( empty # length was out out bounds, restart ) if len(self._rxBuf) != 0 and ptr + 1 >= packetlen + HEADER_LEN: try: self._handleFromRadio(self._rxBuf[HEADER_LEN:]) except Exception as ex: logging.error( f"Error while handling message from radio {ex}" ) traceback.print_exc() self._rxBuf = empty else: # logging.debug(f"timeout") pass except serial.SerialException as ex: if ( not self._wantExit ): # We might intentionally get an exception during shutdown logging.warning( f"Meshtastic serial port disconnected, disconnecting... {ex}" ) except OSError as ex: if ( not self._wantExit ): # We might intentionally get an exception during shutdown logging.error( f"Unexpected OSError, terminating meshtastic reader... {ex}" ) except Exception as ex: logging.error( f"Unexpected exception, terminating meshtastic reader... {ex}" ) finally: logging.debug("reader is exiting") self._disconnected() python-2.3.14/meshtastic/supported_device.py000077500000000000000000000137741464266072200212340ustar00rootroot00000000000000""" Supported Meshtastic Devices - This is a class and collection of Meshtastic devices. It is used for auto detection as to which device might be connected. """ # Goal is to detect which device and port to use from the supported devices # without installing any libraries that are not currently in the python meshtastic library class SupportedDevice: """Devices supported on Meshtastic""" def __init__( self, name, version=None, for_firmware=None, device_class="esp32", baseport_on_linux=None, baseport_on_mac=None, baseport_on_windows="COM", usb_vendor_id_in_hex=None, usb_product_id_in_hex=None, ): """constructor""" self.name = name self.version = version self.for_firmware = for_firmware self.device_class = device_class # could be "nrf52" # when you run "lsusb -d xxxx:" in linux self.usb_vendor_id_in_hex = usb_vendor_id_in_hex # store in lower case self.usb_product_id_in_hex = usb_product_id_in_hex # store in lower case self.baseport_on_linux = baseport_on_linux # ex: ttyUSB or ttyACM self.baseport_on_mac = baseport_on_mac self.baseport_on_windows = baseport_on_windows # supported devices tbeam_v0_7 = SupportedDevice( name="T-Beam", version="0.7", for_firmware="tbeam0.7", baseport_on_linux="ttyACM", baseport_on_mac="cu.usbmodem", usb_vendor_id_in_hex="1a86", usb_product_id_in_hex="55d4", ) tbeam_v1_1 = SupportedDevice( name="T-Beam", version="1.1", for_firmware="tbeam", baseport_on_linux="ttyACM", baseport_on_mac="cu.usbmodem", usb_vendor_id_in_hex="1a86", usb_product_id_in_hex="55d4", ) tbeam_M8N = SupportedDevice( name="T-Beam", version="M8N", for_firmware="tbeam", baseport_on_linux="ttyACM", baseport_on_mac="cu.usbmodem", usb_vendor_id_in_hex="1a86", usb_product_id_in_hex="55d4", ) tbeam_M8N_SX1262 = SupportedDevice( name="T-Beam", version="M8N_SX1262", for_firmware="tbeam", baseport_on_linux="ttyACM", baseport_on_mac="cu.usbmodem", usb_vendor_id_in_hex="1a86", usb_product_id_in_hex="55d4", ) tlora_v1 = SupportedDevice( name="T-Lora", version="1", for_firmware="tlora-v1", baseport_on_linux="ttyUSB", baseport_on_mac="cu.usbserial", usb_vendor_id_in_hex="1a86", usb_product_id_in_hex="55d4", ) tlora_v1_3 = SupportedDevice( name="T-Lora", version="1.3", for_firmware="tlora-v1-3", baseport_on_linux="ttyUSB", baseport_on_mac="cu.usbserial", usb_vendor_id_in_hex="10c4", usb_product_id_in_hex="ea60", ) tlora_v2 = SupportedDevice( name="T-Lora", version="2", for_firmware="tlora-v2", baseport_on_linux="ttyACM", baseport_on_mac="cu.usbmodem", usb_vendor_id_in_hex="1a86", usb_product_id_in_hex="55d4", ) tlora_v2_1_1_6 = SupportedDevice( name="T-Lora", version="2.1-1.6", for_firmware="tlora-v2-1-1.6", baseport_on_linux="ttyACM", baseport_on_mac="cu.usbmodem", usb_vendor_id_in_hex="1a86", usb_product_id_in_hex="55d4", ) heltec_v1 = SupportedDevice( name="Heltec", version="1", for_firmware="heltec-v1", baseport_on_linux="ttyUSB", baseport_on_mac="cu.usbserial-", usb_vendor_id_in_hex="10c4", usb_product_id_in_hex="ea60", ) heltec_v2_0 = SupportedDevice( name="Heltec", version="2.0", for_firmware="heltec-v2.0", baseport_on_linux="ttyUSB", baseport_on_mac="cu.usbserial-", usb_vendor_id_in_hex="10c4", usb_product_id_in_hex="ea60", ) heltec_v2_1 = SupportedDevice( name="Heltec", version="2.1", for_firmware="heltec-v2.1", baseport_on_linux="ttyUSB", baseport_on_mac="cu.usbserial-", usb_vendor_id_in_hex="10c4", usb_product_id_in_hex="ea60", ) rak11200 = SupportedDevice( name="RAK 11200", version="", for_firmware="rak11200", baseport_on_linux="ttyUSB", baseport_on_mac="cu.usbserial-", usb_vendor_id_in_hex="1a86", usb_product_id_in_hex="7523", ) meshtastic_diy_v1 = SupportedDevice( name="Meshtastic DIY", version="1", for_firmware="meshtastic-diy-v1", baseport_on_linux="ttyUSB", baseport_on_mac="cu.usbserial-", usb_vendor_id_in_hex="10c4", usb_product_id_in_hex="ea60", ) # Note: The T-Echo reports product id in boot mode techo_1 = SupportedDevice( name="T-Echo", version="1", for_firmware="t-echo-1", device_class="nrf52", baseport_on_linux="ttyACM", baseport_on_mac="cu.usbmodem", usb_vendor_id_in_hex="239a", usb_product_id_in_hex="0029", ) rak4631_5005 = SupportedDevice( name="RAK 4631 5005", version="", for_firmware="rak4631_5005", device_class="nrf52", baseport_on_linux="ttyACM", baseport_on_mac="cu.usbmodem", usb_vendor_id_in_hex="239a", usb_product_id_in_hex="0029", ) rak4631_5005_epaper = SupportedDevice( name="RAK 4631 5005 14000 epaper", version="", for_firmware="rak4631_5005_epaper", device_class="nrf52", baseport_on_linux="ttyACM", baseport_on_mac="cu.usbmodem", usb_vendor_id_in_hex="239a", usb_product_id_in_hex="0029", ) # Note: The 19003 reports same product id as 5005 in boot mode rak4631_19003 = SupportedDevice( name="RAK 4631 19003", version="", for_firmware="rak4631_19003", device_class="nrf52", baseport_on_linux="ttyACM", baseport_on_mac="cu.usbmodem", usb_vendor_id_in_hex="239a", usb_product_id_in_hex="8029", ) nano_g1 = SupportedDevice( name="Nano G1", version="", for_firmware="nano-g1", baseport_on_linux="ttyACM", baseport_on_mac="cu.usbmodem", usb_vendor_id_in_hex="1a86", usb_product_id_in_hex="55d4", ) supported_devices = [ tbeam_v0_7, tbeam_v1_1, tbeam_M8N, tbeam_M8N_SX1262, tlora_v1, tlora_v1_3, tlora_v2, tlora_v2_1_1_6, heltec_v1, heltec_v2_0, heltec_v2_1, meshtastic_diy_v1, techo_1, rak4631_5005, rak4631_5005_epaper, rak4631_19003, rak11200, nano_g1, ] python-2.3.14/meshtastic/tcp_interface.py000066400000000000000000000046021464266072200204610ustar00rootroot00000000000000"""TCPInterface class for interfacing with http endpoint """ import logging import socket from typing import Optional from meshtastic.stream_interface import StreamInterface class TCPInterface(StreamInterface): """Interface class for meshtastic devices over a TCP link""" def __init__( self, hostname: str, debugOut=None, noProto=False, connectNow=True, portNumber=4403, noNodes:bool=False, ): """Constructor, opens a connection to a specified IP address/hostname Keyword Arguments: hostname {string} -- Hostname/IP address of the device to connect to """ self.stream = None self.hostname = hostname self.portNumber = portNumber if connectNow: logging.debug(f"Connecting to {hostname}") # type: ignore[str-bytes-safe] server_address = (hostname, portNumber) sock = socket.create_connection(server_address) self.socket: Optional[socket.socket] = sock else: self.socket = None StreamInterface.__init__( self, debugOut=debugOut, noProto=noProto, connectNow=connectNow, noNodes=noNodes ) def _socket_shutdown(self): """Shutdown the socket. Note: Broke out this line so the exception could be unit tested. """ self.socket.shutdown(socket.SHUT_RDWR) def myConnect(self): """Connect to socket""" server_address = (self.hostname, self.portNumber) sock = socket.create_connection(server_address) self.socket = sock def close(self): """Close a connection to the device""" logging.debug("Closing TCP stream") StreamInterface.close(self) # Sometimes the socket read might be blocked in the reader thread. # Therefore we force the shutdown by closing the socket here self._wantExit = True if not self.socket is None: try: self._socket_shutdown() except: pass # Ignore errors in shutdown, because we might have a race with the server self.socket.close() def _writeBytes(self, b): """Write an array of bytes to our stream and flush""" self.socket.send(b) def _readBytes(self, length): """Read an array of bytes from our stream""" return self.socket.recv(length) python-2.3.14/meshtastic/test.py000066400000000000000000000135751464266072200166430ustar00rootroot00000000000000"""With two radios connected serially, send and receive test messages and report back if successful. """ import logging import sys import time import traceback from dotmap import DotMap # type: ignore[import-untyped] from pubsub import pub # type: ignore[import-untyped] import meshtastic.util from meshtastic import BROADCAST_NUM from meshtastic.serial_interface import SerialInterface from meshtastic.tcp_interface import TCPInterface """The interfaces we are using for our tests""" interfaces = None """A list of all packets we received while the current test was running""" receivedPackets = None testsRunning = False testNumber = 0 sendingInterface = None def onReceive(packet, interface): """Callback invoked when a packet arrives""" if sendingInterface == interface: pass # print("Ignoring sending interface") else: # print(f"From {interface.stream.port}: {packet}") p = DotMap(packet) if p.decoded.portnum == "TEXT_MESSAGE_APP": # We only care a about clear text packets if receivedPackets is not None: receivedPackets.append(p) def onNode(node): """Callback invoked when the node DB changes""" print(f"Node changed: {node}") def subscribe(): """Subscribe to the topics the user probably wants to see, prints output to stdout""" pub.subscribe(onNode, "meshtastic.node") def testSend( fromInterface, toInterface, isBroadcast=False, asBinary=False, wantAck=False ): """ Sends one test packet between two nodes and then returns success or failure Arguments: fromInterface {[type]} -- [description] toInterface {[type]} -- [description] Returns: boolean -- True for success """ # pylint: disable=W0603 global receivedPackets receivedPackets = [] fromNode = fromInterface.myInfo.my_node_num if isBroadcast: toNode = BROADCAST_NUM else: toNode = toInterface.myInfo.my_node_num logging.debug(f"Sending test wantAck={wantAck} packet from {fromNode} to {toNode}") # pylint: disable=W0603 global sendingInterface sendingInterface = fromInterface if not asBinary: fromInterface.sendText(f"Test {testNumber}", toNode, wantAck=wantAck) else: fromInterface.sendData( (f"Binary {testNumber}").encode("utf-8"), toNode, wantAck=wantAck ) for _ in range(60): # max of 60 secs before we timeout time.sleep(1) if len(receivedPackets) >= 1: return True return False # Failed to send def runTests(numTests=50, wantAck=False, maxFailures=0): """Run the tests.""" logging.info(f"Running {numTests} tests with wantAck={wantAck}") numFail = 0 numSuccess = 0 for _ in range(numTests): # pylint: disable=W0603 global testNumber testNumber = testNumber + 1 isBroadcast = True # asBinary=(i % 2 == 0) success = testSend( interfaces[0], interfaces[1], isBroadcast, asBinary=False, wantAck=wantAck ) if not success: numFail = numFail + 1 logging.error( f"Test {testNumber} failed, expected packet not received ({numFail} failures so far)" ) else: numSuccess = numSuccess + 1 logging.info( f"Test {testNumber} succeeded {numSuccess} successes {numFail} failures so far" ) time.sleep(1) if numFail > maxFailures: logging.error("Too many failures! Test failed!") return False return True def testThread(numTests=50): """Test thread""" logging.info("Found devices, starting tests...") result = runTests(numTests, wantAck=True) if result: # Run another test # Allow a few dropped packets result = runTests(numTests, wantAck=False, maxFailures=1) return result def onConnection(topic=pub.AUTO_TOPIC): """Callback invoked when we connect/disconnect from a radio""" print(f"Connection changed: {topic.getName()}") def openDebugLog(portName): """Open the debug log file""" debugname = "log" + portName.replace("/", "_") logging.info(f"Writing serial debugging to {debugname}") return open(debugname, "w+", buffering=1, encoding="utf8") def testAll(numTests=5): """ Run a series of tests using devices we can find. This is called from the cli with the "--test" option. """ ports = meshtastic.util.findPorts(True) if len(ports) < 2: meshtastic.util.our_exit( "Warning: Must have at least two devices connected to USB." ) pub.subscribe(onConnection, "meshtastic.connection") pub.subscribe(onReceive, "meshtastic.receive") # pylint: disable=W0603 global interfaces interfaces = list( map( lambda port: SerialInterface( port, debugOut=openDebugLog(port), connectNow=True ), ports, ) ) logging.info("Ports opened, starting test") result = testThread(numTests) for i in interfaces: i.close() return result def testSimulator(): """ Assume that someone has launched meshtastic-native as a simulated node. Talk to that node over TCP, do some operations and if they are successful exit the process with a success code, else exit with a non zero exit code. Run with python3 -c 'from meshtastic.test import testSimulator; testSimulator()' """ logging.basicConfig(level=logging.DEBUG) logging.info("Connecting to simulator on localhost!") try: iface = TCPInterface("localhost") iface.showInfo() iface.localNode.showInfo() iface.localNode.exitSimulator() iface.close() logging.info("Integration test successful!") except: print("Error while testing simulator:", sys.exc_info()[0]) traceback.print_exc() sys.exit(1) sys.exit(0) python-2.3.14/meshtastic/tests/000077500000000000000000000000001464266072200164415ustar00rootroot00000000000000python-2.3.14/meshtastic/tests/__init__.py000066400000000000000000000000001464266072200205400ustar00rootroot00000000000000python-2.3.14/meshtastic/tests/conftest.py000066400000000000000000000026731464266072200206500ustar00rootroot00000000000000"""Common pytest code (place for fixtures).""" import argparse from unittest.mock import MagicMock import pytest from meshtastic import mt_config from ..mesh_interface import MeshInterface @pytest.fixture def reset_mt_config(): """Fixture to reset mt_config.""" parser = None parser = argparse.ArgumentParser(add_help=False) mt_config.reset() mt_config.parser = parser @pytest.fixture def iface_with_nodes(): """Fixture to setup some nodes.""" nodesById = { "!9388f81c": { "num": 2475227164, "user": { "id": "!9388f81c", "longName": "Unknown f81c", "shortName": "?1C", "macaddr": "RBeTiPgc", "hwModel": "TBEAM", }, "position": {}, "lastHeard": 1640204888, } } nodesByNum = { 2475227164: { "num": 2475227164, "user": { "id": "!9388f81c", "longName": "Unknown f81c", "shortName": "?1C", "macaddr": "RBeTiPgc", "hwModel": "TBEAM", }, "position": {"time": 1640206266}, "lastHeard": 1640206266, } } iface = MeshInterface(noProto=True) iface.nodes = nodesById iface.nodesByNum = nodesByNum myInfo = MagicMock() iface.myInfo = myInfo iface.myInfo.my_node_num = 2475227164 return iface python-2.3.14/meshtastic/tests/test_examples.py000066400000000000000000000017271464266072200216770ustar00rootroot00000000000000"""Meshtastic test that the examples run as expected. We assume you have a python virtual environment in current directory. If not, you need to run: "python3 -m venv venv", "source venv/bin/activate", "pip install ." """ import subprocess import pytest @pytest.mark.examples def test_examples_hello_world_serial_no_arg(): """Test hello_world_serial without any args""" return_value, _ = subprocess.getstatusoutput( "source venv/bin/activate; python3 examples/hello_world_serial.py" ) assert return_value == 3 @pytest.mark.examples def test_examples_hello_world_serial_with_arg(capsys): """Test hello_world_serial with arg""" return_value, _ = subprocess.getstatusoutput( "source venv/bin/activate; python3 examples/hello_world_serial.py hello" ) assert return_value == 1 _, err = capsys.readouterr() assert err == "" # TODO: Why does this not work? # assert out == 'Warning: No Meshtastic devices detected.' python-2.3.14/meshtastic/tests/test_init.py000066400000000000000000000031401464266072200210130ustar00rootroot00000000000000"""Meshtastic unit tests for __init__.py""" import logging import re from unittest.mock import MagicMock import pytest from meshtastic import _onNodeInfoReceive, _onPositionReceive, _onTextReceive, mt_config from ..serial_interface import SerialInterface @pytest.mark.unit def test_init_onTextReceive_with_exception(caplog): """Test _onTextReceive""" args = MagicMock() mt_config.args = args iface = MagicMock(autospec=SerialInterface) packet = {} with caplog.at_level(logging.DEBUG): _onTextReceive(iface, packet) assert re.search(r"in _onTextReceive", caplog.text, re.MULTILINE) assert re.search(r"Malformatted", caplog.text, re.MULTILINE) @pytest.mark.unit def test_init_onPositionReceive(caplog): """Test _onPositionReceive""" args = MagicMock() mt_config.args = args iface = MagicMock(autospec=SerialInterface) packet = {"from": "foo", "decoded": {"position": {}}} with caplog.at_level(logging.DEBUG): _onPositionReceive(iface, packet) assert re.search(r"in _onPositionReceive", caplog.text, re.MULTILINE) @pytest.mark.unit def test_init_onNodeInfoReceive(caplog, iface_with_nodes): """Test _onNodeInfoReceive""" args = MagicMock() mt_config.args = args iface = iface_with_nodes iface.myInfo.my_node_num = 2475227164 packet = { "from": 4808675309, "decoded": { "user": { "id": "bar", }, }, } with caplog.at_level(logging.DEBUG): _onNodeInfoReceive(iface, packet) assert re.search(r"in _onNodeInfoReceive", caplog.text, re.MULTILINE) python-2.3.14/meshtastic/tests/test_int.py000066400000000000000000000023371464266072200206510ustar00rootroot00000000000000"""Meshtastic integration tests""" import re import subprocess import pytest @pytest.mark.int def test_int_meshtastic_no_args(): """Test meshtastic without any args""" return_value, out = subprocess.getstatusoutput("meshtastic") assert re.match(r"usage: meshtastic", out) assert return_value == 1 @pytest.mark.int def test_int_mesh_tunnel_no_args(): """Test mesh-tunnel without any args""" return_value, out = subprocess.getstatusoutput("mesh-tunnel") assert re.match(r"usage: mesh-tunnel", out) assert return_value == 1 @pytest.mark.int def test_int_version(): """Test '--version'.""" return_value, out = subprocess.getstatusoutput("meshtastic --version") assert re.match(r"[0-9]+\.[0-9]+\.[0-9]", out) assert return_value == 0 @pytest.mark.int def test_int_help(): """Test '--help'.""" return_value, out = subprocess.getstatusoutput("meshtastic --help") assert re.match(r"usage: meshtastic ", out) assert return_value == 0 @pytest.mark.int def test_int_support(): """Test '--support'.""" return_value, out = subprocess.getstatusoutput("meshtastic --support") assert re.search(r"System", out) assert re.search(r"Python", out) assert return_value == 0 python-2.3.14/meshtastic/tests/test_main.py000066400000000000000000002705771464266072200210200ustar00rootroot00000000000000"""Meshtastic unit tests for __main__.py""" # pylint: disable=C0302,W0613 import logging import os import platform import re import sys from unittest.mock import mock_open, MagicMock, patch import pytest from meshtastic.__main__ import ( export_config, initParser, main, onConnection, onNode, onReceive, tunnelMain, ) from meshtastic import mt_config from ..protobuf.channel_pb2 import Channel # pylint: disable=E0611 # from ..ble_interface import BLEInterface from ..node import Node # from ..radioconfig_pb2 import UserPreferences # import meshtastic.config_pb2 from ..serial_interface import SerialInterface from ..tcp_interface import TCPInterface # from ..remote_hardware import onGPIOreceive # from ..config_pb2 import Config @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_init_parser_no_args(capsys): """Test no arguments""" sys.argv = [""] mt_config.args = sys.argv initParser() out, err = capsys.readouterr() assert out == "" assert err == "" @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_init_parser_version(capsys): """Test --version""" sys.argv = ["", "--version"] mt_config.args = sys.argv with pytest.raises(SystemExit) as pytest_wrapped_e: initParser() assert pytest_wrapped_e.type == SystemExit assert pytest_wrapped_e.value.code == 0 out, err = capsys.readouterr() assert re.match(r"[0-9]+\.[0-9]+[\.a][0-9]", out) assert err == "" @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_main_version(capsys): """Test --version""" sys.argv = ["", "--version"] mt_config.args = sys.argv with pytest.raises(SystemExit) as pytest_wrapped_e: main() assert pytest_wrapped_e.type == SystemExit assert pytest_wrapped_e.value.code == 0 out, err = capsys.readouterr() assert re.match(r"[0-9]+\.[0-9]+[\.a][0-9]", out) assert err == "" @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_main_no_args(capsys): """Test with no args""" sys.argv = [""] mt_config.args = sys.argv with pytest.raises(SystemExit) as pytest_wrapped_e: main() assert pytest_wrapped_e.type == SystemExit assert pytest_wrapped_e.value.code == 1 _, err = capsys.readouterr() assert re.search(r"usage:", err, re.MULTILINE) @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_support(capsys): """Test --support""" sys.argv = ["", "--support"] mt_config.args = sys.argv with pytest.raises(SystemExit) as pytest_wrapped_e: main() assert pytest_wrapped_e.type == SystemExit assert pytest_wrapped_e.value.code == 0 out, err = capsys.readouterr() assert re.search(r"System", out, re.MULTILINE) assert re.search(r"Platform", out, re.MULTILINE) assert re.search(r"Machine", out, re.MULTILINE) assert re.search(r"Executable", out, re.MULTILINE) assert err == "" @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") @patch("meshtastic.util.findPorts", return_value=[]) def test_main_ch_index_no_devices(patched_find_ports, capsys): """Test --ch-index 1""" sys.argv = ["", "--ch-index", "1"] mt_config.args = sys.argv with pytest.raises(SystemExit) as pytest_wrapped_e: main() assert mt_config.channel_index == 1 assert pytest_wrapped_e.type == SystemExit assert pytest_wrapped_e.value.code == 1 out, err = capsys.readouterr() assert re.search(r"No.*Meshtastic.*device.*detected", out, re.MULTILINE) assert err == "" patched_find_ports.assert_called() @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") @patch("meshtastic.util.findPorts", return_value=[]) def test_main_test_no_ports(patched_find_ports, capsys): """Test --test with no hardware""" sys.argv = ["", "--test"] mt_config.args = sys.argv with pytest.raises(SystemExit) as pytest_wrapped_e: main() assert pytest_wrapped_e.type == SystemExit assert pytest_wrapped_e.value.code == 1 patched_find_ports.assert_called() out, err = capsys.readouterr() assert re.search( r"Warning: Must have at least two devices connected to USB", out, re.MULTILINE ) assert err == "" @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") @patch("meshtastic.util.findPorts", return_value=["/dev/ttyFake1"]) def test_main_test_one_port(patched_find_ports, capsys): """Test --test with one fake port""" sys.argv = ["", "--test"] mt_config.args = sys.argv with pytest.raises(SystemExit) as pytest_wrapped_e: main() assert pytest_wrapped_e.type == SystemExit assert pytest_wrapped_e.value.code == 1 patched_find_ports.assert_called() out, err = capsys.readouterr() assert re.search( r"Warning: Must have at least two devices connected to USB", out, re.MULTILINE ) assert err == "" @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") @patch("meshtastic.test.testAll", return_value=True) def test_main_test_two_ports_success(patched_test_all, capsys): """Test --test two fake ports and testAll() is a simulated success""" sys.argv = ["", "--test"] mt_config.args = sys.argv with pytest.raises(SystemExit) as pytest_wrapped_e: main() assert pytest_wrapped_e.type == SystemExit assert pytest_wrapped_e.value.code == 0 patched_test_all.assert_called() out, err = capsys.readouterr() assert re.search(r"Test was a success.", out, re.MULTILINE) assert err == "" @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") @patch("meshtastic.test.testAll", return_value=False) def test_main_test_two_ports_fails(patched_test_all, capsys): """Test --test two fake ports and testAll() is a simulated failure""" sys.argv = ["", "--test"] mt_config.args = sys.argv with pytest.raises(SystemExit) as pytest_wrapped_e: main() assert pytest_wrapped_e.type == SystemExit assert pytest_wrapped_e.value.code == 1 patched_test_all.assert_called() out, err = capsys.readouterr() assert re.search(r"Test was not successful.", out, re.MULTILINE) assert err == "" @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_info(capsys, caplog): """Test --info""" sys.argv = ["", "--info"] mt_config.args = sys.argv iface = MagicMock(autospec=SerialInterface) def mock_showInfo(): print("inside mocked showInfo") iface.showInfo.side_effect = mock_showInfo with caplog.at_level(logging.DEBUG): 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"inside mocked showInfo", out, re.MULTILINE) assert err == "" mo.assert_called() @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") @patch("os.getlogin") def test_main_info_with_permission_error(patched_getlogin, capsys, caplog): """Test --info""" sys.argv = ["", "--info"] mt_config.args = sys.argv patched_getlogin.return_value = "me" iface = MagicMock(autospec=SerialInterface) with caplog.at_level(logging.DEBUG): with pytest.raises(SystemExit) as pytest_wrapped_e: with patch( "meshtastic.serial_interface.SerialInterface", return_value=iface ) as mo: mo.side_effect = PermissionError("bla bla") main() assert pytest_wrapped_e.type == SystemExit assert pytest_wrapped_e.value.code == 1 out, err = capsys.readouterr() patched_getlogin.assert_called() assert re.search(r"Need to add yourself", out, re.MULTILINE) assert err == "" @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_info_with_tcp_interface(capsys): """Test --info""" sys.argv = ["", "--info", "--host", "meshtastic.local"] mt_config.args = sys.argv iface = MagicMock(autospec=TCPInterface) def mock_showInfo(): print("inside mocked showInfo") iface.showInfo.side_effect = mock_showInfo with patch("meshtastic.tcp_interface.TCPInterface", return_value=iface) as mo: main() out, err = capsys.readouterr() assert re.search(r"Connected to radio", out, re.MULTILINE) assert re.search(r"inside mocked showInfo", out, re.MULTILINE) assert err == "" mo.assert_called() @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_no_proto(capsys): """Test --noproto (using --info for output)""" sys.argv = ["", "--info", "--noproto"] mt_config.args = sys.argv iface = MagicMock(autospec=SerialInterface) def mock_showInfo(): print("inside mocked showInfo") iface.showInfo.side_effect = mock_showInfo # Override the time.sleep so there is no loop def my_sleep(amount): print(f"amount:{amount}") sys.exit(0) with patch("meshtastic.serial_interface.SerialInterface", return_value=iface): with patch("time.sleep", side_effect=my_sleep): with pytest.raises(SystemExit) as pytest_wrapped_e: main() assert pytest_wrapped_e.type == SystemExit assert pytest_wrapped_e.value.code == 0 out, err = capsys.readouterr() assert re.search(r"Connected to radio", out, re.MULTILINE) assert re.search(r"inside mocked showInfo", out, re.MULTILINE) assert err == "" @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_info_with_seriallog_stdout(capsys): """Test --info""" sys.argv = ["", "--info", "--seriallog", "stdout"] mt_config.args = sys.argv iface = MagicMock(autospec=SerialInterface) def mock_showInfo(): print("inside mocked showInfo") iface.showInfo.side_effect = mock_showInfo 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"inside mocked showInfo", out, re.MULTILINE) assert err == "" mo.assert_called() @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_info_with_seriallog_output_txt(capsys): """Test --info""" sys.argv = ["", "--info", "--seriallog", "output.txt"] mt_config.args = sys.argv iface = MagicMock(autospec=SerialInterface) def mock_showInfo(): print("inside mocked showInfo") iface.showInfo.side_effect = mock_showInfo 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"inside mocked showInfo", out, re.MULTILINE) assert err == "" mo.assert_called() # do some cleanup os.remove("output.txt") @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_qr(capsys): """Test --qr""" sys.argv = ["", "--qr"] mt_config.args = sys.argv iface = MagicMock(autospec=SerialInterface) # TODO: could mock/check url 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"Primary channel URL", out, re.MULTILINE) # if a qr code is generated it will have lots of these assert re.search(r"\[7m", out, re.MULTILINE) assert err == "" mo.assert_called() @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_onConnected_exception(capsys): """Test the exception in onConnected""" sys.argv = ["", "--qr"] mt_config.args = sys.argv def throw_an_exception(junk): raise Exception("Fake exception.") # pylint: disable=W0719 iface = MagicMock(autospec=SerialInterface) with patch("meshtastic.serial_interface.SerialInterface", return_value=iface): with patch("pyqrcode.create", side_effect=throw_an_exception): with pytest.raises(SystemExit) as pytest_wrapped_e: main() out, err = capsys.readouterr() assert re.search("Aborting due to: Fake exception", out, re.MULTILINE) assert err == "" assert pytest_wrapped_e.type == Exception @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_nodes(capsys): """Test --nodes""" sys.argv = ["", "--nodes"] mt_config.args = sys.argv iface = MagicMock(autospec=SerialInterface) def mock_showNodes(): print("inside mocked showNodes") iface.showNodes.side_effect = mock_showNodes 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"inside mocked showNodes", out, re.MULTILINE) assert err == "" mo.assert_called() @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_set_owner_to_bob(capsys): """Test --set-owner bob""" sys.argv = ["", "--set-owner", "bob"] mt_config.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 to bob", out, re.MULTILINE) assert err == "" mo.assert_called() @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_set_owner_short_to_bob(capsys): """Test --set-owner-short bob""" sys.argv = ["", "--set-owner-short", "bob"] mt_config.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.usefixtures("reset_mt_config") def test_main_set_canned_messages(capsys): """Test --set-canned-message""" sys.argv = ["", "--set-canned-message", "foo"] mt_config.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 canned plugin message to foo", out, re.MULTILINE) assert err == "" mo.assert_called() @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_get_canned_messages(capsys, caplog, iface_with_nodes): """Test --get-canned-message""" sys.argv = ["", "--get-canned-message"] mt_config.args = sys.argv iface = iface_with_nodes iface.localNode.cannedPluginMessage = "foo" iface.devPath = "bar" with caplog.at_level(logging.DEBUG): 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"canned_plugin_message:foo", out, re.MULTILINE) assert err == "" mo.assert_called() @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_set_ham_to_KI123(capsys): """Test --set-ham KI123""" sys.argv = ["", "--set-ham", "KI123"] mt_config.args = sys.argv mocked_node = MagicMock(autospec=Node) def mock_turnOffEncryptionOnPrimaryChannel(): print("inside mocked turnOffEncryptionOnPrimaryChannel") def mock_setOwner(name, is_licensed): print(f"inside mocked setOwner name:{name} is_licensed:{is_licensed}") mocked_node.turnOffEncryptionOnPrimaryChannel.side_effect = ( mock_turnOffEncryptionOnPrimaryChannel ) mocked_node.setOwner.side_effect = mock_setOwner iface = MagicMock(autospec=SerialInterface) iface.getNode.return_value = mocked_node 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 Ham ID to KI123", out, re.MULTILINE) assert re.search(r"inside mocked setOwner", out, re.MULTILINE) assert re.search( r"inside mocked turnOffEncryptionOnPrimaryChannel", out, re.MULTILINE ) assert err == "" mo.assert_called() @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_reboot(capsys): """Test --reboot""" sys.argv = ["", "--reboot"] mt_config.args = sys.argv mocked_node = MagicMock(autospec=Node) def mock_reboot(): print("inside mocked reboot") mocked_node.reboot.side_effect = mock_reboot iface = MagicMock(autospec=SerialInterface) iface.getNode.return_value = mocked_node 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"inside mocked reboot", out, re.MULTILINE) assert err == "" mo.assert_called() @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_shutdown(capsys): """Test --shutdown""" sys.argv = ["", "--shutdown"] mt_config.args = sys.argv mocked_node = MagicMock(autospec=Node) def mock_shutdown(): print("inside mocked shutdown") mocked_node.shutdown.side_effect = mock_shutdown iface = MagicMock(autospec=SerialInterface) iface.getNode.return_value = mocked_node 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"inside mocked shutdown", out, re.MULTILINE) assert err == "" mo.assert_called() @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_sendtext(capsys): """Test --sendtext""" sys.argv = ["", "--sendtext", "hello"] mt_config.args = sys.argv iface = MagicMock(autospec=SerialInterface) def mock_sendText( text, dest, wantAck=False, wantResponse=False, onResponse=None, channelIndex=0 ): print("inside mocked sendText") print(f"{text} {dest} {wantAck} {wantResponse} {channelIndex}") iface.sendText.side_effect = mock_sendText 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"Sending text message", out, re.MULTILINE) assert re.search(r"inside mocked sendText", out, re.MULTILINE) assert err == "" mo.assert_called() @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_sendtext_with_channel(capsys): """Test --sendtext""" sys.argv = ["", "--sendtext", "hello", "--ch-index", "1"] mt_config.args = sys.argv iface = MagicMock(autospec=SerialInterface) def mock_sendText( text, dest, wantAck=False, wantResponse=False, onResponse=None, channelIndex=0 ): print("inside mocked sendText") print(f"{text} {dest} {wantAck} {wantResponse} {channelIndex}") iface.sendText.side_effect = mock_sendText 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"Sending text message", out, re.MULTILINE) assert re.search(r"on channelIndex:1", out, re.MULTILINE) assert re.search(r"inside mocked sendText", out, re.MULTILINE) assert err == "" mo.assert_called() @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_sendtext_with_invalid_channel(caplog, capsys): """Test --sendtext""" sys.argv = ["", "--sendtext", "hello", "--ch-index", "-1"] mt_config.args = sys.argv iface = MagicMock(autospec=SerialInterface) iface.localNode.getChannelByChannelIndex.return_value = None with caplog.at_level(logging.DEBUG): with patch( "meshtastic.serial_interface.SerialInterface", return_value=iface ) as mo: with pytest.raises(SystemExit) as pytest_wrapped_e: main() assert pytest_wrapped_e.type == SystemExit assert pytest_wrapped_e.value.code == 1 out, err = capsys.readouterr() assert re.search(r"is not a valid channel", out, re.MULTILINE) assert err == "" mo.assert_called() @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_sendtext_with_invalid_channel_nine(caplog, capsys): """Test --sendtext""" sys.argv = ["", "--sendtext", "hello", "--ch-index", "9"] mt_config.args = sys.argv iface = MagicMock(autospec=SerialInterface) iface.localNode.getChannelByChannelIndex.return_value = None with caplog.at_level(logging.DEBUG): with patch( "meshtastic.serial_interface.SerialInterface", return_value=iface ) as mo: with pytest.raises(SystemExit) as pytest_wrapped_e: main() assert pytest_wrapped_e.type == SystemExit assert pytest_wrapped_e.value.code == 1 out, err = capsys.readouterr() assert re.search(r"is not a valid channel", out, re.MULTILINE) assert err == "" mo.assert_called() @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") @patch("termios.tcsetattr") @patch("termios.tcgetattr") @patch("builtins.open", new_callable=mock_open, read_data="data") @patch("serial.Serial") @patch("meshtastic.util.findPorts", return_value=["/dev/ttyUSBfake"]) def test_main_sendtext_with_dest(mock_findPorts, mock_serial, mocked_open, mock_get, mock_set, capsys, caplog, iface_with_nodes): """Test --sendtext with --dest""" sys.argv = ["", "--sendtext", "hello", "--dest", "foo"] mt_config.args = sys.argv #iface = iface_with_nodes #iface.myInfo.my_node_num = 2475227164 serialInterface = SerialInterface(noProto=True) mocked_channel = MagicMock(autospec=Channel) serialInterface.localNode.getChannelByChannelIndex = mocked_channel with patch("meshtastic.serial_interface.SerialInterface", return_value=serialInterface): with caplog.at_level(logging.DEBUG): #with pytest.raises(SystemExit) as pytest_wrapped_e: main() #assert pytest_wrapped_e.type == SystemExit #assert pytest_wrapped_e.value.code == 1 out, err = capsys.readouterr() assert re.search(r"Connected to radio", out, re.MULTILINE) assert not re.search( r"Warning: 0 is not a valid channel", out, re.MULTILINE ) assert not re.search( r"There is a SECONDARY channel named 'admin'", out, re.MULTILINE ) print(out) assert re.search(r"Not sending packet because", caplog.text, re.MULTILINE) assert re.search(r"Warning: There were no self.nodes.", caplog.text, re.MULTILINE) assert err == "" @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_removeposition_invalid(capsys): """Test --remove-position with an invalid dest""" sys.argv = ["", "--remove-position", "--dest", "!12345678"] mt_config.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"remote nodes is not supported", out, re.MULTILINE) assert err == "" mo.assert_called() @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_setlat_invalid(capsys): """Test --setlat with an invalid dest""" sys.argv = ["", "--setlat", "37.5", "--dest", "!12345678"] mt_config.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"remote nodes is not supported", out, re.MULTILINE) assert err == "" mo.assert_called() @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_removeposition(capsys): """Test --remove-position""" sys.argv = ["", "--remove-position"] mt_config.args = sys.argv mocked_node = MagicMock(autospec=Node) def mock_removeFixedPosition(): print("inside mocked removeFixedPosition") mocked_node.removeFixedPosition.side_effect = mock_removeFixedPosition iface = MagicMock(autospec=SerialInterface) iface.localNode = mocked_node 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"Removing fixed position", out, re.MULTILINE) assert re.search(r"inside mocked removeFixedPosition", out, re.MULTILINE) assert err == "" mo.assert_called() @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_setlat(capsys): """Test --setlat""" sys.argv = ["", "--setlat", "37.5"] mt_config.args = sys.argv mocked_node = MagicMock(autospec=Node) def mock_setFixedPosition(lat, lon, alt): print("inside mocked setFixedPosition") print(f"{lat} {lon} {alt}") mocked_node.setFixedPosition.side_effect = mock_setFixedPosition iface = MagicMock(autospec=SerialInterface) iface.localNode = mocked_node 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"Fixing latitude", out, re.MULTILINE) assert re.search(r"Setting device position", out, re.MULTILINE) assert re.search(r"inside mocked setFixedPosition", out, re.MULTILINE) assert err == "" mo.assert_called() @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_setlon(capsys): """Test --setlon""" sys.argv = ["", "--setlon", "-122.1"] mt_config.args = sys.argv mocked_node = MagicMock(autospec=Node) def mock_setFixedPosition(lat, lon, alt): print("inside mocked setFixedPosition") print(f"{lat} {lon} {alt}") mocked_node.setFixedPosition.side_effect = mock_setFixedPosition iface = MagicMock(autospec=SerialInterface) iface.localNode = mocked_node 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"Fixing longitude", out, re.MULTILINE) assert re.search(r"Setting device position", out, re.MULTILINE) assert re.search(r"inside mocked setFixedPosition", out, re.MULTILINE) assert err == "" mo.assert_called() @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_setalt(capsys): """Test --setalt""" sys.argv = ["", "--setalt", "51"] mt_config.args = sys.argv mocked_node = MagicMock(autospec=Node) def mock_setFixedPosition(lat, lon, alt): print("inside mocked setFixedPosition") print(f"{lat} {lon} {alt}") mocked_node.setFixedPosition.side_effect = mock_setFixedPosition iface = MagicMock(autospec=SerialInterface) iface.localNode = mocked_node 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"Fixing altitude", out, re.MULTILINE) assert re.search(r"Setting device position", out, re.MULTILINE) assert re.search(r"inside mocked setFixedPosition", out, re.MULTILINE) assert err == "" mo.assert_called() @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_seturl(capsys): """Test --seturl (url used below is what is generated after a factory_reset)""" sys.argv = ["", "--seturl", "https://www.meshtastic.org/d/#CgUYAyIBAQ"] mt_config.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 err == "" mo.assert_called() @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") @patch("termios.tcsetattr") @patch("termios.tcgetattr") @patch("builtins.open", new_callable=mock_open, read_data="data") @patch("serial.Serial") @patch("meshtastic.util.findPorts", return_value=["/dev/ttyUSBfake"]) def test_main_set_valid(mocked_findports, mocked_serial, mocked_open, mocked_get, mocked_set, capsys): """Test --set with valid field""" sys.argv = ["", "--set", "network.wifi_ssid", "foo"] mt_config.args = sys.argv serialInterface = SerialInterface(noProto=True) anode = Node(serialInterface, 1234567890, noProto=True) serialInterface.localNode = anode with patch("meshtastic.serial_interface.SerialInterface", return_value=serialInterface) as mo: main() out, err = capsys.readouterr() assert re.search(r"Connected to radio", out, re.MULTILINE) assert re.search(r"Set network.wifi_ssid to foo", out, re.MULTILINE) assert err == "" mo.assert_called() @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") @patch("termios.tcsetattr") @patch("termios.tcgetattr") @patch("builtins.open", new_callable=mock_open, read_data="data") @patch("serial.Serial") @patch("meshtastic.util.findPorts", return_value=["/dev/ttyUSBfake"]) def test_main_set_valid_wifi_psk(mocked_findports, mocked_serial, mocked_open, mocked_get, mocked_set, capsys): """Test --set with valid field""" sys.argv = ["", "--set", "network.wifi_psk", "123456789"] mt_config.args = sys.argv serialInterface = SerialInterface(noProto=True) anode = Node(serialInterface, 1234567890, noProto=True) serialInterface.localNode = anode with patch("meshtastic.serial_interface.SerialInterface", return_value=serialInterface) as mo: main() out, err = capsys.readouterr() assert re.search(r"Connected to radio", out, re.MULTILINE) assert re.search(r"Set network.wifi_psk to 123456789", out, re.MULTILINE) assert err == "" mo.assert_called() @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") @patch("termios.tcsetattr") @patch("termios.tcgetattr") @patch("builtins.open", new_callable=mock_open, read_data="data") @patch("serial.Serial") @patch("meshtastic.util.findPorts", return_value=["/dev/ttyUSBfake"]) def test_main_set_invalid_wifi_psk(mocked_findports, mocked_serial, mocked_open, mocked_get, mocked_set, capsys): """Test --set with an invalid value (psk must be 8 or more characters)""" sys.argv = ["", "--set", "network.wifi_psk", "1234567"] mt_config.args = sys.argv serialInterface = SerialInterface(noProto=True) anode = Node(serialInterface, 1234567890, noProto=True) serialInterface.localNode = anode with patch("meshtastic.serial_interface.SerialInterface", return_value=serialInterface) as mo: main() out, err = capsys.readouterr() assert re.search(r"Connected to radio", out, re.MULTILINE) assert not re.search(r"Set network.wifi_psk to 1234567", out, re.MULTILINE) assert re.search( r"Warning: network.wifi_psk must be 8 or more characters.", out, re.MULTILINE ) assert err == "" mo.assert_called() @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") @patch("termios.tcsetattr") @patch("termios.tcgetattr") @patch("builtins.open", new_callable=mock_open, read_data="data") @patch("serial.Serial") @patch("meshtastic.util.findPorts", return_value=["/dev/ttyUSBfake"]) def test_main_set_valid_camel_case(mocked_findports, mocked_serial, mocked_open, mocked_get, mocked_set, capsys): """Test --set with valid field""" sys.argv = ["", "--set", "network.wifi_ssid", "foo"] mt_config.args = sys.argv mt_config.camel_case = True serialInterface = SerialInterface(noProto=True) anode = Node(serialInterface, 1234567890, noProto=True) serialInterface.localNode = anode with patch("meshtastic.serial_interface.SerialInterface", return_value=serialInterface) as mo: main() out, err = capsys.readouterr() assert re.search(r"Connected to radio", out, re.MULTILINE) assert re.search(r"Set network.wifiSsid to foo", out, re.MULTILINE) assert err == "" mo.assert_called() @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") @patch("termios.tcsetattr") @patch("termios.tcgetattr") @patch("builtins.open", new_callable=mock_open, read_data="data") @patch("serial.Serial") @patch("meshtastic.util.findPorts", return_value=["/dev/ttyUSBfake"]) def test_main_set_with_invalid(mocked_findports, mocked_serial, mocked_open, mocked_get, mocked_set, capsys): """Test --set with invalid field""" sys.argv = ["", "--set", "foo", "foo"] mt_config.args = sys.argv serialInterface = SerialInterface(noProto=True) anode = Node(serialInterface, 1234567890, noProto=True) serialInterface.localNode = anode with patch("meshtastic.serial_interface.SerialInterface", return_value=serialInterface) as mo: main() out, err = capsys.readouterr() assert re.search(r"Connected to radio", out, re.MULTILINE) assert re.search(r"do not have attribute foo", out, re.MULTILINE) assert err == "" mo.assert_called() # TODO: write some negative --configure tests @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") @patch("termios.tcsetattr") @patch("termios.tcgetattr") @patch("builtins.open", new_callable=mock_open, read_data="data") @patch("serial.Serial") @patch("meshtastic.util.findPorts", return_value=["/dev/ttyUSBfake"]) def test_main_configure_with_snake_case(mocked_findports, mocked_serial, mocked_open, mocked_get, mocked_set, capsys): """Test --configure with valid file""" sys.argv = ["", "--configure", "example_config.yaml"] mt_config.args = sys.argv serialInterface = SerialInterface(noProto=True) anode = Node(serialInterface, 1234567890, noProto=True) serialInterface.localNode = anode with patch("meshtastic.serial_interface.SerialInterface", return_value=serialInterface) as mo: main() out, err = capsys.readouterr() assert re.search(r"Connected to radio", out, re.MULTILINE) # should these come back? maybe a flag? #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"Fixing altitude", out, re.MULTILINE) #assert re.search(r"Fixing latitude", out, re.MULTILINE) #assert re.search(r"Fixing longitude", out, re.MULTILINE) #assert re.search(r"Set location_share to LocEnabled", out, re.MULTILINE) assert re.search(r"Writing modified configuration to device", out, re.MULTILINE) assert err == "" mo.assert_called() @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") @patch("termios.tcsetattr") @patch("termios.tcgetattr") @patch("builtins.open", new_callable=mock_open, read_data="data") @patch("serial.Serial") @patch("meshtastic.util.findPorts", return_value=["/dev/ttyUSBfake"]) def test_main_configure_with_camel_case_keys(mocked_findports, mocked_serial, mocked_open, mocked_get, mocked_set, capsys): """Test --configure with valid file""" sys.argv = ["", "--configure", "exampleConfig.yaml"] mt_config.args = sys.argv serialInterface = SerialInterface(noProto=True) anode = Node(serialInterface, 1234567890, noProto=True) serialInterface.localNode = anode with patch("meshtastic.serial_interface.SerialInterface", return_value=serialInterface) as mo: main() out, err = capsys.readouterr() assert re.search(r"Connected to radio", out, re.MULTILINE) # should these come back? maybe a flag? #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"Fixing altitude", out, re.MULTILINE) #assert re.search(r"Fixing latitude", out, re.MULTILINE) #assert re.search(r"Fixing longitude", out, re.MULTILINE) assert re.search(r"Writing modified configuration to device", out, re.MULTILINE) assert err == "" mo.assert_called() @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_ch_add_valid(capsys): """Test --ch-add with valid channel name, and that channel name does not already exist""" sys.argv = ["", "--ch-add", "testing"] mt_config.args = sys.argv mocked_channel = MagicMock(autospec=Channel) # TODO: figure out how to get it to print the channel name instead of MagicMock mocked_node = MagicMock(autospec=Node) # set it up so we do not already have a channel named this mocked_node.getChannelByName.return_value = False # set it up so we have free channels mocked_node.getDisabledChannel.return_value = mocked_channel iface = MagicMock(autospec=SerialInterface) iface.getNode.return_value = mocked_node 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"Writing modified channels to device", out, re.MULTILINE) assert err == "" mo.assert_called() @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_ch_add_invalid_name_too_long(capsys): """Test --ch-add with invalid channel name, name too long""" sys.argv = ["", "--ch-add", "testingtestingtesting"] mt_config.args = sys.argv mocked_channel = MagicMock(autospec=Channel) # TODO: figure out how to get it to print the channel name instead of MagicMock mocked_node = MagicMock(autospec=Node) # set it up so we do not already have a channel named this mocked_node.getChannelByName.return_value = False # set it up so we have free channels mocked_node.getDisabledChannel.return_value = mocked_channel iface = MagicMock(autospec=SerialInterface) iface.getNode.return_value = mocked_node with patch("meshtastic.serial_interface.SerialInterface", return_value=iface) as mo: with pytest.raises(SystemExit) as pytest_wrapped_e: main() assert pytest_wrapped_e.type == SystemExit assert pytest_wrapped_e.value.code == 1 out, err = capsys.readouterr() assert re.search(r"Connected to radio", out, re.MULTILINE) assert re.search(r"Warning: Channel name must be shorter", out, re.MULTILINE) assert err == "" mo.assert_called() @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_ch_add_but_name_already_exists(capsys): """Test --ch-add with a channel name that already exists""" sys.argv = ["", "--ch-add", "testing"] mt_config.args = sys.argv mocked_node = MagicMock(autospec=Node) # set it up so we do not already have a channel named this mocked_node.getChannelByName.return_value = True iface = MagicMock(autospec=SerialInterface) iface.getNode.return_value = mocked_node with patch("meshtastic.serial_interface.SerialInterface", return_value=iface) as mo: with pytest.raises(SystemExit) as pytest_wrapped_e: main() assert pytest_wrapped_e.type == SystemExit assert pytest_wrapped_e.value.code == 1 out, err = capsys.readouterr() assert re.search(r"Connected to radio", out, re.MULTILINE) assert re.search(r"Warning: This node already has", out, re.MULTILINE) assert err == "" mo.assert_called() @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_ch_add_but_no_more_channels(capsys): """Test --ch-add with but there are no more channels""" sys.argv = ["", "--ch-add", "testing"] mt_config.args = sys.argv mocked_node = MagicMock(autospec=Node) # set it up so we do not already have a channel named this mocked_node.getChannelByName.return_value = False # set it up so we have free channels mocked_node.getDisabledChannel.return_value = None iface = MagicMock(autospec=SerialInterface) iface.getNode.return_value = mocked_node with patch("meshtastic.serial_interface.SerialInterface", return_value=iface) as mo: with pytest.raises(SystemExit) as pytest_wrapped_e: main() assert pytest_wrapped_e.type == SystemExit assert pytest_wrapped_e.value.code == 1 out, err = capsys.readouterr() assert re.search(r"Connected to radio", out, re.MULTILINE) assert re.search(r"Warning: No free channels were found", out, re.MULTILINE) assert err == "" mo.assert_called() @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_ch_del(capsys): """Test --ch-del with valid secondary channel to be deleted""" sys.argv = ["", "--ch-del", "--ch-index", "1"] mt_config.args = sys.argv mocked_node = MagicMock(autospec=Node) iface = MagicMock(autospec=SerialInterface) iface.getNode.return_value = mocked_node 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"Deleting channel", out, re.MULTILINE) assert err == "" mo.assert_called() @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_ch_del_no_ch_index_specified(capsys): """Test --ch-del without a valid ch-index""" sys.argv = ["", "--ch-del"] mt_config.args = sys.argv mocked_node = MagicMock(autospec=Node) iface = MagicMock(autospec=SerialInterface) iface.getNode.return_value = mocked_node with patch("meshtastic.serial_interface.SerialInterface", return_value=iface) as mo: with pytest.raises(SystemExit) as pytest_wrapped_e: main() assert pytest_wrapped_e.type == SystemExit assert pytest_wrapped_e.value.code == 1 out, err = capsys.readouterr() assert re.search(r"Connected to radio", out, re.MULTILINE) assert re.search(r"Warning: Need to specify", out, re.MULTILINE) assert err == "" mo.assert_called() @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_ch_del_primary_channel(capsys): """Test --ch-del on ch-index=0""" sys.argv = ["", "--ch-del", "--ch-index", "0"] mt_config.args = sys.argv mt_config.channel_index = 1 mocked_node = MagicMock(autospec=Node) iface = MagicMock(autospec=SerialInterface) iface.getNode.return_value = mocked_node with patch("meshtastic.serial_interface.SerialInterface", return_value=iface) as mo: with pytest.raises(SystemExit) as pytest_wrapped_e: main() assert pytest_wrapped_e.type == SystemExit assert pytest_wrapped_e.value.code == 1 out, err = capsys.readouterr() assert re.search(r"Connected to radio", out, re.MULTILINE) assert re.search(r"Warning: Cannot delete primary channel", out, re.MULTILINE) assert err == "" mo.assert_called() @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_ch_enable_valid_secondary_channel(capsys): """Test --ch-enable with --ch-index""" sys.argv = ["", "--ch-enable", "--ch-index", "1"] mt_config.args = sys.argv mocked_node = MagicMock(autospec=Node) iface = MagicMock(autospec=SerialInterface) iface.getNode.return_value = mocked_node 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"Writing modified channels", out, re.MULTILINE) assert err == "" assert mt_config.channel_index == 1 mo.assert_called() @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_ch_disable_valid_secondary_channel(capsys): """Test --ch-disable with --ch-index""" sys.argv = ["", "--ch-disable", "--ch-index", "1"] mt_config.args = sys.argv mocked_node = MagicMock(autospec=Node) iface = MagicMock(autospec=SerialInterface) iface.getNode.return_value = mocked_node 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"Writing modified channels", out, re.MULTILINE) assert err == "" assert mt_config.channel_index == 1 mo.assert_called() @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_ch_enable_without_a_ch_index(capsys): """Test --ch-enable without --ch-index""" sys.argv = ["", "--ch-enable"] mt_config.args = sys.argv mocked_node = MagicMock(autospec=Node) iface = MagicMock(autospec=SerialInterface) iface.getNode.return_value = mocked_node with patch("meshtastic.serial_interface.SerialInterface", return_value=iface) as mo: with pytest.raises(SystemExit) as pytest_wrapped_e: main() assert pytest_wrapped_e.type == SystemExit assert pytest_wrapped_e.value.code == 1 out, err = capsys.readouterr() assert re.search(r"Connected to radio", out, re.MULTILINE) assert re.search(r"Warning: Need to specify", out, re.MULTILINE) assert err == "" assert mt_config.channel_index is None mo.assert_called() @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_ch_enable_primary_channel(capsys): """Test --ch-enable with --ch-index = 0""" sys.argv = ["", "--ch-enable", "--ch-index", "0"] mt_config.args = sys.argv mocked_node = MagicMock(autospec=Node) iface = MagicMock(autospec=SerialInterface) iface.getNode.return_value = mocked_node with patch("meshtastic.serial_interface.SerialInterface", return_value=iface) as mo: with pytest.raises(SystemExit) as pytest_wrapped_e: main() assert pytest_wrapped_e.type == SystemExit assert pytest_wrapped_e.value.code == 1 out, err = capsys.readouterr() assert re.search(r"Connected to radio", out, re.MULTILINE) assert re.search(r"Warning: Cannot enable/disable PRIMARY", out, re.MULTILINE) assert err == "" assert mt_config.channel_index == 0 mo.assert_called() # TODO # @pytest.mark.unit # @pytest.mark.usefixtures("reset_mt_config") # def test_main_ch_range_options(capsys): # """Test changing the various range options.""" # range_options = ['--ch-vlongslow', '--ch-longslow', '--ch-longfast', '--ch-midslow', # '--ch-midfast', '--ch-shortslow', '--ch-shortfast'] # for range_option in range_options: # sys.argv = ['', f"{range_option}" ] # mt_config.args = sys.argv # # mocked_node = MagicMock(autospec=Node) # # iface = MagicMock(autospec=SerialInterface) # iface.getNode.return_value = mocked_node # # 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'Writing modified channels', out, re.MULTILINE) # assert err == '' # mo.assert_called() @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_ch_longfast_on_non_primary_channel(capsys): """Test --ch-longfast --ch-index 1""" sys.argv = ["", "--ch-longfast", "--ch-index", "1"] mt_config.args = sys.argv mocked_node = MagicMock(autospec=Node) iface = MagicMock(autospec=SerialInterface) iface.getNode.return_value = mocked_node with patch("meshtastic.serial_interface.SerialInterface", return_value=iface) as mo: with pytest.raises(SystemExit) as pytest_wrapped_e: main() assert pytest_wrapped_e.type == SystemExit assert pytest_wrapped_e.value.code == 1 out, err = capsys.readouterr() assert re.search(r"Connected to radio", out, re.MULTILINE) assert re.search(r"Warning: Cannot set modem preset for non-primary channel", out, re.MULTILINE) assert err == "" mo.assert_called() # PositionFlags: # Misc info that might be helpful (this info will grow stale, just # a snapshot of the values.) The radioconfig_pb2.PositionFlags.Name and bit values are: # POS_UNDEFINED 0 # POS_ALTITUDE 1 # POS_ALT_MSL 2 # POS_GEO_SEP 4 # POS_DOP 8 # POS_HVDOP 16 # POS_BATTERY 32 # POS_SATINVIEW 64 # POS_SEQ_NOS 128 # POS_TIMESTAMP 256 # TODO # @pytest.mark.unit # @pytest.mark.usefixtures("reset_mt_config") # def test_main_pos_fields_no_args(capsys): # """Test --pos-fields no args (which shows settings)""" # sys.argv = ['', '--pos-fields'] # mt_config.args = sys.argv # # pos_flags = MagicMock(autospec=meshtastic.radioconfig_pb2.PositionFlags) # # with patch('meshtastic.serial_interface.SerialInterface') as mo: # mo().getNode().radioConfig.preferences.position_flags = 35 # with patch('meshtastic.radioconfig_pb2.PositionFlags', return_value=pos_flags) as mrc: # # mrc.values.return_value = [0, 1, 2, 4, 8, 16, 32, 64, 128, 256] # # Note: When you use side_effect and a list, each call will use a value from the front of the list then # # remove that value from the list. If there are three values in the list, we expect it to be called # # three times. # mrc.Name.side_effect = ['POS_ALTITUDE', 'POS_ALT_MSL', 'POS_BATTERY'] # # main() # # mrc.Name.assert_called() # mrc.values.assert_called() # mo.assert_called() # # out, err = capsys.readouterr() # assert re.search(r'Connected to radio', out, re.MULTILINE) # assert re.search(r'POS_ALTITUDE POS_ALT_MSL POS_BATTERY', out, re.MULTILINE) # assert err == '' # TODO # @pytest.mark.unit # @pytest.mark.usefixtures("reset_mt_config") # def test_main_pos_fields_arg_of_zero(capsys): # """Test --pos-fields an arg of 0 (which shows list)""" # sys.argv = ['', '--pos-fields', '0'] # mt_config.args = sys.argv # # pos_flags = MagicMock(autospec=meshtastic.radioconfig_pb2.PositionFlags) # # with patch('meshtastic.serial_interface.SerialInterface') as mo: # with patch('meshtastic.radioconfig_pb2.PositionFlags', return_value=pos_flags) as mrc: # # def throw_value_error_exception(exc): # raise ValueError() # mrc.Value.side_effect = throw_value_error_exception # mrc.keys.return_value = [ 'POS_UNDEFINED', 'POS_ALTITUDE', 'POS_ALT_MSL', # 'POS_GEO_SEP', 'POS_DOP', 'POS_HVDOP', 'POS_BATTERY', # 'POS_SATINVIEW', 'POS_SEQ_NOS', 'POS_TIMESTAMP'] # # main() # # mrc.Value.assert_called() # mrc.keys.assert_called() # mo.assert_called() # # out, err = capsys.readouterr() # assert re.search(r'Connected to radio', out, re.MULTILINE) # assert re.search(r'ERROR: supported position fields are:', out, re.MULTILINE) # assert re.search(r"['POS_UNDEFINED', 'POS_ALTITUDE', 'POS_ALT_MSL', 'POS_GEO_SEP',"\ # "'POS_DOP', 'POS_HVDOP', 'POS_BATTERY', 'POS_SATINVIEW', 'POS_SEQ_NOS',"\ # "'POS_TIMESTAMP']", out, re.MULTILINE) # assert err == '' # TODO # @pytest.mark.unit # @pytest.mark.usefixtures("reset_mt_config") # def test_main_pos_fields_valid_values(capsys): # """Test --pos-fields with valid values""" # sys.argv = ['', '--pos-fields', 'POS_GEO_SEP', 'POS_ALT_MSL'] # mt_config.args = sys.argv # # pos_flags = MagicMock(autospec=meshtastic.radioconfig_pb2.PositionFlags) # # with patch('meshtastic.serial_interface.SerialInterface') as mo: # with patch('meshtastic.radioconfig_pb2.PositionFlags', return_value=pos_flags) as mrc: # # mrc.Value.side_effect = [ 4, 2 ] # # main() # # mrc.Value.assert_called() # mo.assert_called() # # out, err = capsys.readouterr() # assert re.search(r'Connected to radio', out, re.MULTILINE) # assert re.search(r'Setting position fields to 6', out, re.MULTILINE) # assert re.search(r'Set position_flags to 6', out, re.MULTILINE) # assert re.search(r'Writing modified preferences to device', out, re.MULTILINE) # assert err == '' # TODO # @pytest.mark.unit # @pytest.mark.usefixtures("reset_mt_config") # def test_main_get_with_valid_values(capsys): # """Test --get with valid values (with string, number, boolean)""" # sys.argv = ['', '--get', 'ls_secs', '--get', 'wifi_ssid', '--get', 'fixed_position'] # mt_config.args = sys.argv # # with patch('meshtastic.serial_interface.SerialInterface') as mo: # # mo().getNode().radioConfig.preferences.wifi_ssid = 'foo' # mo().getNode().radioConfig.preferences.ls_secs = 300 # mo().getNode().radioConfig.preferences.fixed_position = False # # main() # # mo.assert_called() # # out, err = capsys.readouterr() # assert re.search(r'Connected to radio', out, re.MULTILINE) # assert re.search(r'ls_secs: 300', out, re.MULTILINE) # assert re.search(r'wifi_ssid: foo', out, re.MULTILINE) # assert re.search(r'fixed_position: False', out, re.MULTILINE) # assert err == '' # TODO #@pytest.mark.unit #@pytest.mark.usefixtures("reset_mt_config") #def test_main_get_with_valid_values_camel(capsys, caplog): # """Test --get with valid values (with string, number, boolean)""" # sys.argv = ["", "--get", "lsSecs", "--get", "wifiSsid", "--get", "fixedPosition"] # mt_config.args = sys.argv # mt_config.camel_case = True # # with caplog.at_level(logging.DEBUG): # with patch("meshtastic.serial_interface.SerialInterface") as mo: # mo().getNode().radioConfig.preferences.wifi_ssid = "foo" # mo().getNode().radioConfig.preferences.ls_secs = 300 # mo().getNode().radioConfig.preferences.fixed_position = False # # main() # # mo.assert_called() # # out, err = capsys.readouterr() # assert re.search(r"Connected to radio", out, re.MULTILINE) # assert re.search(r"lsSecs: 300", out, re.MULTILINE) # assert re.search(r"wifiSsid: foo", out, re.MULTILINE) # assert re.search(r"fixedPosition: False", out, re.MULTILINE) # assert err == "" @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_get_with_invalid(capsys): """Test --get with invalid field""" sys.argv = ["", "--get", "foo"] mt_config.args = sys.argv mocked_user_prefs = MagicMock() mocked_user_prefs.DESCRIPTOR.fields_by_name.get.return_value = None mocked_node = MagicMock(autospec=Node) mocked_node.localConfig = mocked_user_prefs mocked_node.moduleConfig = mocked_user_prefs iface = MagicMock(autospec=SerialInterface) iface.getNode.return_value = mocked_node 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"do not have attribute foo", out, re.MULTILINE) assert re.search(r"Choices are...", out, re.MULTILINE) assert err == "" mo.assert_called() @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_onReceive_empty(caplog, capsys): """Test onReceive""" args = MagicMock() mt_config.args = args iface = MagicMock(autospec=SerialInterface) packet = {} with caplog.at_level(logging.DEBUG): onReceive(packet, iface) assert re.search(r"in onReceive", caplog.text, re.MULTILINE) out, err = capsys.readouterr() assert re.search( r"Warning: There is no field 'to' in the packet.", out, re.MULTILINE ) assert err == "" # TODO: use this captured position app message (might want/need in the future) # packet = { # 'to': 4294967295, # 'decoded': { # 'portnum': 'POSITION_APP', # 'payload': "M69\306a" # }, # 'id': 334776976, # 'hop_limit': 3 # } @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_onReceive_with_sendtext(caplog, capsys): """Test onReceive with sendtext The entire point of this test is to make sure the interface.close() call is made in onReceive(). """ sys.argv = ["", "--sendtext", "hello"] mt_config.args = sys.argv # Note: 'TEXT_MESSAGE_APP' value is 1 packet = { "to": 4294967295, "decoded": {"portnum": 1, "payload": "hello"}, "id": 334776977, "hop_limit": 3, "want_ack": True, } iface = MagicMock(autospec=SerialInterface) iface.myInfo.my_node_num = 4294967295 with patch("meshtastic.serial_interface.SerialInterface", return_value=iface) as mo: with caplog.at_level(logging.DEBUG): main() onReceive(packet, iface) assert re.search(r"in onReceive", caplog.text, re.MULTILINE) mo.assert_called() out, err = capsys.readouterr() assert re.search(r"Sending text message hello to", out, re.MULTILINE) assert err == "" @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_onReceive_with_text(caplog, capsys): """Test onReceive with text""" args = MagicMock() args.sendtext.return_value = "foo" mt_config.args = args # Note: 'TEXT_MESSAGE_APP' value is 1 # Note: Some of this is faked below. packet = { "to": 4294967295, "decoded": {"portnum": 1, "payload": "hello", "text": "faked"}, "id": 334776977, "hop_limit": 3, "want_ack": True, "rxSnr": 6.0, "hopLimit": 3, "raw": "faked", "fromId": "!28b5465c", "toId": "^all", } iface = MagicMock(autospec=SerialInterface) iface.myInfo.my_node_num = 4294967295 with patch("meshtastic.serial_interface.SerialInterface", return_value=iface): with caplog.at_level(logging.DEBUG): onReceive(packet, iface) assert re.search(r"in onReceive", caplog.text, re.MULTILINE) out, err = capsys.readouterr() assert re.search(r"Sending reply", out, re.MULTILINE) assert err == "" @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_onConnection(capsys): """Test onConnection""" sys.argv = [""] mt_config.args = sys.argv iface = MagicMock(autospec=SerialInterface) class TempTopic: """temp class for topic""" def getName(self): """return the fake name of a topic""" return "foo" mytopic = TempTopic() onConnection(iface, mytopic) out, err = capsys.readouterr() assert re.search(r"Connection changed: foo", out, re.MULTILINE) assert err == "" @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_export_config(capsys): """Test export_config() function directly""" iface = MagicMock(autospec=SerialInterface) with patch("meshtastic.serial_interface.SerialInterface", return_value=iface) as mo: mo.getLongName.return_value = "foo" mo.getShortName.return_value = "oof" mo.localNode.getURL.return_value = "bar" mo.getMyNodeInfo().get.return_value = { "latitudeI": 1100000000, "longitudeI": 1200000000, "altitude": 100, "batteryLevel": 34, "latitude": 110.0, "longitude": 120.0, } mo.localNode.radioConfig.preferences = """phone_timeout_secs: 900 ls_secs: 300 position_broadcast_smart: true fixed_position: true position_flags: 35""" export_config(mo) out, err = capsys.readouterr() # ensure we do not output this line 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_short: oof", 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"lat: 110.0", out, re.MULTILINE) assert re.search(r"lon: 120.0", out, re.MULTILINE) assert re.search(r"alt: 100", out, re.MULTILINE) # TODO: rework above config to test the following #assert re.search(r"user_prefs:", out, re.MULTILINE) #assert re.search(r"phone_timeout_secs: 900", out, re.MULTILINE) #assert re.search(r"ls_secs: 300", out, re.MULTILINE) #assert re.search(r"position_broadcast_smart: 'true'", out, re.MULTILINE) #assert re.search(r"fixed_position: 'true'", out, re.MULTILINE) #assert re.search(r"position_flags: 35", out, re.MULTILINE) assert err == "" # TODO # recursion depth exceeded error #@pytest.mark.unit #@pytest.mark.usefixtures("reset_mt_config") #def test_main_export_config_use_camel(capsys): # """Test export_config() function directly""" # mt_config.camel_case = True # iface = MagicMock(autospec=SerialInterface) # with patch("meshtastic.serial_interface.SerialInterface", return_value=iface) as mo: # mo.getLongName.return_value = "foo" # mo.localNode.getURL.return_value = "bar" # mo.getMyNodeInfo().get.return_value = { # "latitudeI": 1100000000, # "longitudeI": 1200000000, # "altitude": 100, # "batteryLevel": 34, # "latitude": 110.0, # "longitude": 120.0, # } # mo.localNode.radioConfig.preferences = """phone_timeout_secs: 900 #ls_secs: 300 #position_broadcast_smart: true #fixed_position: true #position_flags: 35""" # export_config(mo) # out, err = capsys.readouterr() # # # ensure we do not output this line # assert not re.search(r"Connected to radio", out, re.MULTILINE) # # assert re.search(r"owner: foo", out, re.MULTILINE) # assert re.search(r"channelUrl: bar", 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"lon: 120.0", out, re.MULTILINE) # assert re.search(r"alt: 100", out, re.MULTILINE) # assert re.search(r"userPrefs:", out, re.MULTILINE) # assert re.search(r"phoneTimeoutSecs: 900", out, re.MULTILINE) # assert re.search(r"lsSecs: 300", out, re.MULTILINE) # # TODO: should True be capitalized here? # assert re.search(r"positionBroadcastSmart: 'True'", out, re.MULTILINE) # assert re.search(r"fixedPosition: 'True'", out, re.MULTILINE) # assert re.search(r"positionFlags: 35", out, re.MULTILINE) # assert err == "" # TODO # maximum recursion depth error #@pytest.mark.unit #@pytest.mark.usefixtures("reset_mt_config") #def test_main_export_config_called_from_main(capsys): # """Test --export-config""" # sys.argv = ["", "--export-config"] # mt_config.args = sys.argv # # iface = MagicMock(autospec=SerialInterface) # with patch("meshtastic.serial_interface.SerialInterface", return_value=iface) as mo: # main() # out, err = capsys.readouterr() # assert not re.search(r"Connected to radio", out, re.MULTILINE) # assert re.search(r"# start of Meshtastic configure yaml", out, re.MULTILINE) # assert err == "" # mo.assert_called() @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_gpio_rd_no_gpio_channel(capsys): """Test --gpio_rd with no named gpio channel""" sys.argv = ["", "--gpio-rd", "0x10", "--dest", "!foo"] mt_config.args = sys.argv iface = MagicMock(autospec=SerialInterface) iface.localNode.getChannelByName.return_value = None with patch("meshtastic.serial_interface.SerialInterface", return_value=iface): with pytest.raises(SystemExit) as pytest_wrapped_e: main() assert pytest_wrapped_e.type == SystemExit assert pytest_wrapped_e.value.code == 1 out, err = capsys.readouterr() assert re.search(r"Warning: No channel named", out) assert err == "" @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_gpio_rd_no_dest(capsys): """Test --gpio_rd with a named gpio channel but no dest was specified""" sys.argv = ["", "--gpio-rd", "0x2000"] mt_config.args = sys.argv channel = Channel(index=2, role=2) channel.settings.psk = b"\x8a\x94y\x0e\xc6\xc9\x1e5\x91\x12@\xa60\xa8\xb43\x87\x00\xf2K\x0e\xe7\x7fAz\xcd\xf5\xb0\x900\xa84" channel.settings.name = "gpio" iface = MagicMock(autospec=SerialInterface) iface.localNode.getChannelByName.return_value = channel with patch("meshtastic.serial_interface.SerialInterface", return_value=iface): with pytest.raises(SystemExit) as pytest_wrapped_e: main() assert pytest_wrapped_e.type == SystemExit assert pytest_wrapped_e.value.code == 1 out, err = capsys.readouterr() assert re.search(r"Warning: Must use a destination node ID", out) assert err == "" # TODO # @pytest.mark.unit # @pytest.mark.usefixtures("reset_mt_config") # @patch('time.sleep') # def test_main_gpio_rd(caplog, capsys): # """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 # # red arrow (meaning ok to use for our purposes) # # 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. # # 1. Find the 2^n or 2^13 (8192) # # 2. Convert 8192 decimal to hex (0x2000) # # You can use python: # # >>> print(hex(2**13)) # # 0x2000 # sys.argv = ['', '--gpio-rd', '0x1000', '--dest', '!1234'] # mt_config.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': '4096', # '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'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 == '' # TODO # @pytest.mark.unit # @pytest.mark.usefixtures("reset_mt_config") # @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'] # mt_config.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.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=0', out, re.MULTILINE) # assert err == '' # TODO # @pytest.mark.unit # @pytest.mark.usefixtures("reset_mt_config") # def test_main_gpio_watch(caplog, capsys): # """Test --gpio_watch with a named gpio channel""" # sys.argv = ['', '--gpio-watch', '0x1000', '--dest', '!1234'] # mt_config.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 == '' # TODO # @pytest.mark.unit # @pytest.mark.usefixtures("reset_mt_config") # def test_main_gpio_wrb(caplog, capsys): # """Test --gpio_wrb with a named gpio channel""" # sys.argv = ['', '--gpio-wrb', '4', '1', '--dest', '!1234'] # mt_config.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 == '' # TODO # need to restructure these for nested configs #@pytest.mark.unit #@pytest.mark.usefixtures("reset_mt_config") #def test_main_getPref_valid_field(capsys): # """Test getPref() with a valid field""" # prefs = MagicMock() # prefs.DESCRIPTOR.fields_by_name.get.return_value = "ls_secs" # prefs.wifi_ssid = "foo" # prefs.ls_secs = 300 # prefs.fixed_position = False # # getPref(prefs, "ls_secs") # out, err = capsys.readouterr() # assert re.search(r"ls_secs: 300", out, re.MULTILINE) # assert err == "" # # #@pytest.mark.unit #@pytest.mark.usefixtures("reset_mt_config") #def test_main_getPref_valid_field_camel(capsys): # """Test getPref() with a valid field""" # mt_config.camel_case = True # prefs = MagicMock() # prefs.DESCRIPTOR.fields_by_name.get.return_value = "ls_secs" # prefs.wifi_ssid = "foo" # prefs.ls_secs = 300 # prefs.fixed_position = False # # getPref(prefs, "ls_secs") # out, err = capsys.readouterr() # assert re.search(r"lsSecs: 300", out, re.MULTILINE) # assert err == "" # # #@pytest.mark.unit #@pytest.mark.usefixtures("reset_mt_config") #def test_main_getPref_valid_field_string(capsys): # """Test getPref() with a valid field and value as a string""" # prefs = MagicMock() # prefs.DESCRIPTOR.fields_by_name.get.return_value = "wifi_ssid" # prefs.wifi_ssid = "foo" # prefs.ls_secs = 300 # prefs.fixed_position = False # # getPref(prefs, "wifi_ssid") # out, err = capsys.readouterr() # assert re.search(r"wifi_ssid: foo", out, re.MULTILINE) # assert err == "" # # #@pytest.mark.unit #@pytest.mark.usefixtures("reset_mt_config") #def test_main_getPref_valid_field_string_camel(capsys): # """Test getPref() with a valid field and value as a string""" # mt_config.camel_case = True # prefs = MagicMock() # prefs.DESCRIPTOR.fields_by_name.get.return_value = "wifi_ssid" # prefs.wifi_ssid = "foo" # prefs.ls_secs = 300 # prefs.fixed_position = False # # getPref(prefs, "wifi_ssid") # out, err = capsys.readouterr() # assert re.search(r"wifiSsid: foo", out, re.MULTILINE) # assert err == "" # # #@pytest.mark.unit #@pytest.mark.usefixtures("reset_mt_config") #def test_main_getPref_valid_field_bool(capsys): # """Test getPref() with a valid field and value as a bool""" # prefs = MagicMock() # prefs.DESCRIPTOR.fields_by_name.get.return_value = "fixed_position" # prefs.wifi_ssid = "foo" # prefs.ls_secs = 300 # prefs.fixed_position = False # # getPref(prefs, "fixed_position") # out, err = capsys.readouterr() # assert re.search(r"fixed_position: False", out, re.MULTILINE) # assert err == "" # # #@pytest.mark.unit #@pytest.mark.usefixtures("reset_mt_config") #def test_main_getPref_valid_field_bool_camel(capsys): # """Test getPref() with a valid field and value as a bool""" # mt_config.camel_case = True # prefs = MagicMock() # prefs.DESCRIPTOR.fields_by_name.get.return_value = "fixed_position" # prefs.wifi_ssid = "foo" # prefs.ls_secs = 300 # prefs.fixed_position = False # # getPref(prefs, "fixed_position") # out, err = capsys.readouterr() # assert re.search(r"fixedPosition: False", out, re.MULTILINE) # assert err == "" # # #@pytest.mark.unit #@pytest.mark.usefixtures("reset_mt_config") #def test_main_getPref_invalid_field(capsys): # """Test getPref() with an invalid field""" # # class Field: # """Simple class for testing.""" # # def __init__(self, name): # """constructor""" # self.name = name # # prefs = MagicMock() # prefs.DESCRIPTOR.fields_by_name.get.return_value = None # # # Note: This is a subset of the real fields # ls_secs_field = Field("ls_secs") # is_router = Field("is_router") # fixed_position = Field("fixed_position") # # fields = [ls_secs_field, is_router, fixed_position] # prefs.DESCRIPTOR.fields = fields # # getPref(prefs, "foo") # # out, err = capsys.readouterr() # assert re.search(r"does not have an attribute called foo", out, re.MULTILINE) # # ensure they are sorted # assert re.search(r"fixed_position\s+is_router\s+ls_secs", out, re.MULTILINE) # assert err == "" # # #@pytest.mark.unit #@pytest.mark.usefixtures("reset_mt_config") #def test_main_getPref_invalid_field_camel(capsys): # """Test getPref() with an invalid field""" # mt_config.camel_case = True # # class Field: # """Simple class for testing.""" # # def __init__(self, name): # """constructor""" # self.name = name # # prefs = MagicMock() # prefs.DESCRIPTOR.fields_by_name.get.return_value = None # # # Note: This is a subset of the real fields # ls_secs_field = Field("ls_secs") # is_router = Field("is_router") # fixed_position = Field("fixed_position") # # fields = [ls_secs_field, is_router, fixed_position] # prefs.DESCRIPTOR.fields = fields # # getPref(prefs, "foo") # # out, err = capsys.readouterr() # assert re.search(r"does not have an attribute called foo", out, re.MULTILINE) # # ensure they are sorted # assert re.search(r"fixedPosition\s+isRouter\s+lsSecs", out, re.MULTILINE) # assert err == "" # # #@pytest.mark.unit #@pytest.mark.usefixtures("reset_mt_config") #def test_main_setPref_valid_field_int_as_string(capsys): # """Test setPref() with a valid field""" # # class Field: # """Simple class for testing.""" # # def __init__(self, name, enum_type): # """constructor""" # self.name = name # self.enum_type = enum_type # # ls_secs_field = Field("ls_secs", "int") # prefs = MagicMock() # prefs.DESCRIPTOR.fields_by_name.get.return_value = ls_secs_field # # setPref(prefs, "ls_secs", "300") # out, err = capsys.readouterr() # assert re.search(r"Set ls_secs to 300", out, re.MULTILINE) # assert err == "" # TODO # @pytest.mark.unit # @pytest.mark.usefixtures("reset_mt_config") # def test_main_setPref_valid_field_invalid_enum(capsys, caplog): # """Test setPref() with a valid field but invalid enum value""" # # radioConfig = RadioConfig() # prefs = radioConfig.preferences # # with caplog.at_level(logging.DEBUG): # setPref(prefs, 'charge_current', 'foo') # out, err = capsys.readouterr() # assert re.search(r'charge_current does not have an enum called foo', out, re.MULTILINE) # assert re.search(r'Choices in sorted order are', out, re.MULTILINE) # assert re.search(r'MA100', out, re.MULTILINE) # assert re.search(r'MA280', out, re.MULTILINE) # assert err == '' # TODO # @pytest.mark.unit # @pytest.mark.usefixtures("reset_mt_config") # def test_main_setPref_valid_field_invalid_enum_where_enums_are_camel_cased_values(capsys, caplog): # """Test setPref() with a valid field but invalid enum value""" # # radioConfig = RadioConfig() # prefs = radioConfig.preferences # # with caplog.at_level(logging.DEBUG): # setPref(prefs, 'region', 'foo') # out, err = capsys.readouterr() # assert re.search(r'region does not have an enum called foo', out, re.MULTILINE) # assert re.search(r'Choices in sorted order are', out, re.MULTILINE) # assert re.search(r'ANZ', out, re.MULTILINE) # assert re.search(r'CN', out, re.MULTILINE) # assert err == '' # TODO # @pytest.mark.unit # @pytest.mark.usefixtures("reset_mt_config") # def test_main_setPref_valid_field_invalid_enum_camel(capsys, caplog): # """Test setPref() with a valid field but invalid enum value""" # mt_config.camel_case = True # # radioConfig = RadioConfig() # prefs = radioConfig.preferences # # with caplog.at_level(logging.DEBUG): # setPref(prefs, 'charge_current', 'foo') # out, err = capsys.readouterr() # assert re.search(r'chargeCurrent does not have an enum called foo', out, re.MULTILINE) # assert err == '' # TODO # @pytest.mark.unit # @pytest.mark.usefixtures("reset_mt_config") # def test_main_setPref_valid_field_valid_enum(capsys, caplog): # """Test setPref() with a valid field and valid enum value""" # # # charge_current # # some valid values: MA100 MA1000 MA1080 # # radioConfig = RadioConfig() # prefs = radioConfig.preferences # # with caplog.at_level(logging.DEBUG): # setPref(prefs, 'charge_current', 'MA100') # out, err = capsys.readouterr() # assert re.search(r'Set charge_current to MA100', out, re.MULTILINE) # assert err == '' # TODO # @pytest.mark.unit # @pytest.mark.usefixtures("reset_mt_config") # def test_main_setPref_valid_field_valid_enum_camel(capsys, caplog): # """Test setPref() with a valid field and valid enum value""" # mt_config.camel_case = True # # # charge_current # # some valid values: MA100 MA1000 MA1080 # # radioConfig = RadioConfig() # prefs = radioConfig.preferences # # with caplog.at_level(logging.DEBUG): # setPref(prefs, 'charge_current', 'MA100') # out, err = capsys.readouterr() # assert re.search(r'Set chargeCurrent to MA100', out, re.MULTILINE) # assert err == '' # TODO # need to update for nested configs #@pytest.mark.unit #@pytest.mark.usefixtures("reset_mt_config") #def test_main_setPref_invalid_field(capsys): # """Test setPref() with a invalid field""" # # class Field: # """Simple class for testing.""" # # def __init__(self, name): # """constructor""" # self.name = name # # prefs = MagicMock() # prefs.DESCRIPTOR.fields_by_name.get.return_value = None # # # Note: This is a subset of the real fields # ls_secs_field = Field("ls_secs") # is_router = Field("is_router") # fixed_position = Field("fixed_position") # # fields = [ls_secs_field, is_router, fixed_position] # prefs.DESCRIPTOR.fields = fields # # setPref(prefs, "foo", "300") # out, err = capsys.readouterr() # assert re.search(r"does not have an attribute called foo", out, re.MULTILINE) # # ensure they are sorted # assert re.search(r"fixed_position\s+is_router\s+ls_secs", out, re.MULTILINE) # assert err == "" # # #@pytest.mark.unit #@pytest.mark.usefixtures("reset_mt_config") #def test_main_setPref_invalid_field_camel(capsys): # """Test setPref() with a invalid field""" # mt_config.camel_case = True # # class Field: # """Simple class for testing.""" # # def __init__(self, name): # """constructor""" # self.name = name # # prefs = MagicMock() # prefs.DESCRIPTOR.fields_by_name.get.return_value = None # # # Note: This is a subset of the real fields # ls_secs_field = Field("ls_secs") # is_router = Field("is_router") # fixed_position = Field("fixed_position") # # fields = [ls_secs_field, is_router, fixed_position] # prefs.DESCRIPTOR.fields = fields # # setPref(prefs, "foo", "300") # out, err = capsys.readouterr() # assert re.search(r"does not have an attribute called foo", out, re.MULTILINE) # # ensure they are sorted # assert re.search(r"fixedPosition\s+isRouter\s+lsSecs", out, re.MULTILINE) # assert err == "" # # #@pytest.mark.unit #@pytest.mark.usefixtures("reset_mt_config") #def test_main_setPref_ignore_incoming_123(capsys): # """Test setPref() with ignore_incoming""" # # class Field: # """Simple class for testing.""" # # def __init__(self, name, enum_type): # """constructor""" # self.name = name # self.enum_type = enum_type # # ignore_incoming_field = Field("ignore_incoming", "list") # prefs = MagicMock() # prefs.DESCRIPTOR.fields_by_name.get.return_value = ignore_incoming_field # # setPref(prefs, "ignore_incoming", "123") # out, err = capsys.readouterr() # assert re.search(r"Adding '123' to the ignore_incoming list", out, re.MULTILINE) # assert re.search(r"Set ignore_incoming to 123", out, re.MULTILINE) # assert err == "" # # #@pytest.mark.unit #@pytest.mark.usefixtures("reset_mt_config") #def test_main_setPref_ignore_incoming_0(capsys): # """Test setPref() with ignore_incoming""" # # class Field: # """Simple class for testing.""" # # def __init__(self, name, enum_type): # """constructor""" # self.name = name # self.enum_type = enum_type # # ignore_incoming_field = Field("ignore_incoming", "list") # prefs = MagicMock() # prefs.DESCRIPTOR.fields_by_name.get.return_value = ignore_incoming_field # # setPref(prefs, "ignore_incoming", "0") # out, err = capsys.readouterr() # assert re.search(r"Clearing ignore_incoming list", out, re.MULTILINE) # assert re.search(r"Set ignore_incoming to 0", out, re.MULTILINE) # assert err == "" @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_ch_set_psk_no_ch_index(capsys): """Test --ch-set psk""" sys.argv = ["", "--ch-set", "psk", "foo", "--host", "meshtastic.local"] mt_config.args = sys.argv iface = MagicMock(autospec=TCPInterface) with patch("meshtastic.tcp_interface.TCPInterface", return_value=iface) as mo: with pytest.raises(SystemExit) as pytest_wrapped_e: main() out, err = capsys.readouterr() assert re.search(r"Connected to radio", out, re.MULTILINE) assert re.search(r"Warning: Need to specify '--ch-index'", out, re.MULTILINE) assert err == "" assert pytest_wrapped_e.type == SystemExit assert pytest_wrapped_e.value.code == 1 mo.assert_called() @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_ch_set_psk_with_ch_index(capsys): """Test --ch-set psk""" sys.argv = [ "", "--ch-set", "psk", "foo", "--host", "meshtastic.local", "--ch-index", "0", ] mt_config.args = sys.argv iface = MagicMock(autospec=TCPInterface) with patch("meshtastic.tcp_interface.TCPInterface", return_value=iface) as mo: main() out, err = capsys.readouterr() assert re.search(r"Connected to radio", out, re.MULTILINE) assert re.search(r"Writing modified channels to device", out, re.MULTILINE) assert err == "" mo.assert_called() # TODO # doesn't work properly with nested/module config stuff #@pytest.mark.unit #@pytest.mark.usefixtures("reset_mt_config") #def test_main_ch_set_name_with_ch_index(capsys): # """Test --ch-set setting other than psk""" # sys.argv = [ # "", # "--ch-set", # "name", # "foo", # "--host", # "meshtastic.local", # "--ch-index", # "0", # ] # mt_config.args = sys.argv # # iface = MagicMock(autospec=TCPInterface) # with patch("meshtastic.tcp_interface.TCPInterface", return_value=iface) as mo: # main() # out, err = capsys.readouterr() # assert re.search(r"Connected to radio", out, re.MULTILINE) # assert re.search(r"Set name to foo", out, re.MULTILINE) # assert re.search(r"Writing modified channels to device", out, re.MULTILINE) # assert err == "" # mo.assert_called() @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_onNode(capsys): """Test onNode""" onNode("foo") out, err = capsys.readouterr() assert re.search(r"Node changed", out, re.MULTILINE) assert err == "" @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_tunnel_no_args(capsys): """Test tunnel no arguments""" sys.argv = [""] mt_config.args = sys.argv with pytest.raises(SystemExit) as pytest_wrapped_e: tunnelMain() assert pytest_wrapped_e.type == SystemExit assert pytest_wrapped_e.value.code == 1 _, err = capsys.readouterr() assert re.search(r"usage: ", err, re.MULTILINE) @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") @patch("meshtastic.util.findPorts", return_value=[]) @patch("platform.system") def test_tunnel_tunnel_arg_with_no_devices(mock_platform_system, caplog, capsys): """Test tunnel with tunnel arg (act like we are on a linux system)""" a_mock = MagicMock() a_mock.return_value = "Linux" mock_platform_system.side_effect = a_mock sys.argv = ["", "--tunnel"] mt_config.args = sys.argv print(f"platform.system():{platform.system()}") with caplog.at_level(logging.DEBUG): with pytest.raises(SystemExit) as pytest_wrapped_e: tunnelMain() mock_platform_system.assert_called() assert pytest_wrapped_e.type == SystemExit assert pytest_wrapped_e.value.code == 1 out, err = capsys.readouterr() assert re.search(r"No.*Meshtastic.*device.*detected", out, re.MULTILINE) assert err == "" @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") @patch("meshtastic.util.findPorts", return_value=[]) @patch("platform.system") def test_tunnel_subnet_arg_with_no_devices(mock_platform_system, caplog, capsys): """Test tunnel with subnet arg (act like we are on a linux system)""" a_mock = MagicMock() a_mock.return_value = "Linux" mock_platform_system.side_effect = a_mock sys.argv = ["", "--subnet", "foo"] mt_config.args = sys.argv print(f"platform.system():{platform.system()}") with caplog.at_level(logging.DEBUG): with pytest.raises(SystemExit) as pytest_wrapped_e: tunnelMain() mock_platform_system.assert_called() assert pytest_wrapped_e.type == SystemExit assert pytest_wrapped_e.value.code == 1 out, err = capsys.readouterr() assert re.search(r"No.*Meshtastic.*device.*detected", out, re.MULTILINE) assert err == "" @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") @patch("platform.system") @patch("termios.tcsetattr") @patch("termios.tcgetattr") @patch("builtins.open", new_callable=mock_open, read_data="data") @patch("serial.Serial") @patch("meshtastic.util.findPorts", return_value=["/dev/ttyUSBfake"]) def test_tunnel_tunnel_arg( mocked_findPorts, mocked_serial, mocked_open, mock_get, mock_set, mock_platform_system, caplog, iface_with_nodes, capsys ): """Test tunnel with tunnel arg (act like we are on a linux system)""" # Override the time.sleep so there is no loop def my_sleep(amount): print(f"{amount}") sys.exit(3) a_mock = MagicMock() a_mock.return_value = "Linux" mock_platform_system.side_effect = a_mock sys.argv = ["", "--tunnel"] mt_config.args = sys.argv serialInterface = SerialInterface(noProto=True) with caplog.at_level(logging.DEBUG): with patch("meshtastic.serial_interface.SerialInterface", return_value=serialInterface): with patch("time.sleep", side_effect=my_sleep): with pytest.raises(SystemExit) as pytest_wrapped_e: tunnelMain() mock_platform_system.assert_called() assert pytest_wrapped_e.type == SystemExit assert pytest_wrapped_e.value.code == 3 assert re.search(r"Not starting Tunnel", caplog.text, re.MULTILINE) out, err = capsys.readouterr() assert re.search(r"Connected to radio", out, re.MULTILINE) assert err == "" python-2.3.14/meshtastic/tests/test_mesh_interface.py000066400000000000000000000613431464266072200230350ustar00rootroot00000000000000"""Meshtastic unit tests for mesh_interface.py""" import logging import re from unittest.mock import MagicMock, patch import pytest from hypothesis import given, strategies as st from ..protobuf import mesh_pb2, config_pb2 from .. import BROADCAST_ADDR, LOCAL_ADDR from ..mesh_interface import MeshInterface, _timeago from ..node import Node # TODO # from ..config import Config from ..util import Timeout @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_MeshInterface(capsys): """Test that we can instantiate a MeshInterface""" iface = MeshInterface(noProto=True) NODE_ID = "!9388f81c" NODE_NUM = 2475227164 node = { "num": NODE_NUM, "user": { "id": NODE_ID, "longName": "Unknown f81c", "shortName": "?1C", "macaddr": "RBeTiPgc", "hwModel": "TBEAM", }, "position": {}, "lastHeard": 1640204888, } iface.nodes = {NODE_ID: node} iface.nodesByNum = {NODE_NUM: node} myInfo = MagicMock() iface.myInfo = myInfo iface.localNode.localConfig.lora.CopyFrom(config_pb2.Config.LoRaConfig()) iface.showInfo() iface.localNode.showInfo() iface.showNodes() iface.sendText("hello") iface.close() out, err = capsys.readouterr() assert re.search(r"Owner: None \(None\)", out, re.MULTILINE) assert re.search(r"Nodes", out, re.MULTILINE) assert re.search(r"Preferences", out, re.MULTILINE) assert re.search(r"Channels", out, re.MULTILINE) assert re.search(r"Primary channel URL", out, re.MULTILINE) assert err == "" @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_getMyUser(iface_with_nodes): """Test getMyUser()""" iface = iface_with_nodes iface.myInfo.my_node_num = 2475227164 myuser = iface.getMyUser() assert myuser is not None assert myuser["id"] == "!9388f81c" @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_getLongName(iface_with_nodes): """Test getLongName()""" iface = iface_with_nodes iface.myInfo.my_node_num = 2475227164 mylongname = iface.getLongName() assert mylongname == "Unknown f81c" @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_getShortName(iface_with_nodes): """Test getShortName().""" iface = iface_with_nodes iface.myInfo.my_node_num = 2475227164 myshortname = iface.getShortName() assert myshortname == "?1C" @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_handlePacketFromRadio_no_from(capsys): """Test _handlePacketFromRadio with no 'from' in the mesh packet.""" iface = MeshInterface(noProto=True) meshPacket = mesh_pb2.MeshPacket() iface._handlePacketFromRadio(meshPacket) out, err = capsys.readouterr() assert re.search(r"Device returned a packet we sent, ignoring", out, re.MULTILINE) assert err == "" @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_handlePacketFromRadio_with_a_portnum(caplog): """Test _handlePacketFromRadio with a portnum Since we have an attribute called 'from', we cannot simply 'set' it. Had to implement a hack just to be able to test some code. """ iface = MeshInterface(noProto=True) meshPacket = mesh_pb2.MeshPacket() meshPacket.decoded.payload = b"" meshPacket.decoded.portnum = 1 with caplog.at_level(logging.WARNING): iface._handlePacketFromRadio(meshPacket, hack=True) assert re.search(r"Not populating fromId", caplog.text, re.MULTILINE) @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_handlePacketFromRadio_no_portnum(caplog): """Test _handlePacketFromRadio without a portnum""" iface = MeshInterface(noProto=True) meshPacket = mesh_pb2.MeshPacket() meshPacket.decoded.payload = b"" with caplog.at_level(logging.WARNING): iface._handlePacketFromRadio(meshPacket, hack=True) assert re.search(r"Not populating fromId", caplog.text, re.MULTILINE) @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_getNode_with_local(): """Test getNode""" iface = MeshInterface(noProto=True) anode = iface.getNode(LOCAL_ADDR) assert anode == iface.localNode @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_getNode_not_local(caplog): """Test getNode not local""" iface = MeshInterface(noProto=True) anode = MagicMock(autospec=Node) with caplog.at_level(logging.DEBUG): with patch("meshtastic.node.Node", return_value=anode): another_node = iface.getNode("bar2") assert another_node != iface.localNode assert re.search(r"About to requestChannels", caplog.text, re.MULTILINE) @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_getNode_not_local_timeout(capsys): """Test getNode not local, simulate timeout""" iface = MeshInterface(noProto=True) anode = MagicMock(autospec=Node) anode.waitForConfig.return_value = False with patch("meshtastic.node.Node", return_value=anode): with pytest.raises(SystemExit) as pytest_wrapped_e: iface.getNode("bar2") assert pytest_wrapped_e.type == SystemExit assert pytest_wrapped_e.value.code == 1 out, err = capsys.readouterr() assert re.match(r"Error: Timed out waiting for channels", out) assert err == "" @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_sendPosition(caplog): """Test sendPosition""" iface = MeshInterface(noProto=True) with caplog.at_level(logging.DEBUG): iface.sendPosition() iface.close() assert re.search(r"p.time:", caplog.text, re.MULTILINE) # TODO # @pytest.mark.unit # @pytest.mark.usefixtures("reset_mt_config") # 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() # TODO # @pytest.mark.unit # @pytest.mark.usefixtures("reset_mt_config") # 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 @pytest.mark.usefixtures("reset_mt_config") def test_handleFromRadio_with_my_info(caplog): """Test _handleFromRadio with my_info""" # Note: I captured the '--debug --info' for the bytes below. # It "translates" to this: # my_info { # my_node_num: 682584012 # 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) with caplog.at_level(logging.DEBUG): iface._handleFromRadio(from_radio_bytes) iface.close() assert re.search(r"Received from radio: my_info {", caplog.text, re.MULTILINE) assert re.search(r"my_node_num: 682584012", caplog.text, re.MULTILINE) @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_handleFromRadio_with_node_info(caplog, capsys): """Test _handleFromRadio with node_info""" # Note: I captured the '--debug --info' for the bytes below. # It "translates" to this: # node_info { # num: 682584012 # user { # id: "!28af67cc" # long_name: "Unknown 67cc" # short_name: "?CC" # macaddr: "$o(\257g\314" # hw_model: HELTEC_V2_1 # } # position { # } # } from_radio_bytes = b'"2\x08\xcc\xcf\xbd\xc5\x02\x12(\n\t!28af67cc\x12\x0cUnknown 67cc\x1a\x03?CC"\x06$o(\xafg\xcc0\n\x1a\x00' iface = MeshInterface(noProto=True) with caplog.at_level(logging.DEBUG): iface._startConfig() iface._handleFromRadio(from_radio_bytes) assert re.search(r"Received from radio: node_info {", caplog.text, re.MULTILINE) assert re.search(r"682584012", caplog.text, re.MULTILINE) # validate some of showNodes() output iface.showNodes() out, err = capsys.readouterr() assert re.search(r" 1 ", out, re.MULTILINE) assert re.search(r"│ Unknown 67cc │ ", out, re.MULTILINE) assert re.search(r"│\s+!28af67cc\s+│\s+67cc\s+|", out, re.MULTILINE) assert err == "" iface.close() @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_handleFromRadio_with_node_info_tbeam1(caplog, capsys): """Test _handleFromRadio with node_info""" # Note: Captured the '--debug --info' for the bytes below. # pylint: disable=C0301 from_radio_bytes = b'"=\x08\x80\xf8\xc8\xf6\x07\x12"\n\t!7ed23c00\x12\x07TBeam 1\x1a\x02T1"\x06\x94\xb9~\xd2<\x000\x04\x1a\x07 ]MN\x01\xbea%\xad\x01\xbea=\x00\x00,A' iface = MeshInterface(noProto=True) with caplog.at_level(logging.DEBUG): iface._startConfig() iface._handleFromRadio(from_radio_bytes) assert re.search(r"Received nodeinfo", caplog.text, re.MULTILINE) assert re.search(r"TBeam 1", caplog.text, re.MULTILINE) assert re.search(r"2127707136", caplog.text, re.MULTILINE) # validate some of showNodes() output iface.showNodes() out, err = capsys.readouterr() assert re.search(r" 1 ", out, re.MULTILINE) assert re.search(r"│ TBeam 1 │ ", out, re.MULTILINE) assert re.search(r"│ !7ed23c00 │", out, re.MULTILINE) assert err == "" iface.close() @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_handleFromRadio_with_node_info_tbeam_with_bad_data(caplog): """Test _handleFromRadio with node_info with some bad data (issue#172) - ensure we do not throw exception""" # Note: Captured the '--debug --info' for the bytes below. from_radio_bytes = b'"\x17\x08\xdc\x8a\x8a\xae\x02\x12\x08"\x06\x00\x00\x00\x00\x00\x00\x1a\x00=\x00\x00\xb8@' iface = MeshInterface(noProto=True) with caplog.at_level(logging.DEBUG): iface._startConfig() iface._handleFromRadio(from_radio_bytes) @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_MeshInterface_sendToRadioImpl(caplog): """Test _sendToRadioImp()""" iface = MeshInterface(noProto=True) with caplog.at_level(logging.DEBUG): iface._sendToRadioImpl("foo") assert re.search(r"Subclass must provide toradio", caplog.text, re.MULTILINE) iface.close() @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_MeshInterface_sendToRadio_no_proto(caplog): """Test sendToRadio()""" iface = MeshInterface() with caplog.at_level(logging.DEBUG): iface._sendToRadioImpl("foo") assert re.search(r"Subclass must provide toradio", caplog.text, re.MULTILINE) iface.close() @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_sendData_too_long(caplog): """Test when data payload is too big""" iface = MeshInterface(noProto=True) some_large_text = b"This is a long text that will be too long for send text." some_large_text += b"This is a long text that will be too long for send text." some_large_text += b"This is a long text that will be too long for send text." some_large_text += b"This is a long text that will be too long for send text." some_large_text += b"This is a long text that will be too long for send text." some_large_text += b"This is a long text that will be too long for send text." some_large_text += b"This is a long text that will be too long for send text." some_large_text += b"This is a long text that will be too long for send text." some_large_text += b"This is a long text that will be too long for send text." some_large_text += b"This is a long text that will be too long for send text." some_large_text += b"This is a long text that will be too long for send text." some_large_text += b"This is a long text that will be too long for send text." with caplog.at_level(logging.DEBUG): with pytest.raises(MeshInterface.MeshInterfaceError) as pytest_wrapped_e: iface.sendData(some_large_text) assert re.search("Data payload too big", caplog.text, re.MULTILINE) assert pytest_wrapped_e.type == MeshInterface.MeshInterfaceError iface.close() @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_sendData_unknown_app(capsys): """Test sendData when unknown app""" iface = MeshInterface(noProto=True) with pytest.raises(SystemExit) as pytest_wrapped_e: iface.sendData(b"hello", portNum=0) out, err = capsys.readouterr() assert re.search(r"Warning: A non-zero port number", out, re.MULTILINE) assert err == "" assert pytest_wrapped_e.type == SystemExit assert pytest_wrapped_e.value.code == 1 @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_sendPosition_with_a_position(caplog): """Test sendPosition when lat/long/alt""" iface = MeshInterface(noProto=True) with caplog.at_level(logging.DEBUG): iface.sendPosition(latitude=40.8, longitude=-111.86, altitude=201) assert re.search(r"p.latitude_i:408", caplog.text, re.MULTILINE) assert re.search(r"p.longitude_i:-11186", caplog.text, re.MULTILINE) assert re.search(r"p.altitude:201", caplog.text, re.MULTILINE) @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_sendPacket_with_no_destination(capsys): """Test _sendPacket()""" iface = MeshInterface(noProto=True) with pytest.raises(SystemExit) as pytest_wrapped_e: iface._sendPacket(b"", destinationId=None) out, err = capsys.readouterr() assert re.search(r"Warning: destinationId must not be None", out, re.MULTILINE) assert err == "" assert pytest_wrapped_e.type == SystemExit assert pytest_wrapped_e.value.code == 1 @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_sendPacket_with_destination_as_int(caplog): """Test _sendPacket() with int as a destination""" iface = MeshInterface(noProto=True) with caplog.at_level(logging.DEBUG): meshPacket = mesh_pb2.MeshPacket() iface._sendPacket(meshPacket, destinationId=123) assert re.search(r"Not sending packet", caplog.text, re.MULTILINE) @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_sendPacket_with_destination_starting_with_a_bang(caplog): """Test _sendPacket() with int as a destination""" iface = MeshInterface(noProto=True) with caplog.at_level(logging.DEBUG): meshPacket = mesh_pb2.MeshPacket() iface._sendPacket(meshPacket, destinationId="!1234") assert re.search(r"Not sending packet", caplog.text, re.MULTILINE) @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_sendPacket_with_destination_as_BROADCAST_ADDR(caplog): """Test _sendPacket() with BROADCAST_ADDR as a destination""" iface = MeshInterface(noProto=True) with caplog.at_level(logging.DEBUG): meshPacket = mesh_pb2.MeshPacket() iface._sendPacket(meshPacket, destinationId=BROADCAST_ADDR) assert re.search(r"Not sending packet", caplog.text, re.MULTILINE) @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_sendPacket_with_destination_as_LOCAL_ADDR_no_myInfo(capsys): """Test _sendPacket() with LOCAL_ADDR as a destination with no myInfo""" iface = MeshInterface(noProto=True) with pytest.raises(SystemExit) as pytest_wrapped_e: meshPacket = mesh_pb2.MeshPacket() iface._sendPacket(meshPacket, destinationId=LOCAL_ADDR) out, err = capsys.readouterr() assert re.search(r"Warning: No myInfo", out, re.MULTILINE) assert err == "" assert pytest_wrapped_e.type == SystemExit assert pytest_wrapped_e.value.code == 1 @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_sendPacket_with_destination_as_LOCAL_ADDR_with_myInfo(caplog): """Test _sendPacket() with LOCAL_ADDR as a destination with myInfo""" iface = MeshInterface(noProto=True) myInfo = MagicMock() iface.myInfo = myInfo iface.myInfo.my_node_num = 1 with caplog.at_level(logging.DEBUG): meshPacket = mesh_pb2.MeshPacket() iface._sendPacket(meshPacket, destinationId=LOCAL_ADDR) assert re.search(r"Not sending packet", caplog.text, re.MULTILINE) @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_sendPacket_with_destination_is_blank_with_nodes(capsys, iface_with_nodes): """Test _sendPacket() with '' as a destination with myInfo""" iface = iface_with_nodes meshPacket = mesh_pb2.MeshPacket() with pytest.raises(SystemExit) as pytest_wrapped_e: iface._sendPacket(meshPacket, destinationId="") assert pytest_wrapped_e.type == SystemExit assert pytest_wrapped_e.value.code == 1 out, err = capsys.readouterr() assert re.match(r"Warning: NodeId not found in DB", out, re.MULTILINE) assert err == "" @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_sendPacket_with_destination_is_blank_without_nodes(caplog, iface_with_nodes): """Test _sendPacket() with '' as a destination with myInfo""" iface = iface_with_nodes iface.nodes = None meshPacket = mesh_pb2.MeshPacket() with caplog.at_level(logging.WARNING): iface._sendPacket(meshPacket, destinationId="") assert re.search(r"Warning: There were no self.nodes.", caplog.text, re.MULTILINE) @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_getMyNodeInfo(): """Test getMyNodeInfo()""" iface = MeshInterface(noProto=True) anode = iface.getNode(LOCAL_ADDR) iface.nodesByNum = {1: anode} assert iface.nodesByNum.get(1) == anode myInfo = MagicMock() iface.myInfo = myInfo iface.myInfo.my_node_num = 1 myinfo = iface.getMyNodeInfo() assert myinfo == anode @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_generatePacketId(capsys): """Test _generatePacketId() when no currentPacketId (not connected)""" iface = MeshInterface(noProto=True) # not sure when this condition would ever happen... but we can simulate it iface.currentPacketId = None assert iface.currentPacketId is None with pytest.raises(MeshInterface.MeshInterfaceError) as pytest_wrapped_e: iface._generatePacketId() out, err = capsys.readouterr() assert re.search( r"Not connected yet, can not generate packet", out, re.MULTILINE ) assert err == "" assert pytest_wrapped_e.type == MeshInterface.MeshInterfaceError @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_fixupPosition_empty_pos(): """Test _fixupPosition()""" iface = MeshInterface(noProto=True) pos = {} newpos = iface._fixupPosition(pos) assert newpos == pos @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_fixupPosition_no_changes_needed(): """Test _fixupPosition()""" iface = MeshInterface(noProto=True) pos = {"latitude": 101, "longitude": 102} newpos = iface._fixupPosition(pos) assert newpos == pos @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_fixupPosition(): """Test _fixupPosition()""" iface = MeshInterface(noProto=True) pos = {"latitudeI": 1010000000, "longitudeI": 1020000000} newpos = iface._fixupPosition(pos) assert newpos == { "latitude": 101.0, "latitudeI": 1010000000, "longitude": 102.0, "longitudeI": 1020000000, } @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_nodeNumToId(iface_with_nodes): """Test _nodeNumToId()""" iface = iface_with_nodes iface.myInfo.my_node_num = 2475227164 someid = iface._nodeNumToId(2475227164) assert someid == "!9388f81c" @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_nodeNumToId_not_found(iface_with_nodes): """Test _nodeNumToId()""" iface = iface_with_nodes iface.myInfo.my_node_num = 2475227164 someid = iface._nodeNumToId(123) assert someid is None @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_nodeNumToId_to_all(iface_with_nodes): """Test _nodeNumToId()""" iface = iface_with_nodes iface.myInfo.my_node_num = 2475227164 someid = iface._nodeNumToId(0xFFFFFFFF) assert someid == "^all" @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_getOrCreateByNum_minimal(iface_with_nodes): """Test _getOrCreateByNum()""" iface = iface_with_nodes iface.myInfo.my_node_num = 2475227164 tmp = iface._getOrCreateByNum(123) assert tmp == {"num": 123, "user": {"hwModel": "UNSET", "id": "!0000007b", "shortName": "007b", "longName": "Meshtastic 007b"}} @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_getOrCreateByNum_not_found(iface_with_nodes): """Test _getOrCreateByNum()""" iface = iface_with_nodes iface.myInfo.my_node_num = 2475227164 with pytest.raises(MeshInterface.MeshInterfaceError) as pytest_wrapped_e: iface._getOrCreateByNum(0xFFFFFFFF) assert pytest_wrapped_e.type == MeshInterface.MeshInterfaceError @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_getOrCreateByNum(iface_with_nodes): """Test _getOrCreateByNum()""" iface = iface_with_nodes iface.myInfo.my_node_num = 2475227164 tmp = iface._getOrCreateByNum(2475227164) assert tmp["num"] == 2475227164 # TODO # @pytest.mark.unit # def test_enter(): # """Test __enter__()""" # iface = MeshInterface(noProto=True) # assert iface == iface.__enter__() @pytest.mark.unit def test_exit_with_exception(caplog): """Test __exit__()""" iface = MeshInterface(noProto=True) with caplog.at_level(logging.ERROR): iface.__exit__("foo", "bar", "baz") assert re.search( r"An exception of type foo with value bar has occurred", caplog.text, re.MULTILINE, ) assert re.search(r"Traceback: baz", caplog.text, re.MULTILINE) @pytest.mark.unit def test_showNodes_exclude_self(capsys, caplog, iface_with_nodes): """Test that we hit that continue statement""" with caplog.at_level(logging.DEBUG): iface = iface_with_nodes iface.localNode.nodeNum = 2475227164 iface.showNodes() iface.showNodes(includeSelf=False) capsys.readouterr() @pytest.mark.unitslow def test_waitForConfig(capsys): """Test waitForConfig()""" iface = MeshInterface(noProto=True) # override how long to wait iface._timeout = Timeout(0.01) with pytest.raises(MeshInterface.MeshInterfaceError) as pytest_wrapped_e: iface.waitForConfig() assert pytest_wrapped_e.type == MeshInterface.MeshInterfaceError out, err = capsys.readouterr() assert re.search( r"Exception: Timed out waiting for interface config", err, re.MULTILINE ) assert out == "" @pytest.mark.unit def test_waitConnected_raises_an_exception(capsys): """Test waitConnected()""" iface = MeshInterface(noProto=True) with pytest.raises(MeshInterface.MeshInterfaceError) as pytest_wrapped_e: iface.failure = MeshInterface.MeshInterfaceError("warn about something") iface._waitConnected(0.01) assert pytest_wrapped_e.type == MeshInterface.MeshInterfaceError out, err = capsys.readouterr() assert re.search(r"warn about something", err, re.MULTILINE) assert out == "" @pytest.mark.unit def test_waitConnected_isConnected_timeout(capsys): """Test waitConnected()""" with pytest.raises(MeshInterface.MeshInterfaceError) as pytest_wrapped_e: iface = MeshInterface() iface._waitConnected(0.01) assert pytest_wrapped_e.type == MeshInterface.MeshInterfaceError out, err = capsys.readouterr() assert re.search(r"warn about something", err, re.MULTILINE) assert out == "" @pytest.mark.unit def test_timeago(): """Test that the _timeago function returns sane values""" assert _timeago(0) == "now" assert _timeago(1) == "1 sec ago" assert _timeago(15) == "15 secs ago" assert _timeago(333) == "5 mins ago" assert _timeago(99999) == "1 day ago" assert _timeago(9999999) == "3 months ago" assert _timeago(-999) == "now" @given(seconds=st.integers()) def test_timeago_fuzz(seconds): """Fuzz _timeago to ensure it works with any integer""" val = _timeago(seconds) assert re.match(r"(now|\d+ (secs?|mins?|hours?|days?|months?|years?))", val) python-2.3.14/meshtastic/tests/test_node.py000066400000000000000000001462351464266072200210120ustar00rootroot00000000000000"""Meshtastic unit tests for node.py""" import logging import re from unittest.mock import MagicMock, patch import pytest from ..protobuf import localonly_pb2, config_pb2 from ..protobuf.channel_pb2 import Channel # pylint: disable=E0611 from ..node import Node from ..serial_interface import SerialInterface from ..mesh_interface import MeshInterface # from ..config_pb2 import Config # from ..cannedmessages_pb2 import (CannedMessagePluginMessagePart1, CannedMessagePluginMessagePart2, # CannedMessagePluginMessagePart3, CannedMessagePluginMessagePart4, # CannedMessagePluginMessagePart5) # from ..util import Timeout @pytest.mark.unit def test_node(capsys): """Test that we can instantiate a Node""" iface = MagicMock(autospec=SerialInterface) with patch("meshtastic.serial_interface.SerialInterface", return_value=iface) as mo: mo.localNode.getChannelByName.return_value = None mo.myInfo.max_channels = 8 anode = Node(mo, "bar", noProto=True) lc = localonly_pb2.LocalConfig() anode.localConfig = lc lc.lora.CopyFrom(config_pb2.Config.LoRaConfig()) anode.moduleConfig = localonly_pb2.LocalModuleConfig() anode.showInfo() out, err = capsys.readouterr() assert re.search(r'Preferences', out) assert re.search(r'Module preferences', out) assert re.search(r'Channels', out) assert re.search(r'Primary channel URL', out) assert not re.search(r'remote node', out) assert err == '' # TODO # @pytest.mark.unit # def test_node_requestConfig(capsys): # """Test run requestConfig""" # iface = MagicMock(autospec=SerialInterface) # amesg = MagicMock(autospec=AdminMessage) # with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo: # with patch('meshtastic.admin_pb2.AdminMessage', return_value=amesg): # anode = Node(mo, 'bar') # anode.requestConfig() # out, err = capsys.readouterr() # assert re.search(r'Requesting preferences from remote node', out, re.MULTILINE) # assert err == '' # @pytest.mark.unit # def test_node_get_canned_message_with_all_parts(capsys): # """Test run get_canned_message()""" # iface = MagicMock(autospec=SerialInterface) # amesg = MagicMock(autospec=AdminMessage) # with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo: # with patch('meshtastic.admin_pb2.AdminMessage', return_value=amesg): # # we have a sleep in this method, so override it so it goes fast # with patch('time.sleep'): # anode = Node(mo, 'bar') # anode.cannedPluginMessagePart1 = 'a' # anode.cannedPluginMessagePart2 = 'b' # anode.cannedPluginMessagePart3 = 'c' # anode.cannedPluginMessagePart4 = 'd' # anode.cannedPluginMessagePart5 = 'e' # anode.get_canned_message() # out, err = capsys.readouterr() # assert re.search(r'canned_plugin_message:abcde', out, re.MULTILINE) # assert err == '' # # # @pytest.mark.unit # def test_node_get_canned_message_with_some_parts(capsys): # """Test run get_canned_message()""" # iface = MagicMock(autospec=SerialInterface) # amesg = MagicMock(autospec=AdminMessage) # with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo: # with patch('meshtastic.admin_pb2.AdminMessage', return_value=amesg): # # we have a sleep in this method, so override it so it goes fast # with patch('time.sleep'): # anode = Node(mo, 'bar') # anode.cannedPluginMessagePart1 = 'a' # anode.get_canned_message() # out, err = capsys.readouterr() # assert re.search(r'canned_plugin_message:a', out, re.MULTILINE) # assert err == '' # # # @pytest.mark.unit # def test_node_set_canned_message_one_part(caplog): # """Test run set_canned_message()""" # iface = MagicMock(autospec=SerialInterface) # amesg = MagicMock(autospec=AdminMessage) # with caplog.at_level(logging.DEBUG): # with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo: # with patch('meshtastic.admin_pb2.AdminMessage', return_value=amesg): # anode = Node(mo, 'bar') # anode.set_canned_message('foo') # assert re.search(r"Setting canned message 'foo' part 1", caplog.text, re.MULTILINE) # assert not re.search(r"Setting canned message '' part 2", caplog.text, re.MULTILINE) # # # @pytest.mark.unit # def test_node_set_canned_message_200(caplog): # """Test run set_canned_message() 200 characters long""" # iface = MagicMock(autospec=SerialInterface) # amesg = MagicMock(autospec=AdminMessage) # with caplog.at_level(logging.DEBUG): # with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo: # with patch('meshtastic.admin_pb2.AdminMessage', return_value=amesg): # anode = Node(mo, 'bar') # message_200_chars_long = 'a' * 200 # anode.set_canned_message(message_200_chars_long) # assert re.search(r" part 1", caplog.text, re.MULTILINE) # assert not re.search(r"Setting canned message '' part 2", caplog.text, re.MULTILINE) # # # @pytest.mark.unit # def test_node_set_canned_message_201(caplog): # """Test run set_canned_message() 201 characters long""" # iface = MagicMock(autospec=SerialInterface) # amesg = MagicMock(autospec=AdminMessage) # with caplog.at_level(logging.DEBUG): # with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo: # with patch('meshtastic.admin_pb2.AdminMessage', return_value=amesg): # anode = Node(mo, 'bar') # message_201_chars_long = 'a' * 201 # anode.set_canned_message(message_201_chars_long) # assert re.search(r" part 1", caplog.text, re.MULTILINE) # assert re.search(r"Setting canned message 'a' part 2", caplog.text, re.MULTILINE) # # # @pytest.mark.unit # def test_node_set_canned_message_1000(caplog): # """Test run set_canned_message() 1000 characters long""" # iface = MagicMock(autospec=SerialInterface) # amesg = MagicMock(autospec=AdminMessage) # with caplog.at_level(logging.DEBUG): # with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo: # with patch('meshtastic.admin_pb2.AdminMessage', return_value=amesg): # anode = Node(mo, 'bar') # message_1000_chars_long = 'a' * 1000 # anode.set_canned_message(message_1000_chars_long) # assert re.search(r" part 1", caplog.text, re.MULTILINE) # assert re.search(r" part 2", caplog.text, re.MULTILINE) # assert re.search(r" part 3", caplog.text, re.MULTILINE) # assert re.search(r" part 4", caplog.text, re.MULTILINE) # assert re.search(r" part 5", caplog.text, re.MULTILINE) # # # @pytest.mark.unit # def test_node_set_canned_message_1001(capsys): # """Test run set_canned_message() 1001 characters long""" # iface = MagicMock(autospec=SerialInterface) # with pytest.raises(SystemExit) as pytest_wrapped_e: # with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo: # anode = Node(mo, 'bar') # message_1001_chars_long = 'a' * 1001 # anode.set_canned_message(message_1001_chars_long) # assert pytest_wrapped_e.type == SystemExit # assert pytest_wrapped_e.value.code == 1 # out, err = capsys.readouterr() # assert re.search(r'Warning: The canned message', out, re.MULTILINE) # assert err == '' # TODO # @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) # TODO # @pytest.mark.unit # def test_setOwner_no_short_name(caplog): # """Test setOwner""" # anode = Node('foo', 'bar', noProto=True) # with caplog.at_level(logging.DEBUG): # anode.setOwner(long_name ='Test123') # assert re.search(r'p.set_owner.long_name:Test123:', caplog.text, re.MULTILINE) # assert re.search(r'p.set_owner.short_name:Tst:', caplog.text, re.MULTILINE) # assert re.search(r'p.set_owner.is_licensed:False', caplog.text, re.MULTILINE) # TODO # @pytest.mark.unit # def test_setOwner_no_short_name_and_long_name_is_short(caplog): # """Test setOwner""" # anode = Node('foo', 'bar', noProto=True) # with caplog.at_level(logging.DEBUG): # anode.setOwner(long_name ='Tnt') # assert re.search(r'p.set_owner.long_name:Tnt:', caplog.text, re.MULTILINE) # assert re.search(r'p.set_owner.short_name:Tnt:', caplog.text, re.MULTILINE) # assert re.search(r'p.set_owner.is_licensed:False', caplog.text, re.MULTILINE) # TODO # @pytest.mark.unit # def test_setOwner_no_short_name_and_long_name_has_words(caplog): # """Test setOwner""" # anode = Node('foo', 'bar', noProto=True) # with caplog.at_level(logging.DEBUG): # anode.setOwner(long_name ='A B C', is_licensed=True) # assert re.search(r'p.set_owner.long_name:A B C:', caplog.text, re.MULTILINE) # assert re.search(r'p.set_owner.short_name:ABC:', caplog.text, re.MULTILINE) # assert re.search(r'p.set_owner.is_licensed:True', caplog.text, re.MULTILINE) # TODO # @pytest.mark.unit # def test_setOwner_long_name_no_short(caplog): # """Test setOwner""" # anode = Node('foo', 'bar', noProto=True) # with caplog.at_level(logging.DEBUG): # anode.setOwner(long_name ='Aabo', is_licensed=True) # assert re.search(r'p.set_owner.long_name:Aabo:', caplog.text, re.MULTILINE) # assert re.search(r'p.set_owner.short_name:Aab:', caplog.text, re.MULTILINE) @pytest.mark.unit def test_exitSimulator(caplog): """Test exitSimulator""" anode = Node("foo", "bar", noProto=True) with caplog.at_level(logging.DEBUG): anode.exitSimulator() assert re.search(r"in exitSimulator", caplog.text, re.MULTILINE) @pytest.mark.unit def test_reboot(caplog): """Test reboot""" anode = Node(MeshInterface(), 1234567890, noProto=True) with caplog.at_level(logging.DEBUG): anode.reboot() assert re.search(r"Telling node to reboot", caplog.text, re.MULTILINE) @pytest.mark.unit def test_shutdown(caplog): """Test shutdown""" anode = Node(MeshInterface(), 1234567890, noProto=True) with caplog.at_level(logging.DEBUG): anode.shutdown() assert re.search(r"Telling node to shutdown", caplog.text, re.MULTILINE) @pytest.mark.unit def test_setURL_empty_url(capsys): """Test reboot""" anode = Node("foo", "bar", noProto=True) with pytest.raises(SystemExit) as pytest_wrapped_e: anode.setURL("") assert pytest_wrapped_e.type == SystemExit assert pytest_wrapped_e.value.code == 1 out, err = capsys.readouterr() assert re.search(r"Warning: There were no settings.", out, re.MULTILINE) assert err == "" # TODO # @pytest.mark.unit # def test_setURL_valid_URL(caplog): # """Test setURL""" # iface = MagicMock(autospec=SerialInterface) # url = "https://www.meshtastic.org/d/#CgUYAyIBAQ" # with caplog.at_level(logging.DEBUG): # anode = Node(iface, 'bar', noProto=True) # anode.radioConfig = 'baz' # channels = ['zoo'] # anode.channels = channels # anode.setURL(url) # assert re.search(r'Channel i:0', caplog.text, re.MULTILINE) # assert re.search(r'modem_config: MidSlow', caplog.text, re.MULTILINE) # assert re.search(r'psk: "\\001"', caplog.text, re.MULTILINE) # assert re.search(r'role: PRIMARY', caplog.text, re.MULTILINE) @pytest.mark.unit def test_setURL_valid_URL_but_no_settings(capsys): """Test setURL""" iface = MagicMock(autospec=SerialInterface) url = "https://www.meshtastic.org/d/#" with pytest.raises(SystemExit) as pytest_wrapped_e: anode = Node(iface, "bar", noProto=True) anode.radioConfig = "baz" anode.setURL(url) assert pytest_wrapped_e.type == SystemExit assert pytest_wrapped_e.value.code == 1 out, err = capsys.readouterr() assert re.search(r"Warning: There were no settings", out, re.MULTILINE) assert err == "" # TODO # @pytest.mark.unit # def test_showChannels(capsys): # """Test showChannels""" # anode = Node('foo', 'bar') # # # primary channel # # role: 0=Disabled, 1=Primary, 2=Secondary # # modem_config: 0-5 # # role: 0=Disabled, 1=Primary, 2=Secondary # channel1 = Channel(index=1, role=1) # channel1.settings.modem_config = 3 # channel1.settings.psk = b'\x01' # # channel2 = Channel(index=2, role=2) # channel2.settings.psk = b'\x8a\x94y\x0e\xc6\xc9\x1e5\x91\x12@\xa60\xa8\xb43\x87\x00\xf2K\x0e\xe7\x7fAz\xcd\xf5\xb0\x900\xa84' # channel2.settings.name = 'testing' # # channel3 = Channel(index=3, role=0) # channel4 = Channel(index=4, role=0) # channel5 = Channel(index=5, role=0) # channel6 = Channel(index=6, role=0) # channel7 = Channel(index=7, role=0) # channel8 = Channel(index=8, role=0) # # channels = [ channel1, channel2, channel3, channel4, channel5, channel6, channel7, channel8 ] # # anode.channels = channels # anode.showChannels() # out, err = capsys.readouterr() # assert re.search(r'Channels:', out, re.MULTILINE) # # primary channel # assert re.search(r'Primary channel URL', out, re.MULTILINE) # assert re.search(r'PRIMARY psk=default ', out, re.MULTILINE) # assert re.search(r'"modemConfig": "MidSlow"', out, re.MULTILINE) # assert re.search(r'"psk": "AQ=="', out, re.MULTILINE) # # secondary channel # assert re.search(r'SECONDARY psk=secret ', out, re.MULTILINE) # assert re.search(r'"psk": "ipR5DsbJHjWREkCmMKi0M4cA8ksO539Bes31sJAwqDQ="', out, re.MULTILINE) # assert err == '' @pytest.mark.unit def test_getChannelByChannelIndex(): """Test getChannelByChannelIndex()""" anode = Node("foo", "bar") channel1 = Channel(index=1, role=1) # primary channel channel2 = Channel(index=2, role=2) # secondary channel channel3 = Channel(index=3, role=0) channel4 = Channel(index=4, role=0) channel5 = Channel(index=5, role=0) channel6 = Channel(index=6, role=0) channel7 = Channel(index=7, role=0) channel8 = Channel(index=8, role=0) channels = [ channel1, channel2, channel3, channel4, channel5, channel6, channel7, channel8, ] anode.channels = channels # test primary assert anode.getChannelByChannelIndex(0) is not None # test secondary assert anode.getChannelByChannelIndex(1) is not None # test disabled assert anode.getChannelByChannelIndex(2) is not None # test invalid values assert anode.getChannelByChannelIndex(-1) is None assert anode.getChannelByChannelIndex(9) is None # TODO # @pytest.mark.unit # def test_deleteChannel_try_to_delete_primary_channel(capsys): # """Try to delete primary channel.""" # anode = Node('foo', 'bar') # # channel1 = Channel(index=1, role=1) # channel1.settings.modem_config = 3 # channel1.settings.psk = b'\x01' # # # no secondary channels # channel2 = Channel(index=2, role=0) # channel3 = Channel(index=3, role=0) # channel4 = Channel(index=4, role=0) # channel5 = Channel(index=5, role=0) # channel6 = Channel(index=6, role=0) # channel7 = Channel(index=7, role=0) # channel8 = Channel(index=8, role=0) # # channels = [ channel1, channel2, channel3, channel4, channel5, channel6, channel7, channel8 ] # # anode.channels = channels # with pytest.raises(SystemExit) as pytest_wrapped_e: # anode.deleteChannel(0) # assert pytest_wrapped_e.type == SystemExit # assert pytest_wrapped_e.value.code == 1 # out, err = capsys.readouterr() # assert re.search(r'Warning: Only SECONDARY channels can be deleted', out, re.MULTILINE) # assert err == '' # TODO # @pytest.mark.unit # def test_deleteChannel_secondary(): # """Try to delete a secondary channel.""" # # channel1 = Channel(index=1, role=1) # channel1.settings.modem_config = 3 # channel1.settings.psk = b'\x01' # # channel2 = Channel(index=2, role=2) # channel2.settings.psk = b'\x8a\x94y\x0e\xc6\xc9\x1e5\x91\x12@\xa60\xa8\xb43\x87\x00\xf2K\x0e\xe7\x7fAz\xcd\xf5\xb0\x900\xa84' # channel2.settings.name = 'testing' # # channel3 = Channel(index=3, role=0) # channel4 = Channel(index=4, role=0) # channel5 = Channel(index=5, role=0) # channel6 = Channel(index=6, role=0) # channel7 = Channel(index=7, role=0) # channel8 = Channel(index=8, role=0) # # channels = [ channel1, channel2, channel3, channel4, channel5, channel6, channel7, channel8 ] # # # iface = MagicMock(autospec=SerialInterface) # with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo: # mo.localNode.getChannelByName.return_value = None # mo.myInfo.max_channels = 8 # anode = Node(mo, 'bar', noProto=True) # # anode.channels = channels # assert len(anode.channels) == 8 # assert channels[0].settings.modem_config == 3 # assert channels[1].settings.name == 'testing' # assert channels[2].settings.name == '' # assert channels[3].settings.name == '' # assert channels[4].settings.name == '' # assert channels[5].settings.name == '' # assert channels[6].settings.name == '' # assert channels[7].settings.name == '' # # anode.deleteChannel(1) # # assert len(anode.channels) == 8 # assert channels[0].settings.modem_config == 3 # assert channels[1].settings.name == '' # assert channels[2].settings.name == '' # assert channels[3].settings.name == '' # assert channels[4].settings.name == '' # assert channels[5].settings.name == '' # assert channels[6].settings.name == '' # assert channels[7].settings.name == '' # TODO # @pytest.mark.unit # def test_deleteChannel_secondary_with_admin_channel_after_testing(): # """Try to delete a secondary channel where there is an admin channel.""" # # channel1 = Channel(index=1, role=1) # channel1.settings.modem_config = 3 # channel1.settings.psk = b'\x01' # # channel2 = Channel(index=2, role=2) # channel2.settings.psk = b'\x8a\x94y\x0e\xc6\xc9\x1e5\x91\x12@\xa60\xa8\xb43\x87\x00\xf2K\x0e\xe7\x7fAz\xcd\xf5\xb0\x900\xa84' # channel2.settings.name = 'testing' # # channel3 = Channel(index=3, role=2) # channel3.settings.name = 'admin' # # channel4 = Channel(index=4, role=0) # channel5 = Channel(index=5, role=0) # channel6 = Channel(index=6, role=0) # channel7 = Channel(index=7, role=0) # channel8 = Channel(index=8, role=0) # # channels = [ channel1, channel2, channel3, channel4, channel5, channel6, channel7, channel8 ] # # # iface = MagicMock(autospec=SerialInterface) # with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo: # mo.localNode.getChannelByName.return_value = None # mo.myInfo.max_channels = 8 # anode = Node(mo, 'bar', noProto=True) # # # Note: Have to do this next line because every call to MagicMock object/method returns a new magic mock # mo.localNode = anode # # assert mo.localNode == anode # # anode.channels = channels # assert len(anode.channels) == 8 # assert channels[0].settings.modem_config == 3 # assert channels[1].settings.name == 'testing' # assert channels[2].settings.name == 'admin' # assert channels[3].settings.name == '' # assert channels[4].settings.name == '' # assert channels[5].settings.name == '' # assert channels[6].settings.name == '' # assert channels[7].settings.name == '' # # anode.deleteChannel(1) # # assert len(anode.channels) == 8 # assert channels[0].settings.modem_config == 3 # assert channels[1].settings.name == 'admin' # assert channels[2].settings.name == '' # assert channels[3].settings.name == '' # assert channels[4].settings.name == '' # assert channels[5].settings.name == '' # assert channels[6].settings.name == '' # assert channels[7].settings.name == '' # TODO # @pytest.mark.unit # def test_deleteChannel_secondary_with_admin_channel_before_testing(): # """Try to delete a secondary channel where there is an admin channel.""" # # channel1 = Channel(index=1, role=1) # channel1.settings.modem_config = 3 # channel1.settings.psk = b'\x01' # # channel2 = Channel(index=2, role=2) # channel2.settings.psk = b'\x8a\x94y\x0e\xc6\xc9\x1e5\x91\x12@\xa60\xa8\xb43\x87\x00\xf2K\x0e\xe7\x7fAz\xcd\xf5\xb0\x900\xa84' # channel2.settings.name = 'admin' # # channel3 = Channel(index=3, role=2) # channel3.settings.name = 'testing' # # channel4 = Channel(index=4, role=0) # channel5 = Channel(index=5, role=0) # channel6 = Channel(index=6, role=0) # channel7 = Channel(index=7, role=0) # channel8 = Channel(index=8, role=0) # # channels = [ channel1, channel2, channel3, channel4, channel5, channel6, channel7, channel8 ] # # # iface = MagicMock(autospec=SerialInterface) # with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo: # mo.localNode.getChannelByName.return_value = None # mo.myInfo.max_channels = 8 # anode = Node(mo, 'bar', noProto=True) # # anode.channels = channels # assert len(anode.channels) == 8 # assert channels[0].settings.modem_config == 3 # assert channels[1].settings.name == 'admin' # assert channels[2].settings.name == 'testing' # assert channels[3].settings.name == '' # assert channels[4].settings.name == '' # assert channels[5].settings.name == '' # assert channels[6].settings.name == '' # assert channels[7].settings.name == '' # # anode.deleteChannel(2) # # assert len(anode.channels) == 8 # assert channels[0].settings.modem_config == 3 # assert channels[1].settings.name == 'admin' # assert channels[2].settings.name == '' # assert channels[3].settings.name == '' # assert channels[4].settings.name == '' # assert channels[5].settings.name == '' # assert channels[6].settings.name == '' # assert channels[7].settings.name == '' # # # @pytest.mark.unit # def test_getChannelByName(): # """Get a channel by the name.""" # anode = Node('foo', 'bar') # # channel1 = Channel(index=1, role=1) # channel1.settings.modem_config = 3 # channel1.settings.psk = b'\x01' # # channel2 = Channel(index=2, role=2) # channel2.settings.psk = b'\x8a\x94y\x0e\xc6\xc9\x1e5\x91\x12@\xa60\xa8\xb43\x87\x00\xf2K\x0e\xe7\x7fAz\xcd\xf5\xb0\x900\xa84' # channel2.settings.name = 'admin' # # channel3 = Channel(index=3, role=0) # channel4 = Channel(index=4, role=0) # channel5 = Channel(index=5, role=0) # channel6 = Channel(index=6, role=0) # channel7 = Channel(index=7, role=0) # channel8 = Channel(index=8, role=0) # # channels = [ channel1, channel2, channel3, channel4, channel5, channel6, channel7, channel8 ] # # anode.channels = channels # ch = anode.getChannelByName('admin') # assert ch.index == 2 # TODO # @pytest.mark.unit # def test_getChannelByName_invalid_name(): # """Get a channel by the name but one that is not present.""" # anode = Node('foo', 'bar') # # channel1 = Channel(index=1, role=1) # channel1.settings.modem_config = 3 # channel1.settings.psk = b'\x01' # # channel2 = Channel(index=2, role=2) # channel2.settings.psk = b'\x8a\x94y\x0e\xc6\xc9\x1e5\x91\x12@\xa60\xa8\xb43\x87\x00\xf2K\x0e\xe7\x7fAz\xcd\xf5\xb0\x900\xa84' # channel2.settings.name = 'admin' # # channel3 = Channel(index=3, role=0) # channel4 = Channel(index=4, role=0) # channel5 = Channel(index=5, role=0) # channel6 = Channel(index=6, role=0) # channel7 = Channel(index=7, role=0) # channel8 = Channel(index=8, role=0) # # channels = [ channel1, channel2, channel3, channel4, channel5, channel6, channel7, channel8 ] # # anode.channels = channels # ch = anode.getChannelByName('testing') # assert ch is None # # # @pytest.mark.unit # def test_getDisabledChannel(): # """Get the first disabled channel.""" # anode = Node('foo', 'bar') # # channel1 = Channel(index=1, role=1) # channel1.settings.modem_config = 3 # channel1.settings.psk = b'\x01' # # channel2 = Channel(index=2, role=2) # channel2.settings.psk = b'\x8a\x94y\x0e\xc6\xc9\x1e5\x91\x12@\xa60\xa8\xb43\x87\x00\xf2K\x0e\xe7\x7fAz\xcd\xf5\xb0\x900\xa84' # channel2.settings.name = 'testingA' # # channel3 = Channel(index=3, role=2) # channel3.settings.psk = b'\x8a\x94y\x0e\xc6\xc9\x1e5\x91\x12@\xa60\xa8\xb43\x87\x00\xf2K\x0e\xe7\x7fAz\xcd\xf5\xb0\x900\xa84' # channel3.settings.name = 'testingB' # # channel4 = Channel(index=4, role=0) # channel5 = Channel(index=5, role=0) # channel6 = Channel(index=6, role=0) # channel7 = Channel(index=7, role=0) # channel8 = Channel(index=8, role=0) # # channels = [ channel1, channel2, channel3, channel4, channel5, channel6, channel7, channel8 ] # # anode.channels = channels # ch = anode.getDisabledChannel() # assert ch.index == 4 # TODO # @pytest.mark.unit # def test_getDisabledChannel_where_all_channels_are_used(): # """Get the first disabled channel.""" # anode = Node('foo', 'bar') # # channel1 = Channel(index=1, role=1) # channel1.settings.modem_config = 3 # channel1.settings.psk = b'\x01' # # channel2 = Channel(index=2, role=2) # channel3 = Channel(index=3, role=2) # channel4 = Channel(index=4, role=2) # channel5 = Channel(index=5, role=2) # channel6 = Channel(index=6, role=2) # channel7 = Channel(index=7, role=2) # channel8 = Channel(index=8, role=2) # # channels = [ channel1, channel2, channel3, channel4, channel5, channel6, channel7, channel8 ] # # anode.channels = channels # ch = anode.getDisabledChannel() # assert ch is None # TODO # @pytest.mark.unit # def test_getAdminChannelIndex(): # """Get the 'admin' channel index.""" # anode = Node('foo', 'bar') # # channel1 = Channel(index=1, role=1) # channel1.settings.modem_config = 3 # channel1.settings.psk = b'\x01' # # channel2 = Channel(index=2, role=2) # channel2.settings.psk = b'\x8a\x94y\x0e\xc6\xc9\x1e5\x91\x12@\xa60\xa8\xb43\x87\x00\xf2K\x0e\xe7\x7fAz\xcd\xf5\xb0\x900\xa84' # channel2.settings.name = 'admin' # # channel3 = Channel(index=3, role=0) # channel4 = Channel(index=4, role=0) # channel5 = Channel(index=5, role=0) # channel6 = Channel(index=6, role=0) # channel7 = Channel(index=7, role=0) # channel8 = Channel(index=8, role=0) # # channels = [ channel1, channel2, channel3, channel4, channel5, channel6, channel7, channel8 ] # # anode.channels = channels # i = anode._getAdminChannelIndex() # assert i == 2 # TODO # @pytest.mark.unit # def test_getAdminChannelIndex_when_no_admin_named_channel(): # """Get the 'admin' channel when there is not one.""" # anode = Node('foo', 'bar') # # channel1 = Channel(index=1, role=1) # channel1.settings.modem_config = 3 # channel1.settings.psk = b'\x01' # # channel2 = Channel(index=2, role=0) # channel3 = Channel(index=3, role=0) # channel4 = Channel(index=4, role=0) # channel5 = Channel(index=5, role=0) # channel6 = Channel(index=6, role=0) # channel7 = Channel(index=7, role=0) # channel8 = Channel(index=8, role=0) # # channels = [ channel1, channel2, channel3, channel4, channel5, channel6, channel7, channel8 ] # # anode.channels = channels # i = anode._getAdminChannelIndex() # assert i == 0 # TODO # TODO: should we check if we need to turn it off? # @pytest.mark.unit # def test_turnOffEncryptionOnPrimaryChannel(capsys): # """Turn off encryption when there is a psk.""" # anode = Node('foo', 'bar', noProto=True) # # channel1 = Channel(index=1, role=1) # channel1.settings.modem_config = 3 # # value from using "--ch-set psk 0x1a1a1a1a2b2b2b2b1a1a1a1a2b2b2b2b1a1a1a1a2b2b2b2b1a1a1a1a2b2b2b2b " # channel1.settings.psk = b'\x1a\x1a\x1a\x1a++++\x1a\x1a\x1a\x1a++++\x1a\x1a\x1a\x1a++++\x1a\x1a\x1a\x1a++++' # # channel2 = Channel(index=2, role=0) # channel3 = Channel(index=3, role=0) # channel4 = Channel(index=4, role=0) # channel5 = Channel(index=5, role=0) # channel6 = Channel(index=6, role=0) # channel7 = Channel(index=7, role=0) # channel8 = Channel(index=8, role=0) # # channels = [ channel1, channel2, channel3, channel4, channel5, channel6, channel7, channel8 ] # # anode.channels = channels # anode.turnOffEncryptionOnPrimaryChannel() # out, err = capsys.readouterr() # assert re.search(r'Writing modified channels to device', out) # assert err == '' @pytest.mark.unit def test_writeConfig_with_no_radioConfig(capsys): """Test writeConfig with no radioConfig.""" anode = Node("foo", "bar", noProto=True) with pytest.raises(SystemExit) as pytest_wrapped_e: anode.writeConfig('foo') assert pytest_wrapped_e.type == SystemExit assert pytest_wrapped_e.value.code == 1 out, err = capsys.readouterr() print(out) assert re.search(r"Error: No valid config with name foo", out) assert err == "" # TODO # @pytest.mark.unit # def test_writeConfig(caplog): # """Test writeConfig""" # anode = Node('foo', 'bar', noProto=True) # radioConfig = RadioConfig() # anode.radioConfig = radioConfig # # with caplog.at_level(logging.DEBUG): # anode.writeConfig() # assert re.search(r'Wrote config', caplog.text, re.MULTILINE) @pytest.mark.unit def test_requestChannel_not_localNode(caplog, capsys): """Test _requestChannel()""" iface = MagicMock(autospec=SerialInterface) with patch("meshtastic.serial_interface.SerialInterface", return_value=iface) as mo: mo.localNode.getChannelByName.return_value = None mo.myInfo.max_channels = 8 anode = Node(mo, "bar", noProto=True) with caplog.at_level(logging.DEBUG): anode._requestChannel(0) assert re.search( r"Requesting channel 0 info from remote node", caplog.text, re.MULTILINE ) out, err = capsys.readouterr() assert re.search(r"Requesting channel 0 info", out, re.MULTILINE) assert err == "" @pytest.mark.unit def test_requestChannel_localNode(caplog): """Test _requestChannel()""" iface = MagicMock(autospec=SerialInterface) with patch("meshtastic.serial_interface.SerialInterface", return_value=iface) as mo: mo.localNode.getChannelByName.return_value = None mo.myInfo.max_channels = 8 anode = Node(mo, "bar", noProto=True) # Note: Have to do this next line because every call to MagicMock object/method returns a new magic mock mo.localNode = anode with caplog.at_level(logging.DEBUG): anode._requestChannel(0) assert re.search(r"Requesting channel 0", caplog.text, re.MULTILINE) assert not re.search(r"from remote node", caplog.text, re.MULTILINE) # @pytest.mark.unit # def test_onResponseRequestCannedMessagePluginMesagePart1(caplog): # """Test onResponseRequestCannedMessagePluginMessagePart1()""" # # part1 = CannedMessagePluginMessagePart1() # part1.text = 'foo1' # # msg1 = MagicMock(autospec=AdminMessage) # msg1.get_canned_message_plugin_part1_response = part1 # # packet = { # 'from': 682968612, # 'to': 682968612, # 'decoded': { # 'portnum': 'ADMIN_APP', # 'payload': 'faked', # 'requestId': 927039000, # 'admin': { # 'getCannedMessagePluginPart1Response': {'text': 'foo1'}, # 'raw': msg1 # } # }, # 'id': 589440320, # 'rxTime': 1642710843, # 'hopLimit': 3, # 'priority': 'RELIABLE', # 'raw': 'faked', # 'fromId': '!28b54624', # 'toId': '!28b54624' # } # # iface = MagicMock(autospec=SerialInterface) # with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo: # anode = Node(mo, 'bar', noProto=True) # # Note: Have to do this next line because every call to MagicMock object/method returns a new magic mock # mo.localNode = anode # # with caplog.at_level(logging.DEBUG): # anode.onResponseRequestCannedMessagePluginMessagePart1(packet) # assert re.search(r'onResponseRequestCannedMessagePluginMessagePart1', caplog.text, re.MULTILINE) # assert anode.cannedPluginMessagePart1 == 'foo1' # @pytest.mark.unit # def test_onResponseRequestCannedMessagePluginMesagePart2(caplog): # """Test onResponseRequestCannedMessagePluginMessagePart2()""" # # part2 = CannedMessagePluginMessagePart2() # part2.text = 'foo2' # # msg2 = MagicMock(autospec=AdminMessage) # msg2.get_canned_message_plugin_part2_response = part2 # # packet = { # 'from': 682968612, # 'to': 682968612, # 'decoded': { # 'portnum': 'ADMIN_APP', # 'payload': 'faked', # 'requestId': 927039000, # 'admin': { # 'getCannedMessagePluginPart2Response': {'text': 'foo2'}, # 'raw': msg2 # } # }, # 'id': 589440320, # 'rxTime': 1642710843, # 'hopLimit': 3, # 'priority': 'RELIABLE', # 'raw': 'faked', # 'fromId': '!28b54624', # 'toId': '!28b54624' # } # # iface = MagicMock(autospec=SerialInterface) # with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo: # anode = Node(mo, 'bar', noProto=True) # # Note: Have to do this next line because every call to MagicMock object/method returns a new magic mock # mo.localNode = anode # # with caplog.at_level(logging.DEBUG): # anode.onResponseRequestCannedMessagePluginMessagePart2(packet) # assert re.search(r'onResponseRequestCannedMessagePluginMessagePart2', caplog.text, re.MULTILINE) # assert anode.cannedPluginMessagePart2 == 'foo2' # @pytest.mark.unit # def test_onResponseRequestCannedMessagePluginMesagePart3(caplog): # """Test onResponseRequestCannedMessagePluginMessagePart3()""" # # part3 = CannedMessagePluginMessagePart3() # part3.text = 'foo3' # # msg3 = MagicMock(autospec=AdminMessage) # msg3.get_canned_message_plugin_part3_response = part3 # # packet = { # 'from': 682968612, # 'to': 682968612, # 'decoded': { # 'portnum': 'ADMIN_APP', # 'payload': 'faked', # 'requestId': 927039000, # 'admin': { # 'getCannedMessagePluginPart3Response': {'text': 'foo3'}, # 'raw': msg3 # } # }, # 'id': 589440320, # 'rxTime': 1642710843, # 'hopLimit': 3, # 'priority': 'RELIABLE', # 'raw': 'faked', # 'fromId': '!28b54624', # 'toId': '!28b54624' # } # # iface = MagicMock(autospec=SerialInterface) # with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo: # anode = Node(mo, 'bar', noProto=True) # # Note: Have to do this next line because every call to MagicMock object/method returns a new magic mock # mo.localNode = anode # # with caplog.at_level(logging.DEBUG): # anode.onResponseRequestCannedMessagePluginMessagePart3(packet) # assert re.search(r'onResponseRequestCannedMessagePluginMessagePart3', caplog.text, re.MULTILINE) # assert anode.cannedPluginMessagePart3 == 'foo3' # @pytest.mark.unit # def test_onResponseRequestCannedMessagePluginMesagePart4(caplog): # """Test onResponseRequestCannedMessagePluginMessagePart4()""" # # part4 = CannedMessagePluginMessagePart4() # part4.text = 'foo4' # # msg4 = MagicMock(autospec=AdminMessage) # msg4.get_canned_message_plugin_part4_response = part4 # # packet = { # 'from': 682968612, # 'to': 682968612, # 'decoded': { # 'portnum': 'ADMIN_APP', # 'payload': 'faked', # 'requestId': 927039000, # 'admin': { # 'getCannedMessagePluginPart4Response': {'text': 'foo4'}, # 'raw': msg4 # } # }, # 'id': 589440320, # 'rxTime': 1642710843, # 'hopLimit': 3, # 'priority': 'RELIABLE', # 'raw': 'faked', # 'fromId': '!28b54624', # 'toId': '!28b54624' # } # # iface = MagicMock(autospec=SerialInterface) # with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo: # anode = Node(mo, 'bar', noProto=True) # # Note: Have to do this next line because every call to MagicMock object/method returns a new magic mock # mo.localNode = anode # # with caplog.at_level(logging.DEBUG): # anode.onResponseRequestCannedMessagePluginMessagePart4(packet) # assert re.search(r'onResponseRequestCannedMessagePluginMessagePart4', caplog.text, re.MULTILINE) # assert anode.cannedPluginMessagePart4 == 'foo4' # @pytest.mark.unit # def test_onResponseRequestCannedMessagePluginMesagePart5(caplog): # """Test onResponseRequestCannedMessagePluginMessagePart5()""" # # part5 = CannedMessagePluginMessagePart5() # part5.text = 'foo5' # # msg5 = MagicMock(autospec=AdminMessage) # msg5.get_canned_message_plugin_part5_response = part5 # # # packet = { # 'from': 682968612, # 'to': 682968612, # 'decoded': { # 'portnum': 'ADMIN_APP', # 'payload': 'faked', # 'requestId': 927039000, # 'admin': { # 'getCannedMessagePluginPart5Response': {'text': 'foo5'}, # 'raw': msg5 # } # }, # 'id': 589440320, # 'rxTime': 1642710843, # 'hopLimit': 3, # 'priority': 'RELIABLE', # 'raw': 'faked', # 'fromId': '!28b54624', # 'toId': '!28b54624' # } # # iface = MagicMock(autospec=SerialInterface) # with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo: # anode = Node(mo, 'bar', noProto=True) # # Note: Have to do this next line because every call to MagicMock object/method returns a new magic mock # mo.localNode = anode # # with caplog.at_level(logging.DEBUG): # anode.onResponseRequestCannedMessagePluginMessagePart5(packet) # assert re.search(r'onResponseRequestCannedMessagePluginMessagePart5', caplog.text, re.MULTILINE) # assert anode.cannedPluginMessagePart5 == 'foo5' # @pytest.mark.unit # def test_onResponseRequestCannedMessagePluginMesagePart1_error(caplog, capsys): # """Test onResponseRequestCannedMessagePluginMessagePart1() with error""" # # packet = { # 'decoded': { # 'routing': { # 'errorReason': 'some made up error', # }, # }, # } # # iface = MagicMock(autospec=SerialInterface) # with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo: # anode = Node(mo, 'bar', noProto=True) # # Note: Have to do this next line because every call to MagicMock object/method returns a new magic mock # mo.localNode = anode # # with caplog.at_level(logging.DEBUG): # anode.onResponseRequestCannedMessagePluginMessagePart1(packet) # assert re.search(r'onResponseRequestCannedMessagePluginMessagePart1', caplog.text, re.MULTILINE) # out, err = capsys.readouterr() # assert re.search(r'Error on response', out) # assert err == '' # @pytest.mark.unit # def test_onResponseRequestCannedMessagePluginMesagePart2_error(caplog, capsys): # """Test onResponseRequestCannedMessagePluginMessagePart2() with error""" # # packet = { # 'decoded': { # 'routing': { # 'errorReason': 'some made up error', # }, # }, # } # # iface = MagicMock(autospec=SerialInterface) # with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo: # anode = Node(mo, 'bar', noProto=True) # # Note: Have to do this next line because every call to MagicMock object/method returns a new magic mock # mo.localNode = anode # # with caplog.at_level(logging.DEBUG): # anode.onResponseRequestCannedMessagePluginMessagePart2(packet) # assert re.search(r'onResponseRequestCannedMessagePluginMessagePart2', caplog.text, re.MULTILINE) # out, err = capsys.readouterr() # assert re.search(r'Error on response', out) # assert err == '' # @pytest.mark.unit # def test_onResponseRequestCannedMessagePluginMesagePart3_error(caplog, capsys): # """Test onResponseRequestCannedMessagePluginMessagePart3() with error""" # # packet = { # 'decoded': { # 'routing': { # 'errorReason': 'some made up error', # }, # }, # } # # iface = MagicMock(autospec=SerialInterface) # with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo: # anode = Node(mo, 'bar', noProto=True) # # Note: Have to do this next line because every call to MagicMock object/method returns a new magic mock # mo.localNode = anode # # with caplog.at_level(logging.DEBUG): # anode.onResponseRequestCannedMessagePluginMessagePart3(packet) # assert re.search(r'onResponseRequestCannedMessagePluginMessagePart3', caplog.text, re.MULTILINE) # out, err = capsys.readouterr() # assert re.search(r'Error on response', out) # assert err == '' # # # @pytest.mark.unit # def test_onResponseRequestCannedMessagePluginMesagePart4_error(caplog, capsys): # """Test onResponseRequestCannedMessagePluginMessagePart4() with error""" # # packet = { # 'decoded': { # 'routing': { # 'errorReason': 'some made up error', # }, # }, # } # # iface = MagicMock(autospec=SerialInterface) # with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo: # anode = Node(mo, 'bar', noProto=True) # # Note: Have to do this next line because every call to MagicMock object/method returns a new magic mock # mo.localNode = anode # # with caplog.at_level(logging.DEBUG): # anode.onResponseRequestCannedMessagePluginMessagePart4(packet) # assert re.search(r'onResponseRequestCannedMessagePluginMessagePart4', caplog.text, re.MULTILINE) # out, err = capsys.readouterr() # assert re.search(r'Error on response', out) # assert err == '' # # # @pytest.mark.unit # def test_onResponseRequestCannedMessagePluginMesagePart5_error(caplog, capsys): # """Test onResponseRequestCannedMessagePluginMessagePart5() with error""" # # packet = { # 'decoded': { # 'routing': { # 'errorReason': 'some made up error', # }, # }, # } # # iface = MagicMock(autospec=SerialInterface) # with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo: # anode = Node(mo, 'bar', noProto=True) # # Note: Have to do this next line because every call to MagicMock object/method returns a new magic mock # mo.localNode = anode # # with caplog.at_level(logging.DEBUG): # anode.onResponseRequestCannedMessagePluginMessagePart5(packet) # assert re.search(r'onResponseRequestCannedMessagePluginMessagePart5', caplog.text, re.MULTILINE) # out, err = capsys.readouterr() # assert re.search(r'Error on response', out) # assert err == '' # TODO # @pytest.mark.unit # def test_onResponseRequestChannel(caplog): # """Test onResponseRequestChannel()""" # # channel1 = Channel(index=1, role=1) # channel1.settings.modem_config = 3 # channel1.settings.psk = b'\x01' # # msg1 = MagicMock(autospec=AdminMessage) # msg1.get_channel_response = channel1 # # msg2 = MagicMock(autospec=AdminMessage) # channel2 = Channel(index=2, role=0) # disabled # msg2.get_channel_response = channel2 # # # default primary channel # packet1 = { # 'from': 2475227164, # 'to': 2475227164, # 'decoded': { # 'portnum': 'ADMIN_APP', # 'payload': b':\t\x12\x05\x18\x03"\x01\x01\x18\x01', # 'requestId': 2615094405, # 'admin': { # 'getChannelResponse': { # 'settings': { # 'modemConfig': 'Bw125Cr48Sf4096', # 'psk': 'AQ==' # }, # 'role': 'PRIMARY' # }, # 'raw': msg1, # } # }, # 'id': 1692918436, # 'hopLimit': 3, # 'priority': # 'RELIABLE', # 'raw': 'fake', # 'fromId': '!9388f81c', # 'toId': '!9388f81c' # } # # # no other channels # packet2 = { # 'from': 2475227164, # 'to': 2475227164, # 'decoded': { # 'portnum': 'ADMIN_APP', # 'payload': b':\x04\x08\x02\x12\x00', # 'requestId': 743049663, # 'admin': { # 'getChannelResponse': { # 'index': 2, # 'settings': {} # }, # 'raw': msg2, # } # }, # 'id': 1692918456, # 'rxTime': 1640202239, # 'hopLimit': 3, # 'priority': 'RELIABLE', # 'raw': 'faked', # 'fromId': '!9388f81c', # 'toId': '!9388f81c' # } # # iface = MagicMock(autospec=SerialInterface) # with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo: # mo.localNode.getChannelByName.return_value = None # mo.myInfo.max_channels = 8 # anode = Node(mo, 'bar', noProto=True) # # radioConfig = RadioConfig() # anode.radioConfig = radioConfig # # # Note: Have to do this next line because every call to MagicMock object/method returns a new magic mock # mo.localNode = anode # # with caplog.at_level(logging.DEBUG): # anode.requestConfig() # anode.onResponseRequestChannel(packet1) # assert re.search(r'Received channel', caplog.text, re.MULTILINE) # anode.onResponseRequestChannel(packet2) # assert re.search(r'Received channel', caplog.text, re.MULTILINE) # assert re.search(r'Finished downloading channels', caplog.text, re.MULTILINE) # assert len(anode.channels) == 8 # assert anode.channels[0].settings.modem_config == 3 # assert anode.channels[1].settings.name == '' # assert anode.channels[2].settings.name == '' # assert anode.channels[3].settings.name == '' # assert anode.channels[4].settings.name == '' # assert anode.channels[5].settings.name == '' # assert anode.channels[6].settings.name == '' # assert anode.channels[7].settings.name == '' # TODO # @pytest.mark.unit # def test_onResponseRequestSetting(caplog): # """Test onResponseRequestSetting()""" # # Note: Split out the get_radio_response to a MagicMock # # so it could be "returned" (not really sure how to do that # # in a python dict. # amsg = MagicMock(autospec=AdminMessage) # amsg.get_radio_response = """{ # preferences { # phone_timeout_secs: 900 # ls_secs: 300 # position_broadcast_smart: true # position_flags: 35 # } # }""" # packet = { # 'from': 2475227164, # 'to': 2475227164, # 'decoded': { # 'portnum': 'ADMIN_APP', # 'payload': b'*\x0e\n\x0c0\x84\x07P\xac\x02\x88\x01\x01\xb0\t#', # 'requestId': 3145147848, # 'admin': { # 'getRadioResponse': { # 'preferences': { # 'phoneTimeoutSecs': 900, # 'lsSecs': 300, # 'positionBroadcastSmart': True, # 'positionFlags': 35 # } # }, # 'raw': amsg # }, # 'id': 365963704, # 'rxTime': 1640195197, # 'hopLimit': 3, # 'priority': 'RELIABLE', # 'raw': 'faked', # 'fromId': '!9388f81c', # 'toId': '!9388f81c' # } # } # iface = MagicMock(autospec=SerialInterface) # with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo: # mo.localNode.getChannelByName.return_value = None # mo.myInfo.max_channels = 8 # anode = Node(mo, 'bar', noProto=True) # # radioConfig = RadioConfig() # anode.radioConfig = radioConfig # # # Note: Have to do this next line because every call to MagicMock object/method returns a new magic mock # mo.localNode = anode # # with caplog.at_level(logging.DEBUG): # anode.onResponseRequestSettings(packet) # assert re.search(r'Received radio config, now fetching channels..', caplog.text, re.MULTILINE) # TODO # @pytest.mark.unit # def test_onResponseRequestSetting_with_error(capsys): # """Test onResponseRequestSetting() with an error""" # packet = { # 'from': 2475227164, # 'to': 2475227164, # 'decoded': { # 'portnum': 'ADMIN_APP', # 'payload': b'*\x0e\n\x0c0\x84\x07P\xac\x02\x88\x01\x01\xb0\t#', # 'requestId': 3145147848, # 'routing': { # 'errorReason': 'some made up error', # }, # 'admin': { # 'getRadioResponse': { # 'preferences': { # 'phoneTimeoutSecs': 900, # 'lsSecs': 300, # 'positionBroadcastSmart': True, # 'positionFlags': 35 # } # }, # }, # 'id': 365963704, # 'rxTime': 1640195197, # 'hopLimit': 3, # 'priority': 'RELIABLE', # 'fromId': '!9388f81c', # 'toId': '!9388f81c' # } # } # iface = MagicMock(autospec=SerialInterface) # with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo: # mo.localNode.getChannelByName.return_value = None # mo.myInfo.max_channels = 8 # anode = Node(mo, 'bar', noProto=True) # # radioConfig = RadioConfig() # anode.radioConfig = radioConfig # # # Note: Have to do this next line because every call to MagicMock object/method returns a new magic mock # mo.localNode = anode # # anode.onResponseRequestSettings(packet) # out, err = capsys.readouterr() # assert re.search(r'Error on response', out) # assert err == '' # TODO # @pytest.mark.unitslow # def test_waitForConfig(): # """Test waitForConfig()""" # anode = Node('foo', 'bar') # radioConfig = RadioConfig() # anode.radioConfig = radioConfig # anode._timeout = Timeout(0.01) # result = anode.waitForConfig() # assert not result python-2.3.14/meshtastic/tests/test_remote_hardware.py000066400000000000000000000060221464266072200232220ustar00rootroot00000000000000"""Meshtastic unit tests for remote_hardware.py""" import logging import re from unittest.mock import MagicMock, patch import pytest from ..remote_hardware import RemoteHardwareClient, onGPIOreceive from ..serial_interface import SerialInterface @pytest.mark.unit def test_RemoteHardwareClient(): """Test that we can instantiate a RemoteHardwareClient instance""" iface = MagicMock(autospec=SerialInterface) rhw = RemoteHardwareClient(iface) assert rhw.iface == iface iface.close() @pytest.mark.unit def test_onGPIOreceive(capsys): """Test onGPIOreceive""" iface = MagicMock(autospec=SerialInterface) packet = {"decoded": {"remotehw": {"type": "foo", "gpioValue": "4096"}}} onGPIOreceive(packet, iface) out, err = capsys.readouterr() assert re.search(r"Received RemoteHardware", out) assert err == "" @pytest.mark.unit def test_RemoteHardwareClient_no_gpio_channel(capsys): """Test that we can instantiate a RemoteHardwareClient instance but there is no channel named channel 'gpio'""" iface = MagicMock(autospec=SerialInterface) with patch("meshtastic.serial_interface.SerialInterface", return_value=iface) as mo: mo.localNode.getChannelByName.return_value = None with pytest.raises(SystemExit) as pytest_wrapped_e: RemoteHardwareClient(mo) assert pytest_wrapped_e.type == SystemExit assert pytest_wrapped_e.value.code == 1 out, err = capsys.readouterr() assert re.search(r"Warning: No channel named", out) assert err == "" @pytest.mark.unit def test_readGPIOs(caplog): """Test readGPIOs""" iface = MagicMock(autospec=SerialInterface) rhw = RemoteHardwareClient(iface) with caplog.at_level(logging.DEBUG): rhw.readGPIOs("0x10", 123) assert re.search(r"readGPIOs", caplog.text, re.MULTILINE) iface.close() @pytest.mark.unit def test_writeGPIOs(caplog): """Test writeGPIOs""" iface = MagicMock(autospec=SerialInterface) rhw = RemoteHardwareClient(iface) with caplog.at_level(logging.DEBUG): rhw.writeGPIOs("0x10", 123, 1) assert re.search(r"writeGPIOs", caplog.text, re.MULTILINE) iface.close() @pytest.mark.unit def test_watchGPIOs(caplog): """Test watchGPIOs""" iface = MagicMock(autospec=SerialInterface) rhw = RemoteHardwareClient(iface) with caplog.at_level(logging.DEBUG): rhw.watchGPIOs("0x10", 123) assert re.search(r"watchGPIOs", caplog.text, re.MULTILINE) iface.close() @pytest.mark.unit def test_sendHardware_no_nodeid(capsys): """Test sending no nodeid to _sendHardware()""" iface = MagicMock(autospec=SerialInterface) with patch("meshtastic.serial_interface.SerialInterface", return_value=iface) as mo: with pytest.raises(SystemExit) as pytest_wrapped_e: rhw = RemoteHardwareClient(mo) rhw._sendHardware(None, None) assert pytest_wrapped_e.type == SystemExit out, err = capsys.readouterr() assert re.search(r"Warning: Must use a destination node ID", out) assert err == "" python-2.3.14/meshtastic/tests/test_serial_interface.py000066400000000000000000000045701464266072200233570ustar00rootroot00000000000000"""Meshtastic unit tests for serial_interface.py""" import re from unittest.mock import mock_open, patch import pytest from ..serial_interface import SerialInterface from ..protobuf import config_pb2 @pytest.mark.unit @patch("time.sleep") @patch("termios.tcsetattr") @patch("termios.tcgetattr") @patch("builtins.open", new_callable=mock_open, read_data="data") @patch("serial.Serial") @patch("meshtastic.util.findPorts", return_value=["/dev/ttyUSBfake"]) def test_SerialInterface_single_port( mocked_findPorts, mocked_serial, mocked_open, mock_get, mock_set, mock_sleep, capsys ): """Test that we can instantiate a SerialInterface with a single port""" iface = SerialInterface(noProto=True) iface.localNode.localConfig.lora.CopyFrom(config_pb2.Config.LoRaConfig()) iface.showInfo() iface.localNode.showInfo() iface.close() mocked_findPorts.assert_called() mocked_serial.assert_called() mocked_open.assert_called() mock_get.assert_called() mock_set.assert_called() mock_sleep.assert_called() out, err = capsys.readouterr() assert re.search(r"Nodes in mesh", out, re.MULTILINE) assert re.search(r"Preferences", out, re.MULTILINE) assert re.search(r"Channels", out, re.MULTILINE) assert re.search(r"Primary channel", out, re.MULTILINE) assert err == "" @pytest.mark.unit @patch("meshtastic.util.findPorts", return_value=[]) def test_SerialInterface_no_ports(mocked_findPorts, capsys): """Test that we can instantiate a SerialInterface with no ports""" serialInterface = SerialInterface(noProto=True) mocked_findPorts.assert_called() assert serialInterface.devPath is None out, err = capsys.readouterr() assert re.search(r"No.*Meshtastic.*device.*detected", out, re.MULTILINE) assert err == "" @pytest.mark.unit @patch( "meshtastic.util.findPorts", return_value=["/dev/ttyUSBfake1", "/dev/ttyUSBfake2"] ) def test_SerialInterface_multiple_ports(mocked_findPorts, capsys): """Test that we can instantiate a SerialInterface with two ports""" with pytest.raises(SystemExit) as pytest_wrapped_e: SerialInterface(noProto=True) mocked_findPorts.assert_called() assert pytest_wrapped_e.type == SystemExit assert pytest_wrapped_e.value.code == 1 out, err = capsys.readouterr() assert re.search(r"Warning: Multiple serial ports were detected", out, re.MULTILINE) assert err == "" python-2.3.14/meshtastic/tests/test_smoke1.py000066400000000000000000000642121464266072200212560ustar00rootroot00000000000000"""Meshtastic smoke tests with a single device via USB""" import os import platform import re import subprocess import time # Do not like using hard coded sleeps, but it probably makes # sense to pause for the radio at appropriate times import pytest from ..util import findPorts # seconds to pause after running a meshtastic command PAUSE_AFTER_COMMAND = 2 PAUSE_AFTER_REBOOT = 7 @pytest.mark.smoke1 def test_smoke1_reboot(): """Test reboot""" return_value, _ = subprocess.getstatusoutput("meshtastic --reboot") assert return_value == 0 # pause for the radio to reset (10 seconds for the pause, and a few more seconds to be back up) time.sleep(18) @pytest.mark.smoke1 def test_smoke1_info(): """Test --info""" return_value, out = subprocess.getstatusoutput("meshtastic --info") assert re.match(r"Connected to radio", out) assert re.search(r"^Owner", out, re.MULTILINE) assert re.search(r"^My info", out, re.MULTILINE) assert re.search(r"^Nodes in mesh", out, re.MULTILINE) assert re.search(r"^Preferences", out, re.MULTILINE) assert re.search(r"^Channels", out, re.MULTILINE) assert re.search(r"^ PRIMARY", out, re.MULTILINE) assert re.search(r"^Primary channel URL", out, re.MULTILINE) assert return_value == 0 @pytest.mark.smoke1 def test_get_with_invalid_setting(): """Test '--get a_bad_setting'.""" return_value, out = subprocess.getstatusoutput("meshtastic --get a_bad_setting") assert re.search(r"Choices in sorted order", out) assert return_value == 0 @pytest.mark.smoke1 def test_set_with_invalid_setting(): """Test '--set a_bad_setting'.""" return_value, out = subprocess.getstatusoutput("meshtastic --set a_bad_setting foo") assert re.search(r"Choices in sorted order", out) assert return_value == 0 @pytest.mark.smoke1 def test_ch_set_with_invalid_settingpatch_find_ports(): """Test '--ch-set with a_bad_setting'.""" return_value, out = subprocess.getstatusoutput( "meshtastic --ch-set invalid_setting foo --ch-index 0" ) assert re.search(r"Choices in sorted order", out) assert return_value == 0 @pytest.mark.smoke1 def test_smoke1_pos_fields(): """Test --pos-fields (with some values POS_ALTITUDE POS_ALT_MSL POS_BATTERY)""" return_value, out = subprocess.getstatusoutput( "meshtastic --pos-fields POS_ALTITUDE POS_ALT_MSL POS_BATTERY" ) assert re.match(r"Connected to radio", out) assert re.search(r"^Setting position fields to 35", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput("meshtastic --pos-fields") assert re.match(r"Connected to radio", out) assert re.search(r"POS_ALTITUDE", out, re.MULTILINE) assert re.search(r"POS_ALT_MSL", out, re.MULTILINE) assert re.search(r"POS_BATTERY", out, re.MULTILINE) assert return_value == 0 @pytest.mark.smoke1 def test_smoke1_test_with_arg_but_no_hardware(): """Test --test Note: Since only one device is connected, it will not do much. """ return_value, out = subprocess.getstatusoutput("meshtastic --test") assert re.search(r"^Warning: Must have at least two devices", out, re.MULTILINE) assert return_value == 1 @pytest.mark.smoke1 def test_smoke1_debug(): """Test --debug""" return_value, out = subprocess.getstatusoutput("meshtastic --info --debug") assert re.search(r"^Owner", out, re.MULTILINE) assert re.search(r"^DEBUG file", out, re.MULTILINE) assert return_value == 0 @pytest.mark.smoke1 def test_smoke1_seriallog_to_file(): """Test --seriallog to a file creates a file""" filename = "tmpoutput.txt" if os.path.exists(f"{filename}"): os.remove(f"{filename}") return_value, _ = subprocess.getstatusoutput( f"meshtastic --info --seriallog {filename}" ) assert os.path.exists(f"{filename}") assert return_value == 0 os.remove(f"{filename}") @pytest.mark.smoke1 def test_smoke1_qr(): """Test --qr""" filename = "tmpqr" if os.path.exists(f"{filename}"): os.remove(f"{filename}") return_value, _ = subprocess.getstatusoutput(f"meshtastic --qr > {filename}") assert os.path.exists(f"{filename}") # not really testing that a valid qr code is created, just that the file size # is reasonably big enough for a qr code assert os.stat(f"{filename}").st_size > 20000 assert return_value == 0 os.remove(f"{filename}") @pytest.mark.smoke1 def test_smoke1_nodes(): """Test --nodes""" return_value, out = subprocess.getstatusoutput("meshtastic --nodes") assert re.match(r"Connected to radio", out) if platform.system() != "Windows": assert re.search(r" User ", out, re.MULTILINE) assert re.search(r" 1 ", out, re.MULTILINE) assert return_value == 0 @pytest.mark.smoke1 def test_smoke1_send_hello(): """Test --sendtext hello""" return_value, out = subprocess.getstatusoutput("meshtastic --sendtext hello") assert re.match(r"Connected to radio", out) assert re.search(r"^Sending text message hello to \^all", out, re.MULTILINE) assert return_value == 0 @pytest.mark.smoke1 def test_smoke1_port(): """Test --port""" # first, get the ports ports = findPorts(True) # hopefully there is just one assert len(ports) == 1 port = ports[0] return_value, out = subprocess.getstatusoutput(f"meshtastic --port {port} --info") assert re.match(r"Connected to radio", out) assert re.search(r"^Owner", out, re.MULTILINE) assert return_value == 0 @pytest.mark.smoke1 def test_smoke1_set_location_info(): """Test --setlat, --setlon and --setalt""" return_value, out = subprocess.getstatusoutput( "meshtastic --setlat 32.7767 --setlon -96.7970 --setalt 1337" ) assert re.match(r"Connected to radio", out) assert re.search(r"^Fixing altitude", out, re.MULTILINE) assert re.search(r"^Fixing latitude", out, re.MULTILINE) assert re.search(r"^Fixing longitude", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out2 = subprocess.getstatusoutput("meshtastic --info") assert re.search(r"1337", out2, re.MULTILINE) assert re.search(r"32.7767", out2, re.MULTILINE) assert re.search(r"-96.797", out2, re.MULTILINE) assert return_value == 0 @pytest.mark.smoke1 def test_smoke1_set_owner(): """Test --set-owner name""" # make sure the owner is not Joe return_value, out = subprocess.getstatusoutput("meshtastic --set-owner Bob") assert re.match(r"Connected to radio", out) assert re.search(r"^Setting device owner to Bob", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput("meshtastic --info") assert not re.search(r"Owner: Joe", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput("meshtastic --set-owner Joe") assert re.match(r"Connected to radio", out) assert re.search(r"^Setting device owner to Joe", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput("meshtastic --info") assert re.search(r"Owner: Joe", out, re.MULTILINE) assert return_value == 0 @pytest.mark.smoke1 def test_smoke1_ch_set_modem_config(): """Test --ch-set modem_config""" return_value, out = subprocess.getstatusoutput( "meshtastic --ch-set modem_config MedFast" ) assert re.search(r"Warning: Need to specify", out, re.MULTILINE) assert return_value == 1 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput("meshtastic --info") assert not re.search(r"MedFast", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput( "meshtastic --ch-set modem_config MedFast --ch-index 0" ) assert re.match(r"Connected to radio", out) assert re.search(r"^Set modem_config to MedFast", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_REBOOT) return_value, out = subprocess.getstatusoutput("meshtastic --info") assert re.search(r"MedFast", out, re.MULTILINE) assert return_value == 0 @pytest.mark.smoke1 def test_smoke1_ch_values(): """Test --ch-vlongslow --ch-longslow, --ch-longfast, --ch-mediumslow, --ch-mediumsfast, --ch-shortslow, and --ch-shortfast arguments """ exp = { "--ch-vlongslow": '{ "psk": "AQ==" }', "--ch-longslow": "LongSlow", "--ch-longfast": "LongFast", "--ch-medslow": "MedSlow", "--ch-medfast": "MedFast", "--ch-shortslow": "ShortSlow", "--ch-shortfast": "ShortFast", } for key, val in exp.items(): print(key, val) return_value, out = subprocess.getstatusoutput(f"meshtastic {key}") assert re.match(r"Connected to radio", out) assert re.search(r"Writing modified channels to device", out, re.MULTILINE) assert return_value == 0 # pause for the radio (might reboot) time.sleep(PAUSE_AFTER_REBOOT) return_value, out = subprocess.getstatusoutput("meshtastic --info") assert re.search(val, out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) @pytest.mark.smoke1 def test_smoke1_ch_set_name(): """Test --ch-set name""" return_value, out = subprocess.getstatusoutput("meshtastic --info") assert not re.search(r"MyChannel", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput("meshtastic --ch-set name MyChannel") assert re.match(r"Connected to radio", out) assert re.search(r"Warning: Need to specify", out, re.MULTILINE) assert return_value == 1 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput( "meshtastic --ch-set name MyChannel --ch-index 0" ) assert re.match(r"Connected to radio", out) assert re.search(r"^Set name to MyChannel", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput("meshtastic --info") assert re.search(r"MyChannel", out, re.MULTILINE) assert return_value == 0 @pytest.mark.smoke1 def test_smoke1_ch_set_downlink_and_uplink(): """Test -ch-set downlink_enabled X and --ch-set uplink_enabled X""" return_value, out = subprocess.getstatusoutput( "meshtastic --ch-set downlink_enabled false --ch-set uplink_enabled false" ) assert re.match(r"Connected to radio", out) assert re.search(r"Warning: Need to specify", out, re.MULTILINE) assert return_value == 1 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput( "meshtastic --ch-set downlink_enabled false --ch-set uplink_enabled false --ch-index 0" ) assert re.match(r"Connected to radio", out) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput("meshtastic --info") assert not re.search(r"uplinkEnabled", out, re.MULTILINE) assert not re.search(r"downlinkEnabled", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput( "meshtastic --ch-set downlink_enabled true --ch-set uplink_enabled true --ch-index 0" ) assert re.match(r"Connected to radio", out) assert re.search(r"^Set downlink_enabled to true", out, re.MULTILINE) assert re.search(r"^Set uplink_enabled to true", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput("meshtastic --info") assert re.search(r"uplinkEnabled", out, re.MULTILINE) assert re.search(r"downlinkEnabled", out, re.MULTILINE) assert return_value == 0 @pytest.mark.smoke1 def test_smoke1_ch_add_and_ch_del(): """Test --ch-add""" return_value, out = subprocess.getstatusoutput("meshtastic --ch-add testing") assert re.search(r"Writing modified channels to device", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput("meshtastic --info") assert re.match(r"Connected to radio", out) assert re.search(r"SECONDARY", out, re.MULTILINE) assert re.search(r"testing", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput("meshtastic --ch-index 1 --ch-del") assert re.search(r"Deleting channel 1", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_REBOOT) # make sure the secondar channel is not there return_value, out = subprocess.getstatusoutput("meshtastic --info") assert re.match(r"Connected to radio", out) assert not re.search(r"SECONDARY", out, re.MULTILINE) assert not re.search(r"testing", out, re.MULTILINE) assert return_value == 0 @pytest.mark.smoke1 def test_smoke1_ch_enable_and_disable(): """Test --ch-enable and --ch-disable""" return_value, out = subprocess.getstatusoutput("meshtastic --ch-add testing") assert re.search(r"Writing modified channels to device", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput("meshtastic --info") assert re.match(r"Connected to radio", out) assert re.search(r"SECONDARY", out, re.MULTILINE) assert re.search(r"testing", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) # ensure they need to specify a --ch-index return_value, out = subprocess.getstatusoutput("meshtastic --ch-disable") assert return_value == 1 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput( "meshtastic --ch-disable --ch-index 1" ) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput("meshtastic --info") assert re.match(r"Connected to radio", out) assert re.search(r"DISABLED", out, re.MULTILINE) assert re.search(r"testing", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput( "meshtastic --ch-enable --ch-index 1" ) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput("meshtastic --info") assert re.match(r"Connected to radio", out) assert re.search(r"SECONDARY", out, re.MULTILINE) assert re.search(r"testing", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput("meshtastic --ch-del --ch-index 1") assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) @pytest.mark.smoke1 def test_smoke1_ch_del_a_disabled_non_primary_channel(): """Test --ch-del will work on a disabled non-primary channel.""" return_value, out = subprocess.getstatusoutput("meshtastic --ch-add testing") assert re.search(r"Writing modified channels to device", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput("meshtastic --info") assert re.match(r"Connected to radio", out) assert re.search(r"SECONDARY", out, re.MULTILINE) assert re.search(r"testing", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) # ensure they need to specify a --ch-index return_value, out = subprocess.getstatusoutput("meshtastic --ch-disable") assert return_value == 1 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput("meshtastic --ch-del --ch-index 1") assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput("meshtastic --info") assert re.match(r"Connected to radio", out) assert not re.search(r"DISABLED", out, re.MULTILINE) assert not re.search(r"SECONDARY", out, re.MULTILINE) assert not re.search(r"testing", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) @pytest.mark.smoke1 def test_smoke1_attempt_to_delete_primary_channel(): """Test that we cannot delete the PRIMARY channel.""" return_value, out = subprocess.getstatusoutput("meshtastic --ch-del --ch-index 0") assert re.search(r"Warning: Cannot delete primary channel", out, re.MULTILINE) assert return_value == 1 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) @pytest.mark.smoke1 def test_smoke1_attempt_to_disable_primary_channel(): """Test that we cannot disable the PRIMARY channel.""" return_value, out = subprocess.getstatusoutput( "meshtastic --ch-disable --ch-index 0" ) assert re.search(r"Warning: Cannot enable", out, re.MULTILINE) assert return_value == 1 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) @pytest.mark.smoke1 def test_smoke1_attempt_to_enable_primary_channel(): """Test that we cannot enable the PRIMARY channel.""" return_value, out = subprocess.getstatusoutput( "meshtastic --ch-enable --ch-index 0" ) assert re.search(r"Warning: Cannot enable", out, re.MULTILINE) assert return_value == 1 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) @pytest.mark.smoke1 def test_smoke1_ensure_ch_del_second_of_three_channels(): """Test that when we delete the 2nd of 3 channels, that it deletes the correct channel.""" return_value, out = subprocess.getstatusoutput("meshtastic --ch-add testing1") assert re.match(r"Connected to radio", out) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput("meshtastic --info") assert re.match(r"Connected to radio", out) assert re.search(r"SECONDARY", out, re.MULTILINE) assert re.search(r"testing1", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput("meshtastic --ch-add testing2") assert re.match(r"Connected to radio", out) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput("meshtastic --info") assert re.match(r"Connected to radio", out) assert re.search(r"testing2", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput("meshtastic --ch-del --ch-index 1") assert re.match(r"Connected to radio", out) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput("meshtastic --info") assert re.match(r"Connected to radio", out) assert re.search(r"testing2", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput("meshtastic --ch-del --ch-index 1") assert re.match(r"Connected to radio", out) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) @pytest.mark.smoke1 def test_smoke1_ensure_ch_del_third_of_three_channels(): """Test that when we delete the 3rd of 3 channels, that it deletes the correct channel.""" return_value, out = subprocess.getstatusoutput("meshtastic --ch-add testing1") assert re.match(r"Connected to radio", out) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput("meshtastic --info") assert re.match(r"Connected to radio", out) assert re.search(r"SECONDARY", out, re.MULTILINE) assert re.search(r"testing1", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput("meshtastic --ch-add testing2") assert re.match(r"Connected to radio", out) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput("meshtastic --info") assert re.match(r"Connected to radio", out) assert re.search(r"testing2", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput("meshtastic --ch-del --ch-index 2") assert re.match(r"Connected to radio", out) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput("meshtastic --info") assert re.match(r"Connected to radio", out) assert re.search(r"testing1", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput("meshtastic --ch-del --ch-index 1") assert re.match(r"Connected to radio", out) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) @pytest.mark.smoke1 def test_smoke1_seturl_default(): """Test --seturl with default value""" # set some channel value so we no longer have a default channel return_value, out = subprocess.getstatusoutput( "meshtastic --ch-set name foo --ch-index 0" ) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) # ensure we no longer have a default primary channel return_value, out = subprocess.getstatusoutput("meshtastic --info") assert not re.search("CgUYAyIBAQ", out, re.MULTILINE) assert return_value == 0 url = "https://www.meshtastic.org/d/#CgUYAyIBAQ" return_value, out = subprocess.getstatusoutput(f"meshtastic --seturl {url}") assert re.match(r"Connected to radio", out) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput("meshtastic --info") assert re.search("CgUYAyIBAQ", out, re.MULTILINE) assert return_value == 0 @pytest.mark.smoke1 def test_smoke1_seturl_invalid_url(): """Test --seturl with invalid url""" # Note: This url is no longer a valid url. url = "https://www.meshtastic.org/c/#GAMiENTxuzogKQdZ8Lz_q89Oab8qB0RlZmF1bHQ=" return_value, out = subprocess.getstatusoutput(f"meshtastic --seturl {url}") assert re.match(r"Connected to radio", out) assert re.search("Warning: There were no settings", out, re.MULTILINE) assert return_value == 1 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) @pytest.mark.smoke1 def test_smoke1_configure(): """Test --configure""" _, out = subprocess.getstatusoutput(f"meshtastic --configure example_config.yaml") assert re.match(r"Connected to radio", out) assert re.search("^Setting device owner to Bob TBeam", out, re.MULTILINE) assert re.search("^Fixing altitude at 304 meters", out, re.MULTILINE) assert re.search("^Fixing latitude at 35.8", out, re.MULTILINE) assert re.search("^Fixing longitude at -93.8", out, re.MULTILINE) assert re.search("^Setting device position", out, re.MULTILINE) assert re.search("^Set region to 1", out, re.MULTILINE) assert re.search("^Set is_always_powered to true", out, re.MULTILINE) assert re.search("^Set screen_on_secs to 31536000", out, re.MULTILINE) assert re.search("^Set wait_bluetooth_secs to 31536000", out, re.MULTILINE) assert re.search("^Writing modified preferences to device", out, re.MULTILINE) # pause for the radio time.sleep(PAUSE_AFTER_REBOOT) @pytest.mark.smoke1 def test_smoke1_set_ham(): """Test --set-ham Note: Do a factory reset after this setting so it is very short-lived. """ return_value, out = subprocess.getstatusoutput("meshtastic --set-ham KI1234") assert re.search(r"Setting Ham ID", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_REBOOT) return_value, out = subprocess.getstatusoutput("meshtastic --info") assert re.search(r"Owner: KI1234", out, re.MULTILINE) assert return_value == 0 @pytest.mark.smoke1 def test_smoke1_set_wifi_settings(): """Test --set wifi_ssid and --set wifi_password""" return_value, out = subprocess.getstatusoutput( 'meshtastic --set wifi_ssid "some_ssid" --set wifi_password "temp1234"' ) assert re.match(r"Connected to radio", out) assert re.search(r"^Set wifi_ssid to some_ssid", out, re.MULTILINE) assert re.search(r"^Set wifi_password to temp1234", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput( "meshtastic --get wifi_ssid --get wifi_password" ) assert re.search(r"^wifi_ssid: some_ssid", out, re.MULTILINE) assert re.search(r"^wifi_password: sekrit", out, re.MULTILINE) assert return_value == 0 @pytest.mark.smoke1 def test_smoke1_factory_reset(): """Test factory reset""" return_value, out = subprocess.getstatusoutput( "meshtastic --set factory_reset true" ) assert re.match(r"Connected to radio", out) assert re.search(r"^Set factory_reset to true", out, re.MULTILINE) assert re.search(r"^Writing modified preferences to device", out, re.MULTILINE) assert return_value == 0 # NOTE: The radio may not be responsive after this, may need to do a manual reboot # by pressing the button python-2.3.14/meshtastic/tests/test_smoke2.py000066400000000000000000000013251464266072200212530ustar00rootroot00000000000000"""Meshtastic smoke tests with 2 devices connected via USB""" import re import subprocess import pytest @pytest.mark.smoke2 def test_smoke2_info(): """Test --info with 2 devices connected serially""" return_value, out = subprocess.getstatusoutput("meshtastic --info") assert re.search(r"Warning: Multiple", out, re.MULTILINE) assert return_value == 1 @pytest.mark.smoke2 def test_smoke2_test(): """Test --test""" return_value, out = subprocess.getstatusoutput("meshtastic --test") assert re.search(r"Writing serial debugging", out, re.MULTILINE) assert re.search(r"Ports opened", out, re.MULTILINE) assert re.search(r"Running 5 tests", out, re.MULTILINE) assert return_value == 0 python-2.3.14/meshtastic/tests/test_smoke_wifi.py000066400000000000000000000014751464266072200222150ustar00rootroot00000000000000"""Meshtastic smoke tests a device setup with wifi. Need to have run the following on an esp32 device: meshtastic --set wifi_ssid 'foo' --set wifi_password 'sekret' """ import re import subprocess import pytest @pytest.mark.smokewifi def test_smokewifi_info(): """Test --info""" return_value, out = subprocess.getstatusoutput( "meshtastic --info --host meshtastic.local" ) assert re.search(r"^Owner", out, re.MULTILINE) assert re.search(r"^My info", out, re.MULTILINE) assert re.search(r"^Nodes in mesh", out, re.MULTILINE) assert re.search(r"^Preferences", out, re.MULTILINE) assert re.search(r"^Channels", out, re.MULTILINE) assert re.search(r"^ PRIMARY", out, re.MULTILINE) assert re.search(r"^Primary channel URL", out, re.MULTILINE) assert return_value == 0 python-2.3.14/meshtastic/tests/test_smokevirt.py000066400000000000000000000722251464266072200221050ustar00rootroot00000000000000"""Meshtastic smoke tests with a single virtual device via localhost. During the CI build of the Meshtastic-device, a build.zip file is created. Inside that build.zip is a standalone executable meshtasticd_linux_amd64. That linux executable will simulate a Meshtastic device listening on localhost. This smoke test runs against that localhost. """ import os import platform import re import subprocess import time # Do not like using hard coded sleeps, but it probably makes # sense to pause for the radio at appropriate times import pytest from ..util import findPorts # seconds to pause after running a meshtastic command PAUSE_AFTER_COMMAND = 0.1 PAUSE_AFTER_REBOOT = 0.2 # TODO: need to fix the virtual device to have a reboot. When you issue the command # below, you get "FIXME implement reboot for this platform" # @pytest.mark.smokevirt # def test_smokevirt_reboot(): # """Test reboot""" # return_value, _ = subprocess.getstatusoutput('meshtastic --host localhost --reboot') # assert return_value == 0 # # pause for the radio to reset # time.sleep(8) @pytest.mark.smokevirt def test_smokevirt_info(): """Test --info""" return_value, out = subprocess.getstatusoutput("meshtastic --host localhost --info") assert re.match(r"Connected to radio", out) assert re.search(r"^Owner", out, re.MULTILINE) assert re.search(r"^My info", out, re.MULTILINE) assert re.search(r"^Nodes in mesh", out, re.MULTILINE) assert re.search(r"^Preferences", out, re.MULTILINE) assert re.search(r"^Channels", out, re.MULTILINE) assert re.search(r"^ PRIMARY", out, re.MULTILINE) assert re.search(r"^Primary channel URL", out, re.MULTILINE) assert return_value == 0 @pytest.mark.smokevirt def test_get_with_invalid_setting(): """Test '--get a_bad_setting'.""" return_value, out = subprocess.getstatusoutput( "meshtastic --host localhost --get a_bad_setting" ) assert re.search(r"Choices in sorted order", out) assert return_value == 0 @pytest.mark.smokevirt def test_set_with_invalid_setting(): """Test '--set a_bad_setting'.""" return_value, out = subprocess.getstatusoutput( "meshtastic --host localhost --set a_bad_setting foo" ) assert re.search(r"Choices in sorted order", out) assert return_value == 0 @pytest.mark.smokevirt def test_ch_set_with_invalid_settingpatch_find_ports(): """Test '--ch-set with a_bad_setting'.""" return_value, out = subprocess.getstatusoutput( "meshtastic --host localhost --ch-set invalid_setting foo --ch-index 0" ) assert re.search(r"Choices in sorted order", out) assert return_value == 0 @pytest.mark.smokevirt def test_smokevirt_pos_fields(): """Test --pos-fields (with some values POS_ALTITUDE POS_ALT_MSL POS_BATTERY)""" return_value, out = subprocess.getstatusoutput( "meshtastic --host localhost --pos-fields POS_ALTITUDE POS_ALT_MSL POS_BATTERY" ) assert re.match(r"Connected to radio", out) assert re.search(r"^Setting position fields to 35", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput( "meshtastic --host localhost --pos-fields" ) assert re.match(r"Connected to radio", out) assert re.search(r"POS_ALTITUDE", out, re.MULTILINE) assert re.search(r"POS_ALT_MSL", out, re.MULTILINE) assert re.search(r"POS_BATTERY", out, re.MULTILINE) assert return_value == 0 @pytest.mark.smokevirt def test_smokevirt_test_with_arg_but_no_hardware(): """Test --test Note: Since only one device is connected, it will not do much. """ return_value, out = subprocess.getstatusoutput("meshtastic --host localhost --test") assert re.search(r"^Warning: Must have at least two devices", out, re.MULTILINE) assert return_value == 1 @pytest.mark.smokevirt def test_smokevirt_debug(): """Test --debug""" return_value, out = subprocess.getstatusoutput( "meshtastic --host localhost --info --debug" ) assert re.search(r"^Owner", out, re.MULTILINE) assert re.search(r"^DEBUG file", out, re.MULTILINE) assert return_value == 0 @pytest.mark.smokevirt def test_smokevirt_seriallog_to_file(): """Test --seriallog to a file creates a file""" filename = "tmpoutput.txt" if os.path.exists(f"{filename}"): os.remove(f"{filename}") return_value, _ = subprocess.getstatusoutput( f"meshtastic --host localhost --info --seriallog {filename}" ) assert os.path.exists(f"{filename}") assert return_value == 0 os.remove(f"{filename}") @pytest.mark.smokevirt def test_smokevirt_qr(): """Test --qr""" filename = "tmpqr" if os.path.exists(f"{filename}"): os.remove(f"{filename}") return_value, _ = subprocess.getstatusoutput( f"meshtastic --host localhost --qr > {filename}" ) assert os.path.exists(f"{filename}") # not really testing that a valid qr code is created, just that the file size # is reasonably big enough for a qr code assert os.stat(f"{filename}").st_size > 20000 assert return_value == 0 os.remove(f"{filename}") @pytest.mark.smokevirt def test_smokevirt_nodes(): """Test --nodes""" return_value, out = subprocess.getstatusoutput( "meshtastic --host localhost --nodes" ) assert re.match(r"Connected to radio", out) if platform.system() != "Windows": assert re.search(r" User ", out, re.MULTILINE) assert re.search(r" 1 ", out, re.MULTILINE) assert return_value == 0 @pytest.mark.smokevirt def test_smokevirt_send_hello(): """Test --sendtext hello""" return_value, out = subprocess.getstatusoutput( "meshtastic --host localhost --sendtext hello" ) assert re.match(r"Connected to radio", out) assert re.search(r"^Sending text message hello to \^all", out, re.MULTILINE) assert return_value == 0 @pytest.mark.smokevirt def test_smokevirt_port(): """Test --port""" # first, get the ports ports = findPorts() # hopefully there is none assert len(ports) == 0 @pytest.mark.smokevirt def test_smokevirt_set_location_info(): """Test --setlat, --setlon and --setalt""" return_value, out = subprocess.getstatusoutput( "meshtastic --host localhost --setlat 32.7767 --setlon -96.7970 --setalt 1337" ) assert re.match(r"Connected to radio", out) assert re.search(r"^Fixing altitude", out, re.MULTILINE) assert re.search(r"^Fixing latitude", out, re.MULTILINE) assert re.search(r"^Fixing longitude", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out2 = subprocess.getstatusoutput( "meshtastic --host localhost --info" ) assert re.search(r"1337", out2, re.MULTILINE) assert re.search(r"32.7767", out2, re.MULTILINE) assert re.search(r"-96.797", out2, re.MULTILINE) assert return_value == 0 @pytest.mark.smokevirt def test_smokevirt_set_owner(): """Test --set-owner name""" # make sure the owner is not Joe return_value, out = subprocess.getstatusoutput( "meshtastic --host localhost --set-owner Bob" ) assert re.match(r"Connected to radio", out) assert re.search(r"^Setting device owner to Bob", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput("meshtastic --host localhost --info") assert not re.search(r"Owner: Joe", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput( "meshtastic --host localhost --set-owner Joe" ) assert re.match(r"Connected to radio", out) assert re.search(r"^Setting device owner to Joe", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput("meshtastic --host localhost --info") assert re.search(r"Owner: Joe", out, re.MULTILINE) 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": "LongSlow", "--ch-longfast": "LongFast", "--ch-medslow": "MedSlow", "--ch-medfast": "MedFast", "--ch-shortslow": "ShortSlow", "--ch-shortfast": "ShortFast", } for key, val in exp.items(): return_value, out = subprocess.getstatusoutput( f"meshtastic --host localhost {key}" ) assert re.match(r"Connected to radio", out) assert re.search(r"Writing modified channels to device", out, re.MULTILINE) assert return_value == 0 # pause for the radio (might reboot) time.sleep(PAUSE_AFTER_REBOOT) return_value, out = subprocess.getstatusoutput( "meshtastic --host localhost --info" ) assert re.search(val, out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) @pytest.mark.smokevirt def test_smokevirt_ch_set_name(): """Test --ch-set name""" return_value, out = subprocess.getstatusoutput("meshtastic --host localhost --info") assert not re.search(r"MyChannel", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput( "meshtastic --host localhost --ch-set name MyChannel" ) assert re.match(r"Connected to radio", out) assert re.search(r"Warning: Need to specify", out, re.MULTILINE) assert return_value == 1 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput( "meshtastic --host localhost --ch-set name MyChannel --ch-index 0" ) assert re.match(r"Connected to radio", out) assert re.search(r"^Set name to MyChannel", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput("meshtastic --host localhost --info") assert re.search(r"MyChannel", out, re.MULTILINE) assert return_value == 0 @pytest.mark.smokevirt def test_smokevirt_ch_set_downlink_and_uplink(): """Test -ch-set downlink_enabled X and --ch-set uplink_enabled X""" return_value, out = subprocess.getstatusoutput( "meshtastic --host localhost --ch-set downlink_enabled false --ch-set uplink_enabled false" ) assert re.match(r"Connected to radio", out) assert re.search(r"Warning: Need to specify", out, re.MULTILINE) assert return_value == 1 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) # pylint: disable=C0301 return_value, out = subprocess.getstatusoutput( "meshtastic --host localhost --ch-set downlink_enabled false --ch-set uplink_enabled false --ch-index 0" ) assert re.match(r"Connected to radio", out) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput("meshtastic --host localhost --info") assert not re.search(r"uplinkEnabled", out, re.MULTILINE) assert not re.search(r"downlinkEnabled", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) # pylint: disable=C0301 return_value, out = subprocess.getstatusoutput( "meshtastic --host localhost --ch-set downlink_enabled true --ch-set uplink_enabled true --ch-index 0" ) assert re.match(r"Connected to radio", out) assert re.search(r"^Set downlink_enabled to true", out, re.MULTILINE) assert re.search(r"^Set uplink_enabled to true", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput("meshtastic --host localhost --info") assert re.search(r"uplinkEnabled", out, re.MULTILINE) assert re.search(r"downlinkEnabled", out, re.MULTILINE) assert return_value == 0 @pytest.mark.smokevirt def test_smokevirt_ch_add_and_ch_del(): """Test --ch-add""" return_value, out = subprocess.getstatusoutput( "meshtastic --host localhost --ch-index 1 --ch-del" ) assert re.search(r"Deleting channel 1", out, re.MULTILINE) 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 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput("meshtastic --host localhost --info") assert re.match(r"Connected to radio", out) assert re.search(r"SECONDARY", out, re.MULTILINE) assert re.search(r"testing", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput( "meshtastic --host localhost --ch-index 1 --ch-del" ) assert re.search(r"Deleting channel 1", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_REBOOT) # make sure the secondary channel is not there return_value, out = subprocess.getstatusoutput("meshtastic --host localhost --info") assert re.match(r"Connected to radio", out) assert not re.search(r"SECONDARY", out, re.MULTILINE) assert not re.search(r"testing", out, re.MULTILINE) assert return_value == 0 @pytest.mark.smokevirt def test_smokevirt_ch_enable_and_disable(): """Test --ch-enable and --ch-disable""" return_value, out = subprocess.getstatusoutput( "meshtastic --host localhost --ch-index 1 --ch-del" ) assert re.search(r"Deleting channel 1", out, re.MULTILINE) 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 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput("meshtastic --host localhost --info") assert re.match(r"Connected to radio", out) assert re.search(r"SECONDARY", out, re.MULTILINE) assert re.search(r"testing", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) # ensure they need to specify a --ch-index return_value, out = subprocess.getstatusoutput( "meshtastic --host localhost --ch-disable" ) assert return_value == 1 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput( "meshtastic --host localhost --ch-disable --ch-index 1" ) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput("meshtastic --host localhost --info") assert re.match(r"Connected to radio", out) assert re.search(r"DISABLED", out, re.MULTILINE) assert re.search(r"testing", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput( "meshtastic --host localhost --ch-enable --ch-index 1" ) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput("meshtastic --host localhost --info") assert re.match(r"Connected to radio", out) assert re.search(r"SECONDARY", out, re.MULTILINE) assert re.search(r"testing", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput( "meshtastic --host localhost --ch-del --ch-index 1" ) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) @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 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput("meshtastic --host localhost --info") assert re.match(r"Connected to radio", out) assert re.search(r"SECONDARY", out, re.MULTILINE) assert re.search(r"testing", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) # ensure they need to specify a --ch-index return_value, out = subprocess.getstatusoutput( "meshtastic --host localhost --ch-disable" ) assert return_value == 1 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput( "meshtastic --host localhost --ch-del --ch-index 1" ) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput("meshtastic --host localhost --info") assert re.match(r"Connected to radio", out) assert not re.search(r"DISABLED", out, re.MULTILINE) assert not re.search(r"SECONDARY", out, re.MULTILINE) assert not re.search(r"testing", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) @pytest.mark.smokevirt def test_smokevirt_attempt_to_delete_primary_channel(): """Test that we cannot delete the PRIMARY channel.""" return_value, out = subprocess.getstatusoutput( "meshtastic --host localhost --ch-del --ch-index 0" ) assert re.search(r"Warning: Cannot delete primary channel", out, re.MULTILINE) assert return_value == 1 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) @pytest.mark.smokevirt def test_smokevirt_attempt_to_disable_primary_channel(): """Test that we cannot disable the PRIMARY channel.""" return_value, out = subprocess.getstatusoutput( "meshtastic --host localhost --ch-disable --ch-index 0" ) assert re.search(r"Warning: Cannot enable", out, re.MULTILINE) assert return_value == 1 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) @pytest.mark.smokevirt def test_smokevirt_attempt_to_enable_primary_channel(): """Test that we cannot enable the PRIMARY channel.""" return_value, out = subprocess.getstatusoutput( "meshtastic --host localhost --ch-enable --ch-index 0" ) assert re.search(r"Warning: Cannot enable", out, re.MULTILINE) assert return_value == 1 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) @pytest.mark.smokevirt def test_smokevirt_ensure_ch_del_second_of_three_channels(): """Test that when we delete the 2nd of 3 channels, that it deletes the correct channel.""" return_value, out = subprocess.getstatusoutput( "meshtastic --host localhost --ch-add testing1" ) assert re.match(r"Connected to radio", out) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput("meshtastic --host localhost --info") assert re.match(r"Connected to radio", out) assert re.search(r"SECONDARY", out, re.MULTILINE) assert re.search(r"testing1", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput( "meshtastic --host localhost --ch-add testing2" ) assert re.match(r"Connected to radio", out) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput("meshtastic --host localhost --info") assert re.match(r"Connected to radio", out) assert re.search(r"testing2", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput( "meshtastic --host localhost --ch-del --ch-index 1" ) assert re.match(r"Connected to radio", out) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput("meshtastic --host localhost --info") assert re.match(r"Connected to radio", out) assert re.search(r"testing2", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput( "meshtastic --host localhost --ch-del --ch-index 1" ) assert re.match(r"Connected to radio", out) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) @pytest.mark.smokevirt def test_smokevirt_ensure_ch_del_third_of_three_channels(): """Test that when we delete the 3rd of 3 channels, that it deletes the correct channel.""" return_value, out = subprocess.getstatusoutput( "meshtastic --host localhost --ch-add testing1" ) assert re.match(r"Connected to radio", out) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput("meshtastic --host localhost --info") assert re.match(r"Connected to radio", out) assert re.search(r"SECONDARY", out, re.MULTILINE) assert re.search(r"testing1", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput( "meshtastic --host localhost --ch-add testing2" ) assert re.match(r"Connected to radio", out) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput("meshtastic --host localhost --info") assert re.match(r"Connected to radio", out) assert re.search(r"testing2", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput( "meshtastic --host localhost --ch-del --ch-index 2" ) assert re.match(r"Connected to radio", out) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput("meshtastic --host localhost --info") assert re.match(r"Connected to radio", out) assert re.search(r"testing1", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput( "meshtastic --host localhost --ch-del --ch-index 1" ) assert re.match(r"Connected to radio", out) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) @pytest.mark.smokevirt def test_smokevirt_ch_set_modem_config(): """Test --ch-set modem_config""" return_value, out = subprocess.getstatusoutput( "meshtastic --host localhost --ch-set modem_config Bw31_25Cr48Sf512" ) assert re.search(r"Warning: Need to specify", out, re.MULTILINE) assert return_value == 1 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput("meshtastic --host localhost --info") assert not re.search(r"Bw31_25Cr48Sf512", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput( "meshtastic --host localhost --ch-set modem_config MidSlow --ch-index 0" ) assert re.match(r"Connected to radio", out) assert re.search(r"^Set modem_config to MidSlow", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput("meshtastic --host localhost --info") assert re.search(r"MidSlow", out, re.MULTILINE) assert return_value == 0 @pytest.mark.smokevirt def test_smokevirt_seturl_default(): """Test --seturl with default value""" # set some channel value so we no longer have a default channel return_value, out = subprocess.getstatusoutput( "meshtastic --host localhost --ch-set name foo --ch-index 0" ) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) # ensure we no longer have a default primary channel return_value, out = subprocess.getstatusoutput("meshtastic --host localhost --info") assert not re.search("CgUYAyIBAQ", out, re.MULTILINE) assert return_value == 0 url = "https://www.meshtastic.org/d/#CgUYAyIBAQ" return_value, out = subprocess.getstatusoutput( f"meshtastic --host localhost --seturl {url}" ) assert re.match(r"Connected to radio", out) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput("meshtastic --host localhost --info") assert re.search("CgUYAyIBAQ", out, re.MULTILINE) assert return_value == 0 @pytest.mark.smokevirt def test_smokevirt_seturl_invalid_url(): """Test --seturl with invalid url""" # Note: This url is no longer a valid url. url = "https://www.meshtastic.org/c/#GAMiENTxuzogKQdZ8Lz_q89Oab8qB0RlZmF1bHQ=" return_value, out = subprocess.getstatusoutput( f"meshtastic --host localhost --seturl {url}" ) assert re.match(r"Connected to radio", out) assert re.search("Warning: There were no settings", out, re.MULTILINE) assert return_value == 1 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) @pytest.mark.smokevirt def test_smokevirt_configure(): """Test --configure""" _, out = subprocess.getstatusoutput( f"meshtastic --host localhost --configure example_config.yaml" ) assert re.match(r"Connected to radio", out) assert re.search("^Setting device owner to Bob TBeam", out, re.MULTILINE) assert re.search("^Fixing altitude at 304 meters", out, re.MULTILINE) assert re.search("^Fixing latitude at 35.8", out, re.MULTILINE) assert re.search("^Fixing longitude at -93.8", out, re.MULTILINE) assert re.search("^Setting device position", out, re.MULTILINE) assert re.search("^Set region to 1", out, re.MULTILINE) assert re.search("^Set is_always_powered to true", out, re.MULTILINE) assert re.search("^Set send_owner_interval to 2", out, re.MULTILINE) assert re.search("^Set screen_on_secs to 31536000", out, re.MULTILINE) assert re.search("^Set wait_bluetooth_secs to 31536000", out, re.MULTILINE) assert re.search("^Writing modified preferences to device", out, re.MULTILINE) # pause for the radio time.sleep(PAUSE_AFTER_REBOOT) @pytest.mark.smokevirt def test_smokevirt_set_ham(): """Test --set-ham Note: Do a factory reset after this setting so it is very short-lived. """ return_value, out = subprocess.getstatusoutput( "meshtastic --host localhost --set-ham KI1234" ) assert re.search(r"Setting Ham ID", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_REBOOT) return_value, out = subprocess.getstatusoutput("meshtastic --host localhost --info") assert re.search(r"Owner: KI1234", out, re.MULTILINE) assert return_value == 0 @pytest.mark.smokevirt def test_smokevirt_set_wifi_settings(): """Test --set wifi_ssid and --set wifi_password""" return_value, out = subprocess.getstatusoutput( 'meshtastic --host localhost --set wifi_ssid "some_ssid" --set wifi_password "temp1234"' ) assert re.match(r"Connected to radio", out) assert re.search(r"^Set wifi_ssid to some_ssid", out, re.MULTILINE) assert re.search(r"^Set wifi_password to temp1234", out, re.MULTILINE) assert return_value == 0 # pause for the radio time.sleep(PAUSE_AFTER_COMMAND) return_value, out = subprocess.getstatusoutput( "meshtastic --host localhost --get wifi_ssid --get wifi_password" ) assert re.search(r"^wifi_ssid: some_ssid", out, re.MULTILINE) assert re.search(r"^wifi_password: sekrit", out, re.MULTILINE) assert return_value == 0 @pytest.mark.smokevirt def test_smokevirt_factory_reset(): """Test factory reset""" return_value, out = subprocess.getstatusoutput( "meshtastic --host localhost --set factory_reset true" ) assert re.match(r"Connected to radio", out) assert re.search(r"^Set factory_reset to true", out, re.MULTILINE) assert re.search(r"^Writing modified preferences to device", out, re.MULTILINE) assert return_value == 0 # NOTE: The virtual radio will not respond well after this command. Need to re-start the virtual program at this point. # TODO: fix? python-2.3.14/meshtastic/tests/test_stream_interface.py000066400000000000000000000074451464266072200233770ustar00rootroot00000000000000"""Meshtastic unit tests for stream_interface.py""" import logging from unittest.mock import MagicMock import pytest from ..stream_interface import StreamInterface # import re @pytest.mark.unit def test_StreamInterface(): """Test that we cannot instantiate a StreamInterface based on noProto""" with pytest.raises(Exception) as pytest_wrapped_e: StreamInterface() assert pytest_wrapped_e.type == Exception # Note: This takes a bit, so moving from unit to slow @pytest.mark.unitslow @pytest.mark.usefixtures("reset_mt_config") def test_StreamInterface_with_noProto(caplog): """Test that we can instantiate a StreamInterface based on nonProto and we can read/write bytes from a mocked stream """ stream = MagicMock() test_data = b"hello" stream.read.return_value = test_data with caplog.at_level(logging.DEBUG): iface = StreamInterface(noProto=True, connectNow=False) iface.stream = stream iface._writeBytes(test_data) data = iface._readBytes(len(test_data)) assert data == test_data # 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_mt_config") # 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) python-2.3.14/meshtastic/tests/test_tcp_interface.py000066400000000000000000000035441464266072200226660ustar00rootroot00000000000000"""Meshtastic unit tests for tcp_interface.py""" import re from unittest.mock import patch import pytest from ..protobuf import config_pb2 from ..tcp_interface import TCPInterface @pytest.mark.unit def test_TCPInterface(capsys): """Test that we can instantiate a TCPInterface""" with patch("socket.socket") as mock_socket: iface = TCPInterface(hostname="localhost", noProto=True) iface.localNode.localConfig.lora.CopyFrom(config_pb2.Config.LoRaConfig()) iface.myConnect() iface.showInfo() iface.localNode.showInfo() out, err = capsys.readouterr() assert re.search(r"Owner: None \(None\)", out, re.MULTILINE) assert re.search(r"Nodes", out, re.MULTILINE) assert re.search(r"Preferences", out, re.MULTILINE) assert re.search(r"Channels", out, re.MULTILINE) assert re.search(r"Primary channel URL", out, re.MULTILINE) assert err == "" assert mock_socket.called iface.close() @pytest.mark.unit def test_TCPInterface_exception(): """Test that we can instantiate a TCPInterface""" def throw_an_exception(): raise ValueError("Fake exception.") with patch( "meshtastic.tcp_interface.TCPInterface._socket_shutdown" ) as mock_shutdown: mock_shutdown.side_effect = throw_an_exception with patch("socket.socket") as mock_socket: iface = TCPInterface(hostname="localhost", noProto=True) iface.myConnect() iface.close() assert mock_socket.called assert mock_shutdown.called @pytest.mark.unit def test_TCPInterface_without_connecting(): """Test that we can instantiate a TCPInterface with connectNow as false""" with patch("socket.socket"): iface = TCPInterface(hostname="localhost", noProto=True, connectNow=False) assert iface.socket is None python-2.3.14/meshtastic/tests/test_tunnel.py000066400000000000000000000227211464266072200213630ustar00rootroot00000000000000"""Meshtastic unit tests for tunnel.py""" import logging import re import sys from unittest.mock import MagicMock, patch import pytest from meshtastic import mt_config from ..tcp_interface import TCPInterface from ..tunnel import Tunnel, onTunnelReceive @pytest.mark.unit @patch("platform.system") def test_Tunnel_on_non_linux_system(mock_platform_system): """Test that we cannot instantiate a Tunnel on a non Linux system""" a_mock = MagicMock() a_mock.return_value = "notLinux" mock_platform_system.side_effect = a_mock with patch("socket.socket") as mock_socket: with pytest.raises(Tunnel.TunnelError) as pytest_wrapped_e: iface = TCPInterface(hostname="localhost", noProto=True) Tunnel(iface) assert pytest_wrapped_e.type == Tunnel.TunnelError assert mock_socket.called @pytest.mark.unit @patch("platform.system") def test_Tunnel_without_interface(mock_platform_system): """Test that we can not instantiate a Tunnel without a valid interface""" a_mock = MagicMock() a_mock.return_value = "Linux" mock_platform_system.side_effect = a_mock with pytest.raises(Tunnel.TunnelError) as pytest_wrapped_e: Tunnel(None) assert pytest_wrapped_e.type == Tunnel.TunnelError @pytest.mark.unitslow @patch("platform.system") def test_Tunnel_with_interface(mock_platform_system, caplog, iface_with_nodes): """Test that we can not instantiate a Tunnel without a valid interface""" iface = iface_with_nodes iface.myInfo.my_node_num = 2475227164 a_mock = MagicMock() a_mock.return_value = "Linux" mock_platform_system.side_effect = a_mock with caplog.at_level(logging.WARNING): with patch("socket.socket"): tun = Tunnel(iface) assert tun == mt_config.tunnelInstance iface.close() assert re.search(r"Not creating a TapDevice()", caplog.text, re.MULTILINE) assert re.search(r"Not starting TUN reader", caplog.text, re.MULTILINE) assert re.search(r"Not sending packet", caplog.text, re.MULTILINE) @pytest.mark.unitslow @patch("platform.system") def test_onTunnelReceive_from_ourselves(mock_platform_system, caplog, iface_with_nodes): """Test onTunnelReceive""" iface = iface_with_nodes iface.myInfo.my_node_num = 2475227164 sys.argv = [""] mt_config.args = sys.argv packet = {"decoded": {"payload": "foo"}, "from": 2475227164} a_mock = MagicMock() a_mock.return_value = "Linux" mock_platform_system.side_effect = a_mock with caplog.at_level(logging.DEBUG): with patch("socket.socket"): tun = Tunnel(iface) mt_config.tunnelInstance = tun onTunnelReceive(packet, iface) assert re.search(r"in onTunnelReceive", caplog.text, re.MULTILINE) assert re.search(r"Ignoring message we sent", caplog.text, re.MULTILINE) @pytest.mark.unit @patch("platform.system") def test_onTunnelReceive_from_someone_else( mock_platform_system, caplog, iface_with_nodes ): """Test onTunnelReceive""" iface = iface_with_nodes iface.myInfo.my_node_num = 2475227164 sys.argv = [""] mt_config.args = sys.argv packet = {"decoded": {"payload": "foo"}, "from": 123} a_mock = MagicMock() a_mock.return_value = "Linux" mock_platform_system.side_effect = a_mock with caplog.at_level(logging.DEBUG): with patch("socket.socket"): tun = Tunnel(iface) mt_config.tunnelInstance = tun onTunnelReceive(packet, iface) assert re.search(r"in onTunnelReceive", caplog.text, re.MULTILINE) @pytest.mark.unitslow @patch("platform.system") def test_shouldFilterPacket_random(mock_platform_system, caplog, iface_with_nodes): """Test _shouldFilterPacket()""" iface = iface_with_nodes iface.noProto = True # random packet packet = b"1234567890123456789012345678901234567890" a_mock = MagicMock() a_mock.return_value = "Linux" mock_platform_system.side_effect = a_mock with caplog.at_level(logging.DEBUG): with patch("socket.socket"): tun = Tunnel(iface) ignore = tun._shouldFilterPacket(packet) assert not ignore @pytest.mark.unitslow @patch("platform.system") def test_shouldFilterPacket_in_blacklist( mock_platform_system, caplog, iface_with_nodes ): """Test _shouldFilterPacket()""" iface = iface_with_nodes iface.noProto = True # faked IGMP packet = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" a_mock = MagicMock() a_mock.return_value = "Linux" mock_platform_system.side_effect = a_mock with caplog.at_level(logging.DEBUG): with patch("socket.socket"): tun = Tunnel(iface) ignore = tun._shouldFilterPacket(packet) assert ignore @pytest.mark.unitslow @patch("platform.system") def test_shouldFilterPacket_icmp(mock_platform_system, caplog, iface_with_nodes): """Test _shouldFilterPacket()""" iface = iface_with_nodes iface.noProto = True # faked ICMP packet = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" a_mock = MagicMock() a_mock.return_value = "Linux" mock_platform_system.side_effect = a_mock with caplog.at_level(logging.DEBUG): with patch("socket.socket"): tun = Tunnel(iface) ignore = tun._shouldFilterPacket(packet) assert re.search(r"forwarding ICMP message", caplog.text, re.MULTILINE) assert not ignore @pytest.mark.unit @patch("platform.system") def test_shouldFilterPacket_udp(mock_platform_system, caplog, iface_with_nodes): """Test _shouldFilterPacket()""" iface = iface_with_nodes iface.noProto = True # faked UDP packet = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" a_mock = MagicMock() a_mock.return_value = "Linux" mock_platform_system.side_effect = a_mock with caplog.at_level(logging.DEBUG): with patch("socket.socket"): tun = Tunnel(iface) ignore = tun._shouldFilterPacket(packet) assert re.search(r"forwarding udp", caplog.text, re.MULTILINE) assert not ignore @pytest.mark.unitslow @patch("platform.system") def test_shouldFilterPacket_udp_blacklisted( mock_platform_system, caplog, iface_with_nodes ): """Test _shouldFilterPacket()""" iface = iface_with_nodes iface.noProto = True # faked UDP packet = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x07\x6c\x07\x6c\x00\x00\x00" a_mock = MagicMock() a_mock.return_value = "Linux" mock_platform_system.side_effect = a_mock # Note: custom logging level LOG_TRACE = 5 with caplog.at_level(LOG_TRACE): with patch("socket.socket"): tun = Tunnel(iface) ignore = tun._shouldFilterPacket(packet) assert re.search(r"ignoring blacklisted UDP", caplog.text, re.MULTILINE) assert ignore @pytest.mark.unit @patch("platform.system") def test_shouldFilterPacket_tcp(mock_platform_system, caplog, iface_with_nodes): """Test _shouldFilterPacket()""" iface = iface_with_nodes iface.noProto = True # faked TCP packet = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" a_mock = MagicMock() a_mock.return_value = "Linux" mock_platform_system.side_effect = a_mock with caplog.at_level(logging.DEBUG): with patch("socket.socket"): tun = Tunnel(iface) ignore = tun._shouldFilterPacket(packet) assert re.search(r"forwarding tcp", caplog.text, re.MULTILINE) assert not ignore @pytest.mark.unitslow @patch("platform.system") def test_shouldFilterPacket_tcp_blacklisted( mock_platform_system, caplog, iface_with_nodes ): """Test _shouldFilterPacket()""" iface = iface_with_nodes iface.noProto = True # faked TCP packet = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x17\x0c\x17\x0c\x00\x00\x00" a_mock = MagicMock() a_mock.return_value = "Linux" mock_platform_system.side_effect = a_mock # Note: custom logging level LOG_TRACE = 5 with caplog.at_level(LOG_TRACE): with patch("socket.socket"): tun = Tunnel(iface) ignore = tun._shouldFilterPacket(packet) assert re.search(r"ignoring blacklisted TCP", caplog.text, re.MULTILINE) assert ignore @pytest.mark.unitslow @patch("platform.system") def test_ipToNodeId_none(mock_platform_system, caplog, iface_with_nodes): """Test _ipToNodeId()""" iface = iface_with_nodes iface.noProto = True a_mock = MagicMock() a_mock.return_value = "Linux" mock_platform_system.side_effect = a_mock with caplog.at_level(logging.DEBUG): with patch("socket.socket"): tun = Tunnel(iface) nodeid = tun._ipToNodeId("something not useful") assert nodeid is None @pytest.mark.unitslow @patch("platform.system") def test_ipToNodeId_all(mock_platform_system, caplog, iface_with_nodes): """Test _ipToNodeId()""" iface = iface_with_nodes iface.noProto = True a_mock = MagicMock() a_mock.return_value = "Linux" mock_platform_system.side_effect = a_mock with caplog.at_level(logging.DEBUG): with patch("socket.socket"): tun = Tunnel(iface) nodeid = tun._ipToNodeId(b"\x00\x00\xff\xff") assert nodeid == "^all" python-2.3.14/meshtastic/tests/test_util.py000066400000000000000000000443501464266072200210350ustar00rootroot00000000000000"""Meshtastic unit tests for util.py""" import json import logging import re from unittest.mock import patch import pytest from hypothesis import given, strategies as st from meshtastic.supported_device import SupportedDevice from meshtastic.protobuf import mesh_pb2 from meshtastic.util import ( Timeout, active_ports_on_supported_devices, camel_to_snake, catchAndIgnore, convert_mac_addr, eliminate_duplicate_port, findPorts, fixme, fromPSK, fromStr, genPSK256, hexstr, ipstr, is_windows11, our_exit, pskToString, quoteBooleans, readnet_u16, remove_keys_from_dict, snake_to_camel, stripnl, support_info, message_to_json, Acknowledgment ) @pytest.mark.unit def test_genPSK256(): """Test genPSK256""" assert genPSK256() != "" @pytest.mark.unit def test_fromStr(): """Test fromStr""" assert fromStr("") == b"" assert fromStr("0x12") == b"\x12" assert fromStr("t") assert fromStr("T") assert fromStr("true") assert fromStr("True") assert fromStr("yes") assert fromStr("Yes") assert fromStr("f") is False assert fromStr("F") is False assert fromStr("false") is False assert fromStr("False") is False assert fromStr("no") is False assert fromStr("No") is False assert fromStr("100.01") == 100.01 assert fromStr("123") == 123 assert fromStr("abc") == "abc" assert fromStr("123456789") == 123456789 assert fromStr("base64:Zm9vIGJhciBiYXo=") == b"foo bar baz" @pytest.mark.unitslow def test_quoteBooleans(): """Test quoteBooleans""" assert quoteBooleans("") == "" assert quoteBooleans("foo") == "foo" assert quoteBooleans("true") == "true" assert quoteBooleans("false") == "false" assert quoteBooleans(": true") == ": 'true'" assert quoteBooleans(": false") == ": 'false'" @pytest.mark.unit def test_fromPSK(): """Test fromPSK""" assert fromPSK("random") != "" assert fromPSK("none") == b"\x00" assert fromPSK("default") == b"\x01" assert fromPSK("simple22") == b"\x17" assert fromPSK("trash") == "trash" @pytest.mark.unit def test_stripnl(): """Test stripnl""" assert stripnl("") == "" assert stripnl("a\n") == "a" assert stripnl(" a \n ") == "a" assert stripnl("a\nb") == "a b" @pytest.mark.unit def test_pskToString_empty_string(): """Test pskToString empty string""" assert pskToString("") == "unencrypted" @pytest.mark.unit def test_pskToString_string(): """Test pskToString string""" assert pskToString("hunter123") == "secret" @pytest.mark.unit def test_pskToString_one_byte_zero_value(): """Test pskToString one byte that is value of 0""" assert pskToString(bytes([0x00])) == "unencrypted" @pytest.mark.unitslow def test_pskToString_one_byte_non_zero_value(): """Test pskToString one byte that is non-zero""" assert pskToString(bytes([0x01])) == "default" @pytest.mark.unitslow def test_pskToString_many_bytes(): """Test pskToString many bytes""" assert pskToString(bytes([0x02, 0x01])) == "secret" @pytest.mark.unit def test_pskToString_simple(): """Test pskToString simple""" assert pskToString(bytes([0x03])) == "simple2" @pytest.mark.unitslow def test_our_exit_zero_return_value(capsys): """Test our_exit with a zero return value""" with pytest.raises(SystemExit) as pytest_wrapped_e: our_exit("Warning: Some message", 0) out, err = capsys.readouterr() assert re.search(r"Warning: Some message", out, re.MULTILINE) assert err == "" assert pytest_wrapped_e.type == SystemExit assert pytest_wrapped_e.value.code == 0 @pytest.mark.unitslow def test_our_exit_non_zero_return_value(capsys): """Test our_exit with a non-zero return value""" with pytest.raises(SystemExit) as pytest_wrapped_e: our_exit("Error: Some message", 1) out, err = capsys.readouterr() assert re.search(r"Error: Some message", out, re.MULTILINE) assert err == "" assert pytest_wrapped_e.type == SystemExit assert pytest_wrapped_e.value.code == 1 @pytest.mark.unitslow def test_fixme(): """Test fixme()""" with pytest.raises(Exception) as pytest_wrapped_e: fixme("some exception") assert pytest_wrapped_e.type == Exception @pytest.mark.unit def test_support_info(capsys): """Test support_info""" support_info() out, err = capsys.readouterr() assert re.search(r"System", out, re.MULTILINE) assert re.search(r"Platform", out, re.MULTILINE) assert re.search(r"Machine", out, re.MULTILINE) assert re.search(r"Executable", out, re.MULTILINE) assert err == "" @pytest.mark.unit def test_catchAndIgnore(caplog): """Test catchAndIgnore() does not actually throw an exception, but just logs""" def some_closure(): raise Exception("foo") # pylint: disable=W0719 with caplog.at_level(logging.DEBUG): catchAndIgnore("something", some_closure) assert re.search(r"Exception thrown in something", caplog.text, re.MULTILINE) @pytest.mark.unitslow def test_remove_keys_from_dict_empty_keys_empty_dict(): """Test when keys and dict both are empty""" assert not remove_keys_from_dict((), {}) @pytest.mark.unitslow def test_remove_keys_from_dict_empty_dict(): """Test when dict is empty""" assert not remove_keys_from_dict(("a"), {}) @pytest.mark.unit def test_remove_keys_from_dict_empty_keys(): """Test when keys is empty""" assert remove_keys_from_dict((), {"a": 1}) == {"a": 1} @pytest.mark.unitslow def test_remove_keys_from_dict(): """Test remove_keys_from_dict()""" assert remove_keys_from_dict(("b"), {"a": 1, "b": 2}) == {"a": 1} @pytest.mark.unitslow def test_remove_keys_from_dict_multiple_keys(): """Test remove_keys_from_dict()""" keys = ("a", "b") adict = {"a": 1, "b": 2, "c": 3} assert remove_keys_from_dict(keys, adict) == {"c": 3} @pytest.mark.unit def test_remove_keys_from_dict_nested(): """Test remove_keys_from_dict()""" keys = "b" adict = {"a": {"b": 1}, "b": 2, "c": 3} exp = {"a": {}, "c": 3} assert remove_keys_from_dict(keys, adict) == exp @pytest.mark.unitslow def test_Timeout_not_found(): """Test Timeout()""" to = Timeout(0.2) attrs = "foo" to.waitForSet("bar", attrs) @pytest.mark.unitslow def test_Timeout_found(): """Test Timeout()""" to = Timeout(0.2) attrs = () to.waitForSet("bar", attrs) @pytest.mark.unitslow def test_hexstr(): """Test hexstr()""" assert hexstr(b"123") == "31:32:33" assert hexstr(b"") == "" @pytest.mark.unitslow def test_ipstr(): """Test ipstr()""" assert ipstr(b"1234") == "49.50.51.52" assert ipstr(b"") == "" @pytest.mark.unitslow def test_readnet_u16(): """Test readnet_u16()""" assert readnet_u16(b"123456", 2) == 13108 @pytest.mark.unitslow @patch("serial.tools.list_ports.comports", return_value=[]) def test_findPorts_when_none_found(patch_comports): """Test findPorts()""" assert not findPorts() 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()""" assert convert_mac_addr("/c0gFyhb") == "fd:cd:20:17:28:5b" assert convert_mac_addr("fd:cd:20:17:28:5b") == "fd:cd:20:17:28:5b" assert convert_mac_addr("") == "" @pytest.mark.unit def test_snake_to_camel(): """Test snake_to_camel""" assert snake_to_camel("") == "" assert snake_to_camel("foo") == "foo" assert snake_to_camel("foo_bar") == "fooBar" assert snake_to_camel("fooBar") == "fooBar" @pytest.mark.unit def test_camel_to_snake(): """Test camel_to_snake""" assert camel_to_snake("") == "" assert camel_to_snake("foo") == "foo" assert camel_to_snake("Foo") == "foo" assert camel_to_snake("fooBar") == "foo_bar" assert camel_to_snake("fooBarBaz") == "foo_bar_baz" @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() @pytest.mark.unit def test_message_to_json_shows_all(): """Test that message_to_json prints fields that aren't included in data passed in""" actual = json.loads(message_to_json(mesh_pb2.MyNodeInfo())) expected = { "myNodeNum": 0, "rebootCount": 0, "minAppVersion": 0 } assert actual == expected @pytest.mark.unit def test_acknowledgement_reset(): """ Test that the reset method can set all fields back to False """ test_ack_obj = Acknowledgment() # everything's set to False; let's set it to True to get a good test test_ack_obj.receivedAck = True test_ack_obj.receivedNak = True test_ack_obj.receivedImplAck = True test_ack_obj.receivedTraceRoute = True test_ack_obj.receivedTelemetry = True test_ack_obj.receivedPosition = True test_ack_obj.reset() assert test_ack_obj.receivedAck is False assert test_ack_obj.receivedNak is False assert test_ack_obj.receivedImplAck is False assert test_ack_obj.receivedTraceRoute is False assert test_ack_obj.receivedTelemetry is False assert test_ack_obj.receivedPosition is False @given(a_string=st.text( alphabet=st.characters( codec='ascii', min_codepoint=0x5F, max_codepoint=0x7A, exclude_characters=r'`', )).filter( lambda x: x not in [''] and x[0] not in "_" and x[-1] not in '_' and not re.search(r'__', x) )) def test_roundtrip_snake_to_camel_camel_to_snake(a_string): """Test that snake_to_camel and camel_to_snake roundtrip each other""" value0 = snake_to_camel(a_string=a_string) value1 = camel_to_snake(a_string=value0) assert a_string == value1, (a_string, value1) python-2.3.14/meshtastic/tunnel.py000066400000000000000000000207541464266072200171660ustar00rootroot00000000000000"""Code for IP tunnel over a mesh # Note python-pytuntap was too buggy # using pip3 install pytap2 # make sure to "sudo setcap cap_net_admin+eip /usr/bin/python3.8" so python can access tun device without being root # sudo ip tuntap del mode tun tun0 # sudo bin/run.sh --port /dev/ttyUSB0 --setch-shortfast # sudo bin/run.sh --port /dev/ttyUSB0 --tunnel --debug # ssh -Y root@192.168.10.151 (or dietpi), default password p # ncat -e /bin/cat -k -u -l 1235 # ncat -u 10.115.64.152 1235 # ping -c 1 -W 20 10.115.64.152 # ping -i 30 -W 30 10.115.64.152 # FIXME: use a more optimal MTU """ import logging import platform import threading from pubsub import pub # type: ignore[import-untyped] from pytap2 import TapDevice from meshtastic.protobuf import portnums_pb2 from meshtastic import mt_config from meshtastic.util import ipstr, readnet_u16 def onTunnelReceive(packet, interface): # pylint: disable=W0613 """Callback for received tunneled messages from mesh.""" logging.debug(f"in onTunnelReceive()") tunnelInstance = mt_config.tunnelInstance tunnelInstance.onReceive(packet) class Tunnel: """A TUN based IP tunnel over meshtastic""" class TunnelError(Exception): """An exception class for general tunnel errors""" def __init__(self, message): self.message = message super().__init__(self.message) def __init__(self, iface, subnet="10.115", netmask="255.255.0.0"): """ Constructor iface is the already open MeshInterface instance subnet is used to construct our network number (normally 10.115.x.x) """ if not iface: raise Tunnel.TunnelError("Tunnel() must have a interface") if not subnet: raise Tunnel.TunnelError("Tunnel() must have a subnet") if not netmask: raise Tunnel.TunnelError("Tunnel() must have a netmask") self.iface = iface self.subnetPrefix = subnet if platform.system() != "Linux": raise Tunnel.TunnelError("Tunnel() can only be run instantiated on a Linux system") mt_config.tunnelInstance = self """A list of chatty UDP services we should never accidentally forward to our slow network""" self.udpBlacklist = { 1900, # SSDP 5353, # multicast DNS 9001, # Yggdrasil multicast discovery 64512, # cjdns beacon } """A list of TCP services to block""" self.tcpBlacklist = { 5900, # VNC (Note: Only adding for testing purposes.) } """A list of protocols we ignore""" self.protocolBlacklist = { 0x02, # IGMP 0x80, # Service-Specific Connection-Oriented Protocol in a Multilink and Connectionless Environment } # A new non standard log level that is lower level than DEBUG self.LOG_TRACE = 5 # TODO: check if root? logging.info( "Starting IP to mesh tunnel (you must be root for this *pre-alpha* " "feature to work). Mesh members:" ) pub.subscribe(onTunnelReceive, "meshtastic.receive.data.IP_TUNNEL_APP") myAddr = self._nodeNumToIp(self.iface.myInfo.my_node_num) if self.iface.nodes: for node in self.iface.nodes.values(): nodeId = node["user"]["id"] ip = self._nodeNumToIp(node["num"]) logging.info(f"Node { nodeId } has IP address { ip }") logging.debug("creating TUN device with MTU=200") # FIXME - figure out real max MTU, it should be 240 - the overhead bytes for SubPacket and Data self.tun = None if self.iface.noProto: logging.warning( f"Not creating a TapDevice() because it is disabled by noProto" ) else: self.tun = TapDevice(name="mesh") self.tun.up() self.tun.ifconfig(address=myAddr, netmask=netmask, mtu=200) self._rxThread = None if self.iface.noProto: logging.warning( f"Not starting TUN reader because it is disabled by noProto" ) else: logging.debug(f"starting TUN reader, our IP address is {myAddr}") self._rxThread = threading.Thread( target=self.__tunReader, args=(), daemon=True ) self._rxThread.start() def onReceive(self, packet): """onReceive""" p = packet["decoded"]["payload"] if packet["from"] == self.iface.myInfo.my_node_num: logging.debug("Ignoring message we sent") else: logging.debug(f"Received mesh tunnel message type={type(p)} len={len(p)}") # we don't really need to check for filtering here (sender should have checked), # but this provides useful debug printing on types of packets received if not self.iface.noProto: if not self._shouldFilterPacket(p): self.tun.write(p) def _shouldFilterPacket(self, p): """Given a packet, decode it and return true if it should be ignored""" protocol = p[8 + 1] srcaddr = p[12:16] destAddr = p[16:20] subheader = 20 ignore = False # Assume we will be forwarding the packet if protocol in self.protocolBlacklist: ignore = True logging.log( self.LOG_TRACE, f"Ignoring blacklisted protocol 0x{protocol:02x}" ) elif protocol == 0x01: # ICMP icmpType = p[20] icmpCode = p[21] checksum = p[22:24] # pylint: disable=line-too-long logging.debug( f"forwarding ICMP message src={ipstr(srcaddr)}, dest={ipstr(destAddr)}, type={icmpType}, code={icmpCode}, checksum={checksum}" ) # reply to pings (swap src and dest but keep rest of packet unchanged) # pingback = p[:12]+p[16:20]+p[12:16]+p[20:] # tap.write(pingback) elif protocol == 0x11: # UDP srcport = readnet_u16(p, subheader) destport = readnet_u16(p, subheader + 2) if destport in self.udpBlacklist: ignore = True logging.log(self.LOG_TRACE, f"ignoring blacklisted UDP port {destport}") else: logging.debug(f"forwarding udp srcport={srcport}, destport={destport}") elif protocol == 0x06: # TCP srcport = readnet_u16(p, subheader) destport = readnet_u16(p, subheader + 2) if destport in self.tcpBlacklist: ignore = True logging.log(self.LOG_TRACE, f"ignoring blacklisted TCP port {destport}") else: logging.debug(f"forwarding tcp srcport={srcport}, destport={destport}") else: logging.warning( f"forwarding unexpected protocol 0x{protocol:02x}, " "src={ipstr(srcaddr)}, dest={ipstr(destAddr)}" ) return ignore def __tunReader(self): tap = self.tun logging.debug("TUN reader running") while True: p = tap.read() # logging.debug(f"IP packet received on TUN interface, type={type(p)}") destAddr = p[16:20] if not self._shouldFilterPacket(p): self.sendPacket(destAddr, p) def _ipToNodeId(self, ipAddr): # We only consider the last 16 bits of the nodenum for IP address matching ipBits = ipAddr[2] * 256 + ipAddr[3] if ipBits == 0xFFFF: return "^all" for node in self.iface.nodes.values(): nodeNum = node["num"] & 0xFFFF # logging.debug(f"Considering nodenum 0x{nodeNum:x} for ipBits 0x{ipBits:x}") if (nodeNum) == ipBits: return node["user"]["id"] return None def _nodeNumToIp(self, nodeNum): return f"{self.subnetPrefix}.{(nodeNum >> 8) & 0xff}.{nodeNum & 0xff}" def sendPacket(self, destAddr, p): """Forward the provided IP packet into the mesh""" nodeId = self._ipToNodeId(destAddr) if nodeId is not None: logging.debug( f"Forwarding packet bytelen={len(p)} dest={ipstr(destAddr)}, destNode={nodeId}" ) self.iface.sendData(p, nodeId, portnums_pb2.IP_TUNNEL_APP, wantAck=False) else: logging.warning( f"Dropping packet because no node found for destIP={ipstr(destAddr)}" ) def close(self): """Close""" self.tun.close() python-2.3.14/meshtastic/util.py000066400000000000000000000540221464266072200166310ustar00rootroot00000000000000"""Utility functions. """ import base64 import logging import os import platform import re import subprocess import sys import threading import time import traceback from queue import Queue from typing import List, NoReturn, Union from google.protobuf.json_format import MessageToJson from google.protobuf.message import Message import packaging.version as pkg_version import requests import serial # type: ignore[import-untyped] import serial.tools.list_ports # type: ignore[import-untyped] from meshtastic.supported_device import supported_devices from meshtastic.version import get_active_version """Some devices such as a seger jlink or st-link we never want to accidentally open 0x1915 NordicSemi (PPK2) """ blacklistVids = dict.fromkeys([0x1366, 0x0483, 0x1915]) """Some devices are highly likely to be meshtastic. 0x239a RAK4631 0x303a Heltec tracker""" whitelistVids = dict.fromkeys([0x239a, 0x303a]) def quoteBooleans(a_string): """Quote booleans given a string that contains ": true", replace with ": 'true'" (or false) """ tmp = a_string.replace(": true", ": 'true'") tmp = tmp.replace(": false", ": 'false'") return tmp def genPSK256(): """Generate a random preshared key""" return os.urandom(32) def fromPSK(valstr): """A special version of fromStr that assumes the user is trying to set a PSK. In that case we also allow "none", "default" or "random" (to have python generate one), or simpleN """ if valstr == "random": return genPSK256() elif valstr == "none": return bytes([0]) # Use the 'no encryption' PSK elif valstr == "default": return bytes([1]) # Use default channel psk elif valstr.startswith("simple"): # Use one of the single byte encodings return bytes([int(valstr[6:]) + 1]) else: return fromStr(valstr) def fromStr(valstr): """Try to parse as int, float or bool (and fallback to a string as last resort) Returns: an int, bool, float, str or byte array (for strings of hex digits) Args: valstr (string): A user provided string """ if len(valstr) == 0: # Treat an emptystring as an empty bytes val = bytes() 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"}: val = False else: try: val = int(valstr) except ValueError: try: val = float(valstr) except ValueError: val = valstr # Not a float or an int, assume string return val def pskToString(psk: bytes): """Given an array of PSK bytes, decode them into a human readable (but privacy protecting) string""" if len(psk) == 0: return "unencrypted" elif len(psk) == 1: b = psk[0] if b == 0: return "unencrypted" elif b == 1: return "default" else: return f"simple{b - 1}" else: return "secret" def stripnl(s) -> str: """Remove newlines from a string (and remove extra whitespace)""" s = str(s).replace("\n", " ") return " ".join(s.split()) def fixme(message): """Raise an exception for things that needs to be fixed""" raise Exception(f"FIXME: {message}") # pylint: disable=W0719 def catchAndIgnore(reason, closure): """Call a closure but if it throws an exception print it and continue""" try: closure() except BaseException as ex: logging.error(f"Exception thrown in {reason}: {ex}") def findPorts(eliminate_duplicates: bool=False) -> List[str]: """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 """ all_ports = serial.tools.list_ports.comports() # look for 'likely' meshtastic devices ports = list( map( lambda port: port.device, filter( lambda port: port.vid is not None and port.vid in whitelistVids, all_ports, ), ) ) # if no likely devices, just list everything not blacklisted if len(ports) == 0: ports = list( map( lambda port: port.device, filter( lambda port: port.vid is not None and port.vid not in blacklistVids, all_ports, ), ) ) ports.sort() if eliminate_duplicates: ports = eliminate_duplicate_port(ports) return ports class dotdict(dict): """dot.notation access to dictionary attributes""" __getattr__ = dict.get __setattr__ = dict.__setitem__ # type: ignore[assignment] __delattr__ = dict.__delitem__ # type: ignore[assignment] class Timeout: """Timeout class""" def __init__(self, maxSecs: int=20): self.expireTime: Union[int, float] = 0 self.sleepInterval: float = 0.1 self.expireTimeout: int = maxSecs def reset(self): """Restart the waitForSet timer""" self.expireTime = time.time() + self.expireTimeout def waitForSet(self, target, attrs=()) -> bool: """Block until the specified attributes are set. Returns True if config has been received.""" self.reset() while time.time() < self.expireTime: if all(map(lambda a: getattr(target, a, None), attrs)): return True time.sleep(self.sleepInterval) return False def waitForAckNak( self, acknowledgment, attrs=("receivedAck", "receivedNak", "receivedImplAck") ) -> bool: """Block until an ACK or NAK has been received. Returns True if ACK or NAK has been received.""" self.reset() while time.time() < self.expireTime: if any(map(lambda a: getattr(acknowledgment, a, None), attrs)): acknowledgment.reset() return True time.sleep(self.sleepInterval) return False def waitForTraceRoute(self, waitFactor, acknowledgment, attr="receivedTraceRoute") -> bool: """Block until traceroute response is received. Returns True if traceroute response has been received.""" self.expireTimeout *= waitFactor self.reset() while time.time() < self.expireTime: if getattr(acknowledgment, attr, None): acknowledgment.reset() return True time.sleep(self.sleepInterval) return False def waitForTelemetry(self, acknowledgment) -> bool: """Block until telemetry response is received. Returns True if telemetry response has been received.""" self.reset() while time.time() < self.expireTime: if getattr(acknowledgment, "receivedTelemetry", None): acknowledgment.reset() return True time.sleep(self.sleepInterval) return False def waitForPosition(self, acknowledgment) -> bool: """Block until position response is received. Returns True if position response has been received.""" self.reset() while time.time() < self.expireTime: if getattr(acknowledgment, "receivedPosition", None): acknowledgment.reset() return True time.sleep(self.sleepInterval) return False class Acknowledgment: "A class that records which type of acknowledgment was just received, if any." def __init__(self): """initialize""" self.receivedAck = False self.receivedNak = False self.receivedImplAck = False self.receivedTraceRoute = False self.receivedTelemetry = False self.receivedPosition = False def reset(self): """reset""" self.receivedAck = False self.receivedNak = False self.receivedImplAck = False self.receivedTraceRoute = False self.receivedTelemetry = False self.receivedPosition = False class DeferredExecution: """A thread that accepts closures to run, and runs them as they are received""" def __init__(self, name=None): self.queue = Queue() self.thread = threading.Thread(target=self._run, args=(), name=name) self.thread.daemon = True self.thread.start() def queueWork(self, runnable): """Queue up the work""" self.queue.put(runnable) def _run(self): while True: try: o = self.queue.get() o() except: logging.error( f"Unexpected error in deferred execution {sys.exc_info()[0]}" ) print(traceback.format_exc()) def our_exit(message, return_value=1) -> NoReturn: """Print the message and return a value. return_value defaults to 1 (non-successful) """ print(message) sys.exit(return_value) def support_info(): """Print out info that helps troubleshooting of the cli.""" print("") print("If having issues with meshtastic cli or python library") print("or wish to make feature requests, visit:") print("https://github.com/meshtastic/python/issues") print("When adding an issue, be sure to include the following info:") print(f" System: {platform.system()}") print(f" Platform: {platform.platform()}") print(f" Release: {platform.uname().release}") print(f" Machine: {platform.uname().machine}") print(f" Encoding (stdin): {sys.stdin.encoding}") print(f" Encoding (stdout): {sys.stdout.encoding}") the_version = get_active_version() pypi_version = check_if_newer_version() if pypi_version: print( f" meshtastic: v{the_version} (*** newer version v{pypi_version} available ***)" ) else: print(f" meshtastic: v{the_version}") if sys.version_info[0] == 3 and sys.version_info[1] < 9: print(" *** this version of the CLI is the last that supports python 3.8 ***") print(" *** please update your python installation ***") print(f" Executable: {sys.argv[0]}") print( f" Python: {platform.python_version()} {platform.python_implementation()} {platform.python_compiler()}" ) print("") print("Please add the output from the command: meshtastic --info") def remove_keys_from_dict(keys, adict): """Return a dictionary without some keys in it. Will removed nested keys. """ for key in keys: try: del adict[key] except: pass for val in adict.values(): if isinstance(val, dict): remove_keys_from_dict(keys, val) return adict def hexstr(barray): """Print a string of hex digits""" return ":".join(f"{x:02x}" for x in barray) def ipstr(barray): """Print a string of ip digits""" return ".".join(f"{x}" for x in barray) def readnet_u16(p, offset): """Read big endian u16 (network byte order)""" return p[offset] * 256 + p[offset + 1] def convert_mac_addr(val): """Convert the base 64 encoded value to a mac address val - base64 encoded value (ex: '/c0gFyhb')) returns: a string formatted like a mac address (ex: 'fd:cd:20:17:28:5b') """ if not re.match("[0-9a-f]{2}([-:]?)[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$", val): val_as_bytes = base64.b64decode(val) return hexstr(val_as_bytes) return val def snake_to_camel(a_string): """convert snake_case to camelCase""" # split underscore using split temp = a_string.split("_") # joining result result = temp[0] + "".join(ele.title() for ele in temp[1:]) return result def camel_to_snake(a_string): """convert camelCase to snake_case""" return "".join(["_" + i.lower() if i.isupper() else i for i in a_string]).lstrip( "_" ) def detect_supported_devices(): """detect supported devices based on vendor id""" system = platform.system() # print(f'system:{system}') possible_devices = set() if system == "Linux": # if linux, run lsusb and list ports # linux: use lsusb # Bus 001 Device 091: ID 10c4:ea60 Silicon Labs CP210x UART Bridge _, lsusb_output = subprocess.getstatusoutput("lsusb") vids = get_unique_vendor_ids() for vid in vids: # print(f'looking for {vid}...') search = f" {vid}:" # print(f'search:"{search}"') if re.search(search, lsusb_output, re.MULTILINE): # print(f'Found vendor id that matches') devices = get_devices_with_vendor_id(vid) for device in devices: possible_devices.add(device) elif system == "Windows": # if windows, run Get-PnpDevice _, sp_output = subprocess.getstatusoutput( 'powershell.exe "[Console]::OutputEncoding = [Text.UTF8Encoding]::UTF8;' 'Get-PnpDevice -PresentOnly | Format-List"' ) # print(f'sp_output:{sp_output}') vids = get_unique_vendor_ids() for vid in vids: # print(f'looking for {vid.upper()}...') search = f"DeviceID.*{vid.upper()}&" # search = f'{vid.upper()}' # print(f'search:"{search}"') if re.search(search, sp_output, re.MULTILINE): # print(f'Found vendor id that matches') devices = get_devices_with_vendor_id(vid) for device in devices: possible_devices.add(device) elif system == "Darwin": # run: system_profiler SPUSBDataType # Note: If in boot mode, the 19003 reports same product ID as 5005. _, sp_output = subprocess.getstatusoutput("system_profiler SPUSBDataType") vids = get_unique_vendor_ids() for vid in vids: # print(f'looking for {vid}...') search = f"Vendor ID: 0x{vid}" # print(f'search:"{search}"') if re.search(search, sp_output, re.MULTILINE): # print(f'Found vendor id that matches') devices = get_devices_with_vendor_id(vid) for device in devices: possible_devices.add(device) return possible_devices def detect_windows_needs_driver(sd, print_reason=False): """detect if Windows user needs to install driver for a supported device""" need_to_install_driver = False if sd: system = platform.system() # print(f'in detect_windows_needs_driver system:{system}') if system == "Windows": # if windows, see if we can find a DeviceId with the vendor id # Get-PnpDevice | Where-Object{ ($_.DeviceId -like '*10C4*')} | Format-List command = 'powershell.exe "[Console]::OutputEncoding = [Text.UTF8Encoding]::UTF8; Get-PnpDevice | 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}') search = f"CM_PROB_FAILED_INSTALL" # print(f'search:"{search}"') if re.search(search, sp_output, re.MULTILINE): need_to_install_driver = True # if the want to see the reason 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 def check_if_newer_version(): """Check pip to see if we are running the latest version.""" pypi_version = None try: url = "https://pypi.org/pypi/meshtastic/json" data = requests.get(url, timeout=5).json() pypi_version = data["info"]["version"] except Exception: pass act_version = get_active_version() try: parsed_act_version = pkg_version.parse(act_version) parsed_pypi_version = pkg_version.parse(pypi_version) except pkg_version.InvalidVersion: return pypi_version if parsed_pypi_version <= parsed_act_version: return None return pypi_version def message_to_json(message: Message, multiline: bool=False) -> str: """Return protobuf message as JSON. Always print all fields, even when not present in data.""" json = MessageToJson(message, always_print_fields_with_no_presence=True) return stripnl(json) if not multiline else json python-2.3.14/meshtastic/version.py000066400000000000000000000006771464266072200173500ustar00rootroot00000000000000"""Version lookup utilities, isolated for cleanliness""" import sys try: from importlib.metadata import version except: import pkg_resources def get_active_version(): """Get the currently active version using importlib, or pkg_resources if we must""" if "importlib.metadata" in sys.modules: return version("meshtastic") else: return pkg_resources.get_distribution("meshtastic").version # pylint: disable=E0601 python-2.3.14/poetry.lock000066400000000000000000003402431464266072200153350ustar00rootroot00000000000000# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. [[package]] name = "altgraph" version = "0.17.4" description = "Python graph (network) package" optional = false python-versions = "*" files = [ {file = "altgraph-0.17.4-py2.py3-none-any.whl", hash = "sha256:642743b4750de17e655e6711601b077bc6598dbfa3ba5fa2b2a35ce12b508dff"}, {file = "altgraph-0.17.4.tar.gz", hash = "sha256:1b5afbb98f6c4dcadb2e2ae6ab9fa994bbb8c1d75f4fa96d340f9437ae454406"}, ] [[package]] name = "astroid" version = "3.2.2" description = "An abstract syntax tree for Python with inference support." optional = false python-versions = ">=3.8.0" files = [ {file = "astroid-3.2.2-py3-none-any.whl", hash = "sha256:e8a0083b4bb28fcffb6207a3bfc9e5d0a68be951dd7e336d5dcf639c682388c0"}, {file = "astroid-3.2.2.tar.gz", hash = "sha256:8ead48e31b92b2e217b6c9733a21afafe479d52d6e164dd25fb1a770c7c3cf94"}, ] [package.dependencies] typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.11\""} [[package]] name = "async-timeout" version = "4.0.3" description = "Timeout context manager for asyncio programs" optional = false python-versions = ">=3.7" files = [ {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"}, {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, ] [[package]] name = "attrs" version = "23.2.0" description = "Classes Without Boilerplate" optional = false python-versions = ">=3.7" files = [ {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, ] [package.extras] cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] dev = ["attrs[tests]", "pre-commit"] docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] tests = ["attrs[tests-no-zope]", "zope-interface"] tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] [[package]] name = "autopep8" version = "2.3.1" description = "A tool that automatically formats Python code to conform to the PEP 8 style guide" optional = false python-versions = ">=3.8" files = [ {file = "autopep8-2.3.1-py2.py3-none-any.whl", hash = "sha256:a203fe0fcad7939987422140ab17a930f684763bf7335bdb6709991dd7ef6c2d"}, {file = "autopep8-2.3.1.tar.gz", hash = "sha256:8d6c87eba648fdcfc83e29b788910b8643171c395d9c4bcf115ece035b9c9dda"}, ] [package.dependencies] pycodestyle = ">=2.12.0" tomli = {version = "*", markers = "python_version < \"3.11\""} [[package]] name = "bleak" version = "0.21.1" description = "Bluetooth Low Energy platform Agnostic Klient" optional = false python-versions = ">=3.8,<3.13" files = [ {file = "bleak-0.21.1-py3-none-any.whl", hash = "sha256:ccec260a0f5ec02dd133d68b0351c0151b2ecf3ddd0bcabc4c04a1cdd7f33256"}, {file = "bleak-0.21.1.tar.gz", hash = "sha256:ec4a1a2772fb315b992cbaa1153070c7e26968a52b0e2727035f443a1af5c18f"}, ] [package.dependencies] async-timeout = {version = ">=3.0.0,<5", markers = "python_version < \"3.11\""} bleak-winrt = {version = ">=1.2.0,<2.0.0", markers = "platform_system == \"Windows\" and python_version < \"3.12\""} dbus-fast = {version = ">=1.83.0,<3", markers = "platform_system == \"Linux\""} pyobjc-core = {version = ">=9.2,<10.0", markers = "platform_system == \"Darwin\""} pyobjc-framework-CoreBluetooth = {version = ">=9.2,<10.0", markers = "platform_system == \"Darwin\""} pyobjc-framework-libdispatch = {version = ">=9.2,<10.0", markers = "platform_system == \"Darwin\""} typing-extensions = {version = ">=4.7.0", markers = "python_version < \"3.12\""} "winrt-Windows.Devices.Bluetooth" = {version = "2.0.0b1", markers = "platform_system == \"Windows\" and python_version >= \"3.12\""} "winrt-Windows.Devices.Bluetooth.Advertisement" = {version = "2.0.0b1", markers = "platform_system == \"Windows\" and python_version >= \"3.12\""} "winrt-Windows.Devices.Bluetooth.GenericAttributeProfile" = {version = "2.0.0b1", markers = "platform_system == \"Windows\" and python_version >= \"3.12\""} "winrt-Windows.Devices.Enumeration" = {version = "2.0.0b1", markers = "platform_system == \"Windows\" and python_version >= \"3.12\""} "winrt-Windows.Foundation" = {version = "2.0.0b1", markers = "platform_system == \"Windows\" and python_version >= \"3.12\""} "winrt-Windows.Foundation.Collections" = {version = "2.0.0b1", markers = "platform_system == \"Windows\" and python_version >= \"3.12\""} "winrt-Windows.Storage.Streams" = {version = "2.0.0b1", markers = "platform_system == \"Windows\" and python_version >= \"3.12\""} [[package]] name = "bleak-winrt" version = "1.2.0" description = "Python WinRT bindings for Bleak" optional = false python-versions = "*" files = [ {file = "bleak-winrt-1.2.0.tar.gz", hash = "sha256:0577d070251b9354fc6c45ffac57e39341ebb08ead014b1bdbd43e211d2ce1d6"}, {file = "bleak_winrt-1.2.0-cp310-cp310-win32.whl", hash = "sha256:a2ae3054d6843ae0cfd3b94c83293a1dfd5804393977dd69bde91cb5099fc47c"}, {file = "bleak_winrt-1.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:677df51dc825c6657b3ae94f00bd09b8ab88422b40d6a7bdbf7972a63bc44e9a"}, {file = "bleak_winrt-1.2.0-cp311-cp311-win32.whl", hash = "sha256:9449cdb942f22c9892bc1ada99e2ccce9bea8a8af1493e81fefb6de2cb3a7b80"}, {file = "bleak_winrt-1.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:98c1b5a6a6c431ac7f76aa4285b752fe14a1c626bd8a1dfa56f66173ff120bee"}, {file = "bleak_winrt-1.2.0-cp37-cp37m-win32.whl", hash = "sha256:623ac511696e1f58d83cb9c431e32f613395f2199b3db7f125a3d872cab968a4"}, {file = "bleak_winrt-1.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:13ab06dec55469cf51a2c187be7b630a7a2922e1ea9ac1998135974a7239b1e3"}, {file = "bleak_winrt-1.2.0-cp38-cp38-win32.whl", hash = "sha256:5a36ff8cd53068c01a795a75d2c13054ddc5f99ce6de62c1a97cd343fc4d0727"}, {file = "bleak_winrt-1.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:810c00726653a962256b7acd8edf81ab9e4a3c66e936a342ce4aec7dbd3a7263"}, {file = "bleak_winrt-1.2.0-cp39-cp39-win32.whl", hash = "sha256:dd740047a08925bde54bec357391fcee595d7b8ca0c74c87170a5cbc3f97aa0a"}, {file = "bleak_winrt-1.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:63130c11acfe75c504a79c01f9919e87f009f5e742bfc7b7a5c2a9c72bf591a7"}, ] [[package]] name = "certifi" version = "2024.7.4" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ {file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"}, {file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"}, ] [[package]] name = "charset-normalizer" version = "3.3.2" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7.0" files = [ {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, ] [[package]] name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] [[package]] name = "coverage" version = "7.5.4" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ {file = "coverage-7.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6cfb5a4f556bb51aba274588200a46e4dd6b505fb1a5f8c5ae408222eb416f99"}, {file = "coverage-7.5.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2174e7c23e0a454ffe12267a10732c273243b4f2d50d07544a91198f05c48f47"}, {file = "coverage-7.5.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2214ee920787d85db1b6a0bd9da5f8503ccc8fcd5814d90796c2f2493a2f4d2e"}, {file = "coverage-7.5.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1137f46adb28e3813dec8c01fefadcb8c614f33576f672962e323b5128d9a68d"}, {file = "coverage-7.5.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b385d49609f8e9efc885790a5a0e89f2e3ae042cdf12958b6034cc442de428d3"}, {file = "coverage-7.5.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b4a474f799456e0eb46d78ab07303286a84a3140e9700b9e154cfebc8f527016"}, {file = "coverage-7.5.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5cd64adedf3be66f8ccee418473c2916492d53cbafbfcff851cbec5a8454b136"}, {file = "coverage-7.5.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e564c2cf45d2f44a9da56f4e3a26b2236504a496eb4cb0ca7221cd4cc7a9aca9"}, {file = "coverage-7.5.4-cp310-cp310-win32.whl", hash = "sha256:7076b4b3a5f6d2b5d7f1185fde25b1e54eb66e647a1dfef0e2c2bfaf9b4c88c8"}, {file = "coverage-7.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:018a12985185038a5b2bcafab04ab833a9a0f2c59995b3cec07e10074c78635f"}, {file = "coverage-7.5.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:db14f552ac38f10758ad14dd7b983dbab424e731588d300c7db25b6f89e335b5"}, {file = "coverage-7.5.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3257fdd8e574805f27bb5342b77bc65578e98cbc004a92232106344053f319ba"}, {file = "coverage-7.5.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a6612c99081d8d6134005b1354191e103ec9705d7ba2754e848211ac8cacc6b"}, {file = "coverage-7.5.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d45d3cbd94159c468b9b8c5a556e3f6b81a8d1af2a92b77320e887c3e7a5d080"}, {file = "coverage-7.5.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed550e7442f278af76d9d65af48069f1fb84c9f745ae249c1a183c1e9d1b025c"}, {file = "coverage-7.5.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7a892be37ca35eb5019ec85402c3371b0f7cda5ab5056023a7f13da0961e60da"}, {file = "coverage-7.5.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8192794d120167e2a64721d88dbd688584675e86e15d0569599257566dec9bf0"}, {file = "coverage-7.5.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:820bc841faa502e727a48311948e0461132a9c8baa42f6b2b84a29ced24cc078"}, {file = "coverage-7.5.4-cp311-cp311-win32.whl", hash = "sha256:6aae5cce399a0f065da65c7bb1e8abd5c7a3043da9dceb429ebe1b289bc07806"}, {file = "coverage-7.5.4-cp311-cp311-win_amd64.whl", hash = "sha256:d2e344d6adc8ef81c5a233d3a57b3c7d5181f40e79e05e1c143da143ccb6377d"}, {file = "coverage-7.5.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:54317c2b806354cbb2dc7ac27e2b93f97096912cc16b18289c5d4e44fc663233"}, {file = "coverage-7.5.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:042183de01f8b6d531e10c197f7f0315a61e8d805ab29c5f7b51a01d62782747"}, {file = "coverage-7.5.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6bb74ed465d5fb204b2ec41d79bcd28afccf817de721e8a807d5141c3426638"}, {file = "coverage-7.5.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3d45ff86efb129c599a3b287ae2e44c1e281ae0f9a9bad0edc202179bcc3a2e"}, {file = "coverage-7.5.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5013ed890dc917cef2c9f765c4c6a8ae9df983cd60dbb635df8ed9f4ebc9f555"}, {file = "coverage-7.5.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1014fbf665fef86cdfd6cb5b7371496ce35e4d2a00cda501cf9f5b9e6fced69f"}, {file = "coverage-7.5.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3684bc2ff328f935981847082ba4fdc950d58906a40eafa93510d1b54c08a66c"}, {file = "coverage-7.5.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:581ea96f92bf71a5ec0974001f900db495488434a6928a2ca7f01eee20c23805"}, {file = "coverage-7.5.4-cp312-cp312-win32.whl", hash = "sha256:73ca8fbc5bc622e54627314c1a6f1dfdd8db69788f3443e752c215f29fa87a0b"}, {file = "coverage-7.5.4-cp312-cp312-win_amd64.whl", hash = "sha256:cef4649ec906ea7ea5e9e796e68b987f83fa9a718514fe147f538cfeda76d7a7"}, {file = "coverage-7.5.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cdd31315fc20868c194130de9ee6bfd99755cc9565edff98ecc12585b90be882"}, {file = "coverage-7.5.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:02ff6e898197cc1e9fa375581382b72498eb2e6d5fc0b53f03e496cfee3fac6d"}, {file = "coverage-7.5.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d05c16cf4b4c2fc880cb12ba4c9b526e9e5d5bb1d81313d4d732a5b9fe2b9d53"}, {file = "coverage-7.5.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5986ee7ea0795a4095ac4d113cbb3448601efca7f158ec7f7087a6c705304e4"}, {file = "coverage-7.5.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5df54843b88901fdc2f598ac06737f03d71168fd1175728054c8f5a2739ac3e4"}, {file = "coverage-7.5.4-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:ab73b35e8d109bffbda9a3e91c64e29fe26e03e49addf5b43d85fc426dde11f9"}, {file = "coverage-7.5.4-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:aea072a941b033813f5e4814541fc265a5c12ed9720daef11ca516aeacd3bd7f"}, {file = "coverage-7.5.4-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:16852febd96acd953b0d55fc842ce2dac1710f26729b31c80b940b9afcd9896f"}, {file = "coverage-7.5.4-cp38-cp38-win32.whl", hash = "sha256:8f894208794b164e6bd4bba61fc98bf6b06be4d390cf2daacfa6eca0a6d2bb4f"}, {file = "coverage-7.5.4-cp38-cp38-win_amd64.whl", hash = "sha256:e2afe743289273209c992075a5a4913e8d007d569a406ffed0bd080ea02b0633"}, {file = "coverage-7.5.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b95c3a8cb0463ba9f77383d0fa8c9194cf91f64445a63fc26fb2327e1e1eb088"}, {file = "coverage-7.5.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3d7564cc09dd91b5a6001754a5b3c6ecc4aba6323baf33a12bd751036c998be4"}, {file = "coverage-7.5.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:44da56a2589b684813f86d07597fdf8a9c6ce77f58976727329272f5a01f99f7"}, {file = "coverage-7.5.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e16f3d6b491c48c5ae726308e6ab1e18ee830b4cdd6913f2d7f77354b33f91c8"}, {file = "coverage-7.5.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dbc5958cb471e5a5af41b0ddaea96a37e74ed289535e8deca404811f6cb0bc3d"}, {file = "coverage-7.5.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:a04e990a2a41740b02d6182b498ee9796cf60eefe40cf859b016650147908029"}, {file = "coverage-7.5.4-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ddbd2f9713a79e8e7242d7c51f1929611e991d855f414ca9996c20e44a895f7c"}, {file = "coverage-7.5.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b1ccf5e728ccf83acd313c89f07c22d70d6c375a9c6f339233dcf792094bcbf7"}, {file = "coverage-7.5.4-cp39-cp39-win32.whl", hash = "sha256:56b4eafa21c6c175b3ede004ca12c653a88b6f922494b023aeb1e836df953ace"}, {file = "coverage-7.5.4-cp39-cp39-win_amd64.whl", hash = "sha256:65e528e2e921ba8fd67d9055e6b9f9e34b21ebd6768ae1c1723f4ea6ace1234d"}, {file = "coverage-7.5.4-pp38.pp39.pp310-none-any.whl", hash = "sha256:79b356f3dd5b26f3ad23b35c75dbdaf1f9e2450b6bcefc6d0825ea0aa3f86ca5"}, {file = "coverage-7.5.4.tar.gz", hash = "sha256:a44963520b069e12789d0faea4e9fdb1e410cdc4aab89d94f7f55cbb7fef0353"}, ] [package.dependencies] tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} [package.extras] toml = ["tomli"] [[package]] name = "dbus-fast" version = "2.22.1" description = "A faster version of dbus-next" optional = false python-versions = "<4.0,>=3.8" files = [ {file = "dbus_fast-2.22.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f19c08fc0ab5f0e209e008f4646bb0624eacb96fb54367ea36e450aacfe289f"}, {file = "dbus_fast-2.22.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:714c5bca7d1ae20557a5857fdb3022ff0a3f5ef2e14379eae0403940882a4d72"}, {file = "dbus_fast-2.22.1-cp310-cp310-manylinux_2_31_x86_64.whl", hash = "sha256:ac004b0f6a7f7b58ae7488f12463df68199546a8d71085379b5eed17ae012905"}, {file = "dbus_fast-2.22.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a54533ee4b30a2c062078c02d10c5a258fc10eac51a0b85cfdd7f690f1d6285f"}, {file = "dbus_fast-2.22.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cadf90548aaf336820e0b7037b0f0f46b9836ac0f2c6af0f494b00fe6bc23929"}, {file = "dbus_fast-2.22.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38e213b0252f97d6a9ceb97cd2d84ddac0d998b8dd15bdca051def181a666b6a"}, {file = "dbus_fast-2.22.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6497859da721041dbf7615aab1cae666e5c0a169fca80032ab2fd8b03f7730f5"}, {file = "dbus_fast-2.22.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a3ba17d91a32b53f8e16b40e7f948260847f3e8fbbbf83872dafe44b38a1ae42"}, {file = "dbus_fast-2.22.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2b7f32e765051817d58e3242697b47cfe5def086181ad1087c9bc70e2db48004"}, {file = "dbus_fast-2.22.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:beebe8cbd0cd90d24b757c4aad617fcfa77f2e654287bc80b11c0e4964891c22"}, {file = "dbus_fast-2.22.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b72ebd07ac873906f1001cb6eb75e864e30cb6cdcce17afe79939987b0a28b5"}, {file = "dbus_fast-2.22.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c73e3b59de2b6e7447b1c3d26ccd307838d05c6a85bcc9eac7bc990bb843cc92"}, {file = "dbus_fast-2.22.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dcb333f56ebb0de5cf3aa8affb9c492bd821e252d704dcce444a379c0513c6be"}, {file = "dbus_fast-2.22.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2980b92493698f80910b3521d685ce230f94d93deac0bcf33f2082ce551b8ac5"}, {file = "dbus_fast-2.22.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d88f7f1d4124feb4418f5d9efe359661e2f38e89f6c31539d998e3769f7f7b3"}, {file = "dbus_fast-2.22.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:bf198217013b068fe610b1d5ce7ce53e15b993625331d2c83f53be5744c0be40"}, {file = "dbus_fast-2.22.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:f90017ba2c95dba4c1e417850d3c735d5eb464cbe0ebfb5d49cc0e95e7d916d2"}, {file = "dbus_fast-2.22.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98e6d2cd04da08a9d21be68faa4d23123a2f4cb5cef3406cc1a2ef900507b1c0"}, {file = "dbus_fast-2.22.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2735f9cc9e6692b0bb114c48580709af824a16ea791922f628c265aa05f183a"}, {file = "dbus_fast-2.22.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:b709a9eaaae542d0d883c5a2f147c0cbe7ef29262ec0bf90f5a5945e76786c39"}, {file = "dbus_fast-2.22.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7e7924d5042de42dcdc6be942d2f6cf1f187cf7a4ae2902b68431ea856ef654c"}, {file = "dbus_fast-2.22.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e15b15c0bdef24f86a5940539ba68d0920d58b96cca8543fbda9189cb144fb13"}, {file = "dbus_fast-2.22.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux_2_5_x86_64.manylinux1_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f70821ac238e3fa0f5a6ae4e99054d57261743f5d5516e43226f2bec0065a3d"}, {file = "dbus_fast-2.22.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e56f6f0976aa953a2a5c71817e9ceecace6dd6a2a23dc64622025701005bf15"}, {file = "dbus_fast-2.22.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux_2_5_x86_64.manylinux1_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c6f894fe9b60374dc20c43bdf7a5b4a81e2db963433815a9d6ceaaeb51cba801"}, {file = "dbus_fast-2.22.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0934118cc2e4f777d785df923b139f253ba3019469ec1f90eb8a5e4c12fff0ce"}, {file = "dbus_fast-2.22.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux_2_5_x86_64.manylinux1_x86_64.manylinux2014_x86_64.whl", hash = "sha256:994931d9bc57166a9e16ae71cb93133fa87f35d57125d741a92a1f4e56cade28"}, {file = "dbus_fast-2.22.1.tar.gz", hash = "sha256:aa75dfb5bc7ba42f53391ae503ca5a21bd133e74ebb09965013ba23bdffc9a0e"}, ] [[package]] name = "dill" version = "0.3.8" description = "serialize all of Python" optional = false python-versions = ">=3.8" files = [ {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"}, {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"}, ] [package.extras] graph = ["objgraph (>=1.7.2)"] profile = ["gprof2dot (>=2022.7.29)"] [[package]] name = "dotmap" version = "1.3.30" description = "ordered, dynamically-expandable dot-access dictionary" optional = false python-versions = "*" files = [ {file = "dotmap-1.3.30-py3-none-any.whl", hash = "sha256:bd9fa15286ea2ad899a4d1dc2445ed85a1ae884a42effb87c89a6ecce71243c6"}, {file = "dotmap-1.3.30.tar.gz", hash = "sha256:5821a7933f075fb47563417c0e92e0b7c031158b4c9a6a7e56163479b658b368"}, ] [[package]] name = "exceptiongroup" version = "1.2.1" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" files = [ {file = "exceptiongroup-1.2.1-py3-none-any.whl", hash = "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad"}, {file = "exceptiongroup-1.2.1.tar.gz", hash = "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16"}, ] [package.extras] test = ["pytest (>=6)"] [[package]] name = "hypothesis" version = "6.104.2" description = "A library for property-based testing" optional = false python-versions = ">=3.8" files = [ {file = "hypothesis-6.104.2-py3-none-any.whl", hash = "sha256:8b52b7e2462e552c75b819495d5cb6251a2b840accc79cf2ce52588004c915d9"}, {file = "hypothesis-6.104.2.tar.gz", hash = "sha256:6f2a1489bc8fe1c87ffd202707319b66ec46b2bc11faf6e0161e957b8b9b1eab"}, ] [package.dependencies] attrs = ">=22.2.0" exceptiongroup = {version = ">=1.0.0", markers = "python_version < \"3.11\""} sortedcontainers = ">=2.1.0,<3.0.0" [package.extras] all = ["backports.zoneinfo (>=0.2.1)", "black (>=19.10b0)", "click (>=7.0)", "crosshair-tool (>=0.0.55)", "django (>=3.2)", "dpcontracts (>=0.4)", "hypothesis-crosshair (>=0.0.4)", "lark (>=0.10.1)", "libcst (>=0.3.16)", "numpy (>=1.17.3)", "pandas (>=1.1)", "pytest (>=4.6)", "python-dateutil (>=1.4)", "pytz (>=2014.1)", "redis (>=3.0.0)", "rich (>=9.0.0)", "tzdata (>=2024.1)"] cli = ["black (>=19.10b0)", "click (>=7.0)", "rich (>=9.0.0)"] codemods = ["libcst (>=0.3.16)"] crosshair = ["crosshair-tool (>=0.0.55)", "hypothesis-crosshair (>=0.0.4)"] dateutil = ["python-dateutil (>=1.4)"] django = ["django (>=3.2)"] dpcontracts = ["dpcontracts (>=0.4)"] ghostwriter = ["black (>=19.10b0)"] lark = ["lark (>=0.10.1)"] numpy = ["numpy (>=1.17.3)"] pandas = ["pandas (>=1.1)"] pytest = ["pytest (>=4.6)"] pytz = ["pytz (>=2014.1)"] redis = ["redis (>=3.0.0)"] zoneinfo = ["backports.zoneinfo (>=0.2.1)", "tzdata (>=2024.1)"] [[package]] name = "idna" version = "3.7" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.5" files = [ {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, ] [[package]] name = "importlib-metadata" version = "8.0.0" description = "Read metadata from Python packages" optional = false python-versions = ">=3.8" files = [ {file = "importlib_metadata-8.0.0-py3-none-any.whl", hash = "sha256:15584cf2b1bf449d98ff8a6ff1abef57bf20f3ac6454f431736cd3e660921b2f"}, {file = "importlib_metadata-8.0.0.tar.gz", hash = "sha256:188bd24e4c346d3f0a933f275c2fec67050326a856b9a359881d7c2a697e8812"}, ] [package.dependencies] zipp = ">=0.5" [package.extras] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] perf = ["ipython"] test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] [[package]] name = "iniconfig" version = "2.0.0" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.7" files = [ {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] [[package]] name = "isort" version = "5.13.2" description = "A Python utility / library to sort Python imports." optional = false python-versions = ">=3.8.0" files = [ {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, ] [package.extras] colors = ["colorama (>=0.4.6)"] [[package]] name = "macholib" version = "1.16.3" description = "Mach-O header analysis and editing" optional = false python-versions = "*" files = [ {file = "macholib-1.16.3-py2.py3-none-any.whl", hash = "sha256:0e315d7583d38b8c77e815b1ecbdbf504a8258d8b3e17b61165c6feb60d18f2c"}, {file = "macholib-1.16.3.tar.gz", hash = "sha256:07ae9e15e8e4cd9a788013d81f5908b3609aa76f9b1421bae9c4d7606ec86a30"}, ] [package.dependencies] altgraph = ">=0.17" [[package]] name = "mako" version = "1.3.5" description = "A super-fast templating language that borrows the best ideas from the existing templating languages." optional = false python-versions = ">=3.8" files = [ {file = "Mako-1.3.5-py3-none-any.whl", hash = "sha256:260f1dbc3a519453a9c856dedfe4beb4e50bd5a26d96386cb6c80856556bb91a"}, {file = "Mako-1.3.5.tar.gz", hash = "sha256:48dbc20568c1d276a2698b36d968fa76161bf127194907ea6fc594fa81f943bc"}, ] [package.dependencies] MarkupSafe = ">=0.9.2" [package.extras] babel = ["Babel"] lingua = ["lingua"] testing = ["pytest"] [[package]] name = "markdown" version = "3.6" description = "Python implementation of John Gruber's Markdown." optional = false python-versions = ">=3.8" files = [ {file = "Markdown-3.6-py3-none-any.whl", hash = "sha256:48f276f4d8cfb8ce6527c8f79e2ee29708508bf4d40aa410fbc3b4ee832c850f"}, {file = "Markdown-3.6.tar.gz", hash = "sha256:ed4f41f6daecbeeb96e576ce414c41d2d876daa9a16cb35fa8ed8c2ddfad0224"}, ] [package.dependencies] importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""} [package.extras] docs = ["mdx-gh-links (>=0.2)", "mkdocs (>=1.5)", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-nature (>=0.6)", "mkdocs-section-index", "mkdocstrings[python]"] testing = ["coverage", "pyyaml"] [[package]] name = "markupsafe" version = "2.1.5" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.7" files = [ {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, ] [[package]] name = "mccabe" version = "0.7.0" description = "McCabe checker, plugin for flake8" optional = false python-versions = ">=3.6" files = [ {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, ] [[package]] name = "mypy" version = "1.10.1" description = "Optional static typing for Python" optional = false python-versions = ">=3.8" files = [ {file = "mypy-1.10.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e36f229acfe250dc660790840916eb49726c928e8ce10fbdf90715090fe4ae02"}, {file = "mypy-1.10.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:51a46974340baaa4145363b9e051812a2446cf583dfaeba124af966fa44593f7"}, {file = "mypy-1.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:901c89c2d67bba57aaaca91ccdb659aa3a312de67f23b9dfb059727cce2e2e0a"}, {file = "mypy-1.10.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0cd62192a4a32b77ceb31272d9e74d23cd88c8060c34d1d3622db3267679a5d9"}, {file = "mypy-1.10.1-cp310-cp310-win_amd64.whl", hash = "sha256:a2cbc68cb9e943ac0814c13e2452d2046c2f2b23ff0278e26599224cf164e78d"}, {file = "mypy-1.10.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bd6f629b67bb43dc0d9211ee98b96d8dabc97b1ad38b9b25f5e4c4d7569a0c6a"}, {file = "mypy-1.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a1bbb3a6f5ff319d2b9d40b4080d46cd639abe3516d5a62c070cf0114a457d84"}, {file = "mypy-1.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8edd4e9bbbc9d7b79502eb9592cab808585516ae1bcc1446eb9122656c6066f"}, {file = "mypy-1.10.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6166a88b15f1759f94a46fa474c7b1b05d134b1b61fca627dd7335454cc9aa6b"}, {file = "mypy-1.10.1-cp311-cp311-win_amd64.whl", hash = "sha256:5bb9cd11c01c8606a9d0b83ffa91d0b236a0e91bc4126d9ba9ce62906ada868e"}, {file = "mypy-1.10.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d8681909f7b44d0b7b86e653ca152d6dff0eb5eb41694e163c6092124f8246d7"}, {file = "mypy-1.10.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:378c03f53f10bbdd55ca94e46ec3ba255279706a6aacaecac52ad248f98205d3"}, {file = "mypy-1.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bacf8f3a3d7d849f40ca6caea5c055122efe70e81480c8328ad29c55c69e93e"}, {file = "mypy-1.10.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:701b5f71413f1e9855566a34d6e9d12624e9e0a8818a5704d74d6b0402e66c04"}, {file = "mypy-1.10.1-cp312-cp312-win_amd64.whl", hash = "sha256:3c4c2992f6ea46ff7fce0072642cfb62af7a2484efe69017ed8b095f7b39ef31"}, {file = "mypy-1.10.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:604282c886497645ffb87b8f35a57ec773a4a2721161e709a4422c1636ddde5c"}, {file = "mypy-1.10.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37fd87cab83f09842653f08de066ee68f1182b9b5282e4634cdb4b407266bade"}, {file = "mypy-1.10.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8addf6313777dbb92e9564c5d32ec122bf2c6c39d683ea64de6a1fd98b90fe37"}, {file = "mypy-1.10.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5cc3ca0a244eb9a5249c7c583ad9a7e881aa5d7b73c35652296ddcdb33b2b9c7"}, {file = "mypy-1.10.1-cp38-cp38-win_amd64.whl", hash = "sha256:1b3a2ffce52cc4dbaeee4df762f20a2905aa171ef157b82192f2e2f368eec05d"}, {file = "mypy-1.10.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fe85ed6836165d52ae8b88f99527d3d1b2362e0cb90b005409b8bed90e9059b3"}, {file = "mypy-1.10.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c2ae450d60d7d020d67ab440c6e3fae375809988119817214440033f26ddf7bf"}, {file = "mypy-1.10.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6be84c06e6abd72f960ba9a71561c14137a583093ffcf9bbfaf5e613d63fa531"}, {file = "mypy-1.10.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2189ff1e39db399f08205e22a797383613ce1cb0cb3b13d8bcf0170e45b96cc3"}, {file = "mypy-1.10.1-cp39-cp39-win_amd64.whl", hash = "sha256:97a131ee36ac37ce9581f4220311247ab6cba896b4395b9c87af0675a13a755f"}, {file = "mypy-1.10.1-py3-none-any.whl", hash = "sha256:71d8ac0b906354ebda8ef1673e5fde785936ac1f29ff6987c7483cfbd5a4235a"}, {file = "mypy-1.10.1.tar.gz", hash = "sha256:1f8f492d7db9e3593ef42d4f115f04e556130f2819ad33ab84551403e97dd4c0"}, ] [package.dependencies] mypy-extensions = ">=1.0.0" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} typing-extensions = ">=4.1.0" [package.extras] dmypy = ["psutil (>=4.0)"] install-types = ["pip"] mypyc = ["setuptools (>=50)"] reports = ["lxml"] [[package]] name = "mypy-extensions" version = "1.0.0" description = "Type system extensions for programs checked with the mypy type checker." optional = false python-versions = ">=3.5" files = [ {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] [[package]] name = "mypy-protobuf" version = "3.6.0" description = "Generate mypy stub files from protobuf specs" optional = false python-versions = ">=3.8" files = [ {file = "mypy-protobuf-3.6.0.tar.gz", hash = "sha256:02f242eb3409f66889f2b1a3aa58356ec4d909cdd0f93115622e9e70366eca3c"}, {file = "mypy_protobuf-3.6.0-py3-none-any.whl", hash = "sha256:56176e4d569070e7350ea620262478b49b7efceba4103d468448f1d21492fd6c"}, ] [package.dependencies] protobuf = ">=4.25.3" types-protobuf = ">=4.24" [[package]] name = "packaging" version = "24.1" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" files = [ {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, ] [[package]] name = "pdoc3" version = "0.10.0" description = "Auto-generate API documentation for Python projects." optional = false python-versions = ">= 3.6" files = [ {file = "pdoc3-0.10.0-py3-none-any.whl", hash = "sha256:ba45d1ada1bd987427d2bf5cdec30b2631a3ff5fb01f6d0e77648a572ce6028b"}, {file = "pdoc3-0.10.0.tar.gz", hash = "sha256:5f22e7bcb969006738e1aa4219c75a32f34c2d62d46dc9d2fb2d3e0b0287e4b7"}, ] [package.dependencies] mako = "*" markdown = ">=3.0" [[package]] name = "pefile" version = "2023.2.7" description = "Python PE parsing module" optional = false python-versions = ">=3.6.0" files = [ {file = "pefile-2023.2.7-py3-none-any.whl", hash = "sha256:da185cd2af68c08a6cd4481f7325ed600a88f6a813bad9dea07ab3ef73d8d8d6"}, {file = "pefile-2023.2.7.tar.gz", hash = "sha256:82e6114004b3d6911c77c3953e3838654b04511b8b66e8583db70c65998017dc"}, ] [[package]] name = "pexpect" version = "4.9.0" description = "Pexpect allows easy control of interactive console applications." optional = false python-versions = "*" files = [ {file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"}, {file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"}, ] [package.dependencies] ptyprocess = ">=0.5" [[package]] name = "platformdirs" version = "4.2.2" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" files = [ {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, ] [package.extras] docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] type = ["mypy (>=1.8)"] [[package]] name = "pluggy" version = "1.5.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" files = [ {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, ] [package.extras] dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] [[package]] name = "print-color" version = "0.4.6" description = "A simple package to print in color to the terminal" optional = false python-versions = ">=3.7,<4.0" files = [ {file = "print_color-0.4.6-py3-none-any.whl", hash = "sha256:494bd1cdb84daf481f0e63bd22b3c32f7d52827d8f5d9138a96bb01ca8ba9299"}, {file = "print_color-0.4.6.tar.gz", hash = "sha256:d3aafc1666c8d31a85fffa6ee8e4f269f5d5e338d685b4e6179915c71867c585"}, ] [[package]] name = "protobuf" version = "5.27.2" description = "" optional = false python-versions = ">=3.8" files = [ {file = "protobuf-5.27.2-cp310-abi3-win32.whl", hash = "sha256:354d84fac2b0d76062e9b3221f4abbbacdfd2a4d8af36bab0474f3a0bb30ab38"}, {file = "protobuf-5.27.2-cp310-abi3-win_amd64.whl", hash = "sha256:0e341109c609749d501986b835f667c6e1e24531096cff9d34ae411595e26505"}, {file = "protobuf-5.27.2-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a109916aaac42bff84702fb5187f3edadbc7c97fc2c99c5ff81dd15dcce0d1e5"}, {file = "protobuf-5.27.2-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:176c12b1f1c880bf7a76d9f7c75822b6a2bc3db2d28baa4d300e8ce4cde7409b"}, {file = "protobuf-5.27.2-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:b848dbe1d57ed7c191dfc4ea64b8b004a3f9ece4bf4d0d80a367b76df20bf36e"}, {file = "protobuf-5.27.2-cp38-cp38-win32.whl", hash = "sha256:4fadd8d83e1992eed0248bc50a4a6361dc31bcccc84388c54c86e530b7f58863"}, {file = "protobuf-5.27.2-cp38-cp38-win_amd64.whl", hash = "sha256:610e700f02469c4a997e58e328cac6f305f649826853813177e6290416e846c6"}, {file = "protobuf-5.27.2-cp39-cp39-win32.whl", hash = "sha256:9e8f199bf7f97bd7ecebffcae45ebf9527603549b2b562df0fbc6d4d688f14ca"}, {file = "protobuf-5.27.2-cp39-cp39-win_amd64.whl", hash = "sha256:7fc3add9e6003e026da5fc9e59b131b8f22b428b991ccd53e2af8071687b4fce"}, {file = "protobuf-5.27.2-py3-none-any.whl", hash = "sha256:54330f07e4949d09614707c48b06d1a22f8ffb5763c159efd5c0928326a91470"}, {file = "protobuf-5.27.2.tar.gz", hash = "sha256:f3ecdef226b9af856075f28227ff2c90ce3a594d092c39bee5513573f25e2714"}, ] [[package]] name = "ptyprocess" version = "0.7.0" description = "Run a subprocess in a pseudo terminal" optional = false python-versions = "*" files = [ {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, ] [[package]] name = "pycodestyle" version = "2.12.0" description = "Python style guide checker" optional = false python-versions = ">=3.8" files = [ {file = "pycodestyle-2.12.0-py2.py3-none-any.whl", hash = "sha256:949a39f6b86c3e1515ba1787c2022131d165a8ad271b11370a8819aa070269e4"}, {file = "pycodestyle-2.12.0.tar.gz", hash = "sha256:442f950141b4f43df752dd303511ffded3a04c2b6fb7f65980574f0c31e6e79c"}, ] [[package]] name = "pyinstaller" version = "6.8.0" description = "PyInstaller bundles a Python application and all its dependencies into a single package." optional = false python-versions = "<3.13,>=3.8" files = [ {file = "pyinstaller-6.8.0-py3-none-macosx_10_13_universal2.whl", hash = "sha256:5ff6bc2784c1026f8e2f04aa3760cbed41408e108a9d4cf1dd52ee8351a3f6e1"}, {file = "pyinstaller-6.8.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:39ac424d2ee2457d2ab11a5091436e75a0cccae207d460d180aa1fcbbafdd528"}, {file = "pyinstaller-6.8.0-py3-none-manylinux2014_i686.whl", hash = "sha256:355832a3acc7de90a255ecacd4b9f9e166a547a79c8905d49f14e3a75c1acdb9"}, {file = "pyinstaller-6.8.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:6303c7a009f47e6a96ef65aed49f41e36ece8d079b9193ca92fe807403e5fe80"}, {file = "pyinstaller-6.8.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2b71509468c811968c0b5decb5bbe85b6292ea52d7b1f26313d2aabb673fa9a5"}, {file = "pyinstaller-6.8.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:ff31c5b99e05a4384bbe2071df67ec8b2b347640a375eae9b40218be2f1754c6"}, {file = "pyinstaller-6.8.0-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:000c36b13fe4cd8d0d8c2bc855b1ddcf39867b5adf389e6b5ca45b25fa3e619d"}, {file = "pyinstaller-6.8.0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:fe0af018d7d5077180e3144ada89a4da5df8d07716eb7e9482834a56dc57a4e8"}, {file = "pyinstaller-6.8.0-py3-none-win32.whl", hash = "sha256:d257f6645c7334cbd66f38a4fac62c3ad614cc46302b2b5d9f8cc48c563bce0e"}, {file = "pyinstaller-6.8.0-py3-none-win_amd64.whl", hash = "sha256:81cccfa9b16699b457f4788c5cc119b50f3cd4d0db924955f15c33f2ad27a50d"}, {file = "pyinstaller-6.8.0-py3-none-win_arm64.whl", hash = "sha256:1c3060a263758cf7f0144ab4c016097b20451b2469d468763414665db1bb743d"}, {file = "pyinstaller-6.8.0.tar.gz", hash = "sha256:3f4b6520f4423fe19bcc2fd63ab7238851ae2bdcbc98f25bc5d2f97cc62012e9"}, ] [package.dependencies] altgraph = "*" importlib-metadata = {version = ">=4.6", markers = "python_version < \"3.10\""} macholib = {version = ">=1.8", markers = "sys_platform == \"darwin\""} packaging = ">=22.0" pefile = {version = ">=2022.5.30", markers = "sys_platform == \"win32\""} pyinstaller-hooks-contrib = ">=2024.6" pywin32-ctypes = {version = ">=0.2.1", markers = "sys_platform == \"win32\""} setuptools = ">=42.0.0" [package.extras] completion = ["argcomplete"] hook-testing = ["execnet (>=1.5.0)", "psutil", "pytest (>=2.7.3)"] [[package]] name = "pyinstaller-hooks-contrib" version = "2024.7" description = "Community maintained hooks for PyInstaller" optional = false python-versions = ">=3.7" files = [ {file = "pyinstaller_hooks_contrib-2024.7-py2.py3-none-any.whl", hash = "sha256:8bf0775771fbaf96bcd2f4dfd6f7ae6c1dd1b1efe254c7e50477b3c08e7841d8"}, {file = "pyinstaller_hooks_contrib-2024.7.tar.gz", hash = "sha256:fd5f37dcf99bece184e40642af88be16a9b89613ecb958a8bd1136634fc9fac5"}, ] [package.dependencies] importlib-metadata = {version = ">=4.6", markers = "python_version < \"3.10\""} packaging = ">=22.0" setuptools = ">=42.0.0" [[package]] name = "pylint" version = "3.2.5" description = "python code static checker" optional = false python-versions = ">=3.8.0" files = [ {file = "pylint-3.2.5-py3-none-any.whl", hash = "sha256:32cd6c042b5004b8e857d727708720c54a676d1e22917cf1a2df9b4d4868abd6"}, {file = "pylint-3.2.5.tar.gz", hash = "sha256:e9b7171e242dcc6ebd0aaa7540481d1a72860748a0a7816b8fe6cf6c80a6fe7e"}, ] [package.dependencies] astroid = ">=3.2.2,<=3.3.0-dev0" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} dill = [ {version = ">=0.2", markers = "python_version < \"3.11\""}, {version = ">=0.3.7", markers = "python_version >= \"3.12\""}, {version = ">=0.3.6", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, ] isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" mccabe = ">=0.6,<0.8" platformdirs = ">=2.2.0" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} tomlkit = ">=0.10.1" typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""} [package.extras] spelling = ["pyenchant (>=3.2,<4.0)"] testutils = ["gitpython (>3)"] [[package]] name = "pyobjc-core" version = "9.2" description = "Python<->ObjC Interoperability Module" optional = false python-versions = ">=3.7" files = [ {file = "pyobjc-core-9.2.tar.gz", hash = "sha256:d734b9291fec91ff4e3ae38b9c6839debf02b79c07314476e87da8e90b2c68c3"}, {file = "pyobjc_core-9.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fa674a39949f5cde8e5c7bbcd24496446bfc67592b028aedbec7f81dc5fc4daa"}, {file = "pyobjc_core-9.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bbc8de304ee322a1ee530b4d2daca135a49b4a49aa3cedc6b2c26c43885f4842"}, {file = "pyobjc_core-9.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0fa950f092673883b8bd28bc18397415cabb457bf410920762109b411789ade9"}, {file = "pyobjc_core-9.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:586e4cae966282eaa61b21cae66ccdcee9d69c036979def26eebdc08ddebe20f"}, {file = "pyobjc_core-9.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:41189c2c680931c0395a55691763c481fc681f454f21bb4f1644f98c24a45954"}, {file = "pyobjc_core-9.2-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:2d23ee539f2ba5e9f5653d75a13f575c7e36586fc0086792739e69e4c2617eda"}, {file = "pyobjc_core-9.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b9809cf96678797acb72a758f34932fe8e2602d5ab7abec15c5ac68ddb481720"}, ] [[package]] name = "pyobjc-framework-cocoa" version = "9.2" description = "Wrappers for the Cocoa frameworks on macOS" optional = false python-versions = ">=3.7" files = [ {file = "pyobjc-framework-Cocoa-9.2.tar.gz", hash = "sha256:efd78080872d8c8de6c2b97e0e4eac99d6203a5d1637aa135d071d464eb2db53"}, {file = "pyobjc_framework_Cocoa-9.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9e02d8a7cc4eb7685377c50ba4f17345701acf4c05b1e7480d421bff9e2f62a4"}, {file = "pyobjc_framework_Cocoa-9.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3b1e6287b3149e4c6679cdbccd8e9ef6557a4e492a892e80a77df143f40026d2"}, {file = "pyobjc_framework_Cocoa-9.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:312977ce2e3989073c6b324c69ba24283de206fe7acd6dbbbaf3e29238a22537"}, {file = "pyobjc_framework_Cocoa-9.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:aae7841cf40c26dd915f4dd828f91c6616e6b7998630b72e704750c09e00f334"}, {file = "pyobjc_framework_Cocoa-9.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:739a421e14382a46cbeb9a883f192dceff368ad28ec34d895c48c0ad34cf2c1d"}, {file = "pyobjc_framework_Cocoa-9.2-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:32d9ac1033fac1b821ddee8c68f972a7074ad8c50bec0bea9a719034c1c2fb94"}, {file = "pyobjc_framework_Cocoa-9.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b236bb965e41aeb2e215d4e98a5a230d4b63252c6d26e00924ea2e69540a59d6"}, ] [package.dependencies] pyobjc-core = ">=9.2" [[package]] name = "pyobjc-framework-corebluetooth" version = "9.2" description = "Wrappers for the framework CoreBluetooth on macOS" optional = false python-versions = ">=3.7" files = [ {file = "pyobjc-framework-CoreBluetooth-9.2.tar.gz", hash = "sha256:cb2481b1dfe211ae9ce55f36537dc8155dbf0dc8ff26e0bc2e13f7afb0a291d1"}, {file = "pyobjc_framework_CoreBluetooth-9.2-cp36-abi3-macosx_10_9_universal2.whl", hash = "sha256:53d888742119d0f0c725d0b0c2389f68e8f21f0cba6d6aec288c53260a0196b6"}, {file = "pyobjc_framework_CoreBluetooth-9.2-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:179532882126526e38fe716a50fb0ee8f440e0b838d290252c515e622b5d0e49"}, {file = "pyobjc_framework_CoreBluetooth-9.2-cp36-abi3-macosx_11_0_universal2.whl", hash = "sha256:256a5031ea9d8a7406541fa1b0dfac549b1de93deae8284605f9355b13fb58be"}, ] [package.dependencies] pyobjc-core = ">=9.2" pyobjc-framework-Cocoa = ">=9.2" [[package]] name = "pyobjc-framework-libdispatch" version = "9.2" description = "Wrappers for libdispatch on macOS" optional = false python-versions = ">=3.7" files = [ {file = "pyobjc-framework-libdispatch-9.2.tar.gz", hash = "sha256:542e7f7c2b041939db5ed6f3119c1d67d73ec14a996278b92485f8513039c168"}, {file = "pyobjc_framework_libdispatch-9.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:88d4091d4bcb5702783d6e86b4107db973425a17d1de491543f56bd348909b60"}, {file = "pyobjc_framework_libdispatch-9.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1a67b007113328538b57893cc7829a722270764cdbeae6d5e1460a1d911314df"}, {file = "pyobjc_framework_libdispatch-9.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:6fccea1a57436cf1ac50d9ebc6e3e725bcf77f829ba6b118e62e6ed7866d359d"}, {file = "pyobjc_framework_libdispatch-9.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6eba747b7ad91b0463265a7aee59235bb051fb97687f35ca2233690369b5e4e4"}, {file = "pyobjc_framework_libdispatch-9.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2e835495860d04f63c2d2f73ae3dd79da4222864c107096dc0f99e8382700026"}, {file = "pyobjc_framework_libdispatch-9.2-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:1b107e5c3580b09553030961ea6b17abad4a5132101eab1af3ad2cb36d0f08bb"}, {file = "pyobjc_framework_libdispatch-9.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:83cdb672acf722717b5ecf004768f215f02ac02d7f7f2a9703da6e921ab02222"}, ] [package.dependencies] pyobjc-core = ">=9.2" [[package]] name = "pyparsing" version = "3.1.2" description = "pyparsing module - Classes and methods to define and execute parsing grammars" optional = false python-versions = ">=3.6.8" files = [ {file = "pyparsing-3.1.2-py3-none-any.whl", hash = "sha256:f9db75911801ed778fe61bb643079ff86601aca99fcae6345aa67292038fb742"}, {file = "pyparsing-3.1.2.tar.gz", hash = "sha256:a1bac0ce561155ecc3ed78ca94d3c9378656ad4c94c1270de543f621420f94ad"}, ] [package.extras] diagrams = ["jinja2", "railroad-diagrams"] [[package]] name = "pypubsub" version = "4.0.3" description = "Python Publish-Subscribe Package" optional = false python-versions = ">=3.3, <4" files = [ {file = "Pypubsub-4.0.3-py3-none-any.whl", hash = "sha256:7f716bae9388afe01ff82b264ba8a96a8ae78b42bb1f114f2716ca8f9e404e2a"}, ] [[package]] name = "pyqrcode" version = "1.2.1" description = "A QR code generator written purely in Python with SVG, EPS, PNG and terminal output." optional = false python-versions = "*" files = [ {file = "PyQRCode-1.2.1.tar.gz", hash = "sha256:fdbf7634733e56b72e27f9bce46e4550b75a3a2c420414035cae9d9d26b234d5"}, {file = "PyQRCode-1.2.1.zip", hash = "sha256:1b2812775fa6ff5c527977c4cd2ccb07051ca7d0bc0aecf937a43864abe5eff6"}, ] [package.extras] png = ["pypng (>=0.0.13)"] [[package]] name = "pyserial" version = "3.5" description = "Python Serial Port Extension" optional = false python-versions = "*" files = [ {file = "pyserial-3.5-py2.py3-none-any.whl", hash = "sha256:c4451db6ba391ca6ca299fb3ec7bae67a5c55dde170964c7a14ceefec02f2cf0"}, {file = "pyserial-3.5.tar.gz", hash = "sha256:3c77e014170dfffbd816e6ffc205e9842efb10be9f58ec16d3e8675b4925cddb"}, ] [package.extras] cp2110 = ["hidapi"] [[package]] name = "pytap2" version = "2.3.0" description = "Object-oriented wrapper around the Linux Tun/Tap device" optional = false python-versions = ">=3.8" files = [ {file = "pytap2-2.3.0-py3-none-any.whl", hash = "sha256:a1edc287cf25c61f8fa8415fb6b61e50ac119ef5cd758ce15f2105d2c69f24ef"}, {file = "pytap2-2.3.0.tar.gz", hash = "sha256:5a90d7b7c7107a438e53c7b27c1baadffe72889ada6024c02d19801fece2c383"}, ] [[package]] name = "pytest" version = "8.2.2" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ {file = "pytest-8.2.2-py3-none-any.whl", hash = "sha256:c434598117762e2bd304e526244f67bf66bbd7b5d6cf22138be51ff661980343"}, {file = "pytest-8.2.2.tar.gz", hash = "sha256:de4bb8104e201939ccdc688b27a89a7be2079b22e2bd2b07f806b6ba71117977"}, ] [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} iniconfig = "*" packaging = "*" pluggy = ">=1.5,<2.0" tomli = {version = ">=1", markers = "python_version < \"3.11\""} [package.extras] dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] [[package]] name = "pytest-cov" version = "5.0.0" description = "Pytest plugin for measuring coverage." optional = false python-versions = ">=3.8" files = [ {file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"}, {file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"}, ] [package.dependencies] coverage = {version = ">=5.2.1", extras = ["toml"]} pytest = ">=4.6" [package.extras] testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] [[package]] name = "pywin32-ctypes" version = "0.2.2" description = "A (partial) reimplementation of pywin32 using ctypes/cffi" optional = false python-versions = ">=3.6" files = [ {file = "pywin32-ctypes-0.2.2.tar.gz", hash = "sha256:3426e063bdd5fd4df74a14fa3cf80a0b42845a87e1d1e81f6549f9daec593a60"}, {file = "pywin32_ctypes-0.2.2-py3-none-any.whl", hash = "sha256:bf490a1a709baf35d688fe0ecf980ed4de11d2b3e37b51e5442587a75d9957e7"}, ] [[package]] name = "pyyaml" version = "6.0.1" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.6" files = [ {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, ] [[package]] name = "requests" version = "2.32.3" description = "Python HTTP for Humans." optional = false python-versions = ">=3.8" files = [ {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, ] [package.dependencies] certifi = ">=2017.4.17" charset-normalizer = ">=2,<4" idna = ">=2.5,<4" urllib3 = ">=1.21.1,<3" [package.extras] socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "setuptools" version = "70.1.1" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ {file = "setuptools-70.1.1-py3-none-any.whl", hash = "sha256:a58a8fde0541dab0419750bcc521fbdf8585f6e5cb41909df3a472ef7b81ca95"}, {file = "setuptools-70.1.1.tar.gz", hash = "sha256:937a48c7cdb7a21eb53cd7f9b59e525503aa8abaf3584c730dc5f7a5bec3a650"}, ] [package.extras] docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] testing = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.10.0)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.3.2)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "sortedcontainers" version = "2.4.0" description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set" optional = false python-versions = "*" files = [ {file = "sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0"}, {file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"}, ] [[package]] name = "tabulate" version = "0.9.0" description = "Pretty-print tabular data" optional = false python-versions = ">=3.7" files = [ {file = "tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f"}, {file = "tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c"}, ] [package.extras] widechars = ["wcwidth"] [[package]] name = "tomli" version = "2.0.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.7" files = [ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] [[package]] name = "tomlkit" version = "0.12.5" description = "Style preserving TOML library" optional = false python-versions = ">=3.7" files = [ {file = "tomlkit-0.12.5-py3-none-any.whl", hash = "sha256:af914f5a9c59ed9d0762c7b64d3b5d5df007448eb9cd2edc8a46b1eafead172f"}, {file = "tomlkit-0.12.5.tar.gz", hash = "sha256:eef34fba39834d4d6b73c9ba7f3e4d1c417a4e56f89a7e96e090dd0d24b8fb3c"}, ] [[package]] name = "types-protobuf" version = "5.27.0.20240626" description = "Typing stubs for protobuf" optional = false python-versions = ">=3.8" files = [ {file = "types-protobuf-5.27.0.20240626.tar.gz", hash = "sha256:683ba14043bade6785e3f937a7498f243b37881a91ac8d81b9202ecf8b191e9c"}, {file = "types_protobuf-5.27.0.20240626-py3-none-any.whl", hash = "sha256:688e8f7e8d9295db26bc560df01fb731b27a25b77cbe4c1ce945647f7024f5c1"}, ] [[package]] name = "types-pyyaml" version = "6.0.12.20240311" description = "Typing stubs for PyYAML" optional = false python-versions = ">=3.8" files = [ {file = "types-PyYAML-6.0.12.20240311.tar.gz", hash = "sha256:a9e0f0f88dc835739b0c1ca51ee90d04ca2a897a71af79de9aec5f38cb0a5342"}, {file = "types_PyYAML-6.0.12.20240311-py3-none-any.whl", hash = "sha256:b845b06a1c7e54b8e5b4c683043de0d9caf205e7434b3edc678ff2411979b8f6"}, ] [[package]] name = "types-requests" version = "2.32.0.20240622" description = "Typing stubs for requests" optional = false python-versions = ">=3.8" files = [ {file = "types-requests-2.32.0.20240622.tar.gz", hash = "sha256:ed5e8a412fcc39159d6319385c009d642845f250c63902718f605cd90faade31"}, {file = "types_requests-2.32.0.20240622-py3-none-any.whl", hash = "sha256:97bac6b54b5bd4cf91d407e62f0932a74821bc2211f22116d9ee1dd643826caf"}, ] [package.dependencies] urllib3 = ">=2" [[package]] name = "types-setuptools" version = "69.5.0.20240522" description = "Typing stubs for setuptools" optional = false python-versions = ">=3.8" files = [ {file = "types-setuptools-69.5.0.20240522.tar.gz", hash = "sha256:c5a97601b2d040d3b9fcd0633730f0a8c86ebef208552525c97301427f261549"}, {file = "types_setuptools-69.5.0.20240522-py3-none-any.whl", hash = "sha256:e27231cbc80648cfaee4921d2f1150107fdf8d33666958abf2aba0191a82688b"}, ] [[package]] name = "types-tabulate" version = "0.9.0.20240106" description = "Typing stubs for tabulate" optional = false python-versions = ">=3.8" files = [ {file = "types-tabulate-0.9.0.20240106.tar.gz", hash = "sha256:c9b6db10dd7fcf55bd1712dd3537f86ddce72a08fd62bb1af4338c7096ce947e"}, {file = "types_tabulate-0.9.0.20240106-py3-none-any.whl", hash = "sha256:0378b7b6fe0ccb4986299496d027a6d4c218298ecad67199bbd0e2d7e9d335a1"}, ] [[package]] name = "typing-extensions" version = "4.12.2" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] [[package]] name = "urllib3" version = "2.2.2" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.8" files = [ {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"}, {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"}, ] [package.extras] brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] [[package]] name = "webencodings" version = "0.5.1" description = "Character encoding aliases for legacy web content" optional = false python-versions = "*" files = [ {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, ] [[package]] name = "winrt-runtime" version = "2.0.0b1" description = "Python projection of Windows Runtime (WinRT) APIs" optional = false python-versions = "<3.13,>=3.9" files = [ {file = "winrt-runtime-2.0.0b1.tar.gz", hash = "sha256:28db2ebe7bfb347d110224e9f23fe8079cea45af0fcbd643d039524ced07d22c"}, {file = "winrt_runtime-2.0.0b1-cp310-cp310-win32.whl", hash = "sha256:8f812b01e2c8dd3ca68aa51a7aa02e815cc2ac3c8520a883b4ec7a4fc63afb04"}, {file = "winrt_runtime-2.0.0b1-cp310-cp310-win_amd64.whl", hash = "sha256:f36f6102f9b7a08d917a6809117c085639b66be2c579f4089d3fd47b83e8f87b"}, {file = "winrt_runtime-2.0.0b1-cp310-cp310-win_arm64.whl", hash = "sha256:4a99f267da96edc977623355b816b46c1344c66dc34732857084417d8cf9a96b"}, {file = "winrt_runtime-2.0.0b1-cp311-cp311-win32.whl", hash = "sha256:ba998e3fc452338c5e2d7bf5174a6206580245066d60079ee4130082d0eb61c2"}, {file = "winrt_runtime-2.0.0b1-cp311-cp311-win_amd64.whl", hash = "sha256:e7838f0fdf5653ce245888590214177a1f54884cece2c8dfbfe3d01b2780171e"}, {file = "winrt_runtime-2.0.0b1-cp311-cp311-win_arm64.whl", hash = "sha256:2afa45b7385e99a63d55ccda29096e6a84fcd4c654479005c147b0e65e274abf"}, {file = "winrt_runtime-2.0.0b1-cp312-cp312-win32.whl", hash = "sha256:edda124ff965cec3a6bfdb26fbe88e004f96975dd84115176e30c1efbcb16f4c"}, {file = "winrt_runtime-2.0.0b1-cp312-cp312-win_amd64.whl", hash = "sha256:d8935951efeec6b3d546dce8f48bb203aface57a1ba991c066f0e12e84c8f91e"}, {file = "winrt_runtime-2.0.0b1-cp312-cp312-win_arm64.whl", hash = "sha256:509fb9a03af5e1125433f58522725716ceef040050d33625460b5a5eb98a46ac"}, {file = "winrt_runtime-2.0.0b1-cp39-cp39-win32.whl", hash = "sha256:41138fe4642345d7143e817ce0905d82e60b3832558143e0a17bfea8654c6512"}, {file = "winrt_runtime-2.0.0b1-cp39-cp39-win_amd64.whl", hash = "sha256:081a429fe85c33cb6610c4a799184b7650b30f15ab1d89866f2bda246d3a5c0a"}, {file = "winrt_runtime-2.0.0b1-cp39-cp39-win_arm64.whl", hash = "sha256:e6984604c6ae1f3258973ba2503d1ea5aa15e536ca41d6a131ad305ebbb6519d"}, ] [[package]] name = "winrt-windows-devices-bluetooth" version = "2.0.0b1" description = "Python projection of Windows Runtime (WinRT) APIs" optional = false python-versions = "<3.13,>=3.9" files = [ {file = "winrt-Windows.Devices.Bluetooth-2.0.0b1.tar.gz", hash = "sha256:786bd43786b873a083b89debece538974f720584662a2573d6a8a8501a532860"}, {file = "winrt_Windows.Devices.Bluetooth-2.0.0b1-cp310-cp310-win32.whl", hash = "sha256:79631bf3f96954da260859df9228a028835ffade0d885ba3942c5a86a853d150"}, {file = "winrt_Windows.Devices.Bluetooth-2.0.0b1-cp310-cp310-win_amd64.whl", hash = "sha256:cd85337a95065d0d2045c06db1a5edd4a447aad47cf7027818f6fb69f831c56c"}, {file = "winrt_Windows.Devices.Bluetooth-2.0.0b1-cp310-cp310-win_arm64.whl", hash = "sha256:6a963869ed003d260e90e9bedc334129303f263f068ea1c0d994df53317db2bc"}, {file = "winrt_Windows.Devices.Bluetooth-2.0.0b1-cp311-cp311-win32.whl", hash = "sha256:7c5951943a3911d94a8da190f4355dc70128d7d7f696209316372c834b34d462"}, {file = "winrt_Windows.Devices.Bluetooth-2.0.0b1-cp311-cp311-win_amd64.whl", hash = "sha256:b0bb154ae92235649ed234982f609c490a467d5049c27d63397be9abbb00730e"}, {file = "winrt_Windows.Devices.Bluetooth-2.0.0b1-cp311-cp311-win_arm64.whl", hash = "sha256:6688dfb0fc3b7dc517bf8cf40ae00544a50b4dec91470d37be38fc33c4523632"}, {file = "winrt_Windows.Devices.Bluetooth-2.0.0b1-cp312-cp312-win32.whl", hash = "sha256:613c6ff4125df46189b3bef6d3110d94ec725d357ab734f00eedb11c4116c367"}, {file = "winrt_Windows.Devices.Bluetooth-2.0.0b1-cp312-cp312-win_amd64.whl", hash = "sha256:59c403b64e9f4e417599c6f6aea6ee6fac960597c21eac6b3fd8a84f64aa387c"}, {file = "winrt_Windows.Devices.Bluetooth-2.0.0b1-cp312-cp312-win_arm64.whl", hash = "sha256:b7f6e1b9bb6e33be80045adebd252cf25cd648759fad6e86c61a393ddd709f7f"}, {file = "winrt_Windows.Devices.Bluetooth-2.0.0b1-cp39-cp39-win32.whl", hash = "sha256:eae7a89106eab047e96843e28c3c6ce0886dd7dee60180a1010498925e9503f9"}, {file = "winrt_Windows.Devices.Bluetooth-2.0.0b1-cp39-cp39-win_amd64.whl", hash = "sha256:8dfd1915c894ac19dd0b24aba38ef676c92c3473c0d9826762ba9616ad7df68b"}, {file = "winrt_Windows.Devices.Bluetooth-2.0.0b1-cp39-cp39-win_arm64.whl", hash = "sha256:49058587e6d82ba33da0767b97a378ddfea8e3a5991bdeff680faa287bfae57e"}, ] [package.dependencies] winrt-runtime = "2.0.0-beta.1" [package.extras] all = ["winrt-Windows.Devices.Bluetooth.GenericAttributeProfile[all] (==2.0.0-beta.1)", "winrt-Windows.Devices.Bluetooth.Rfcomm[all] (==2.0.0-beta.1)", "winrt-Windows.Devices.Enumeration[all] (==2.0.0-beta.1)", "winrt-Windows.Devices.Radios[all] (==2.0.0-beta.1)", "winrt-Windows.Foundation.Collections[all] (==2.0.0-beta.1)", "winrt-Windows.Foundation[all] (==2.0.0-beta.1)", "winrt-Windows.Networking[all] (==2.0.0-beta.1)", "winrt-Windows.Storage.Streams[all] (==2.0.0-beta.1)"] [[package]] name = "winrt-windows-devices-bluetooth-advertisement" version = "2.0.0b1" description = "Python projection of Windows Runtime (WinRT) APIs" optional = false python-versions = "<3.13,>=3.9" files = [ {file = "winrt-Windows.Devices.Bluetooth.Advertisement-2.0.0b1.tar.gz", hash = "sha256:d9050faa4377d410d4f0e9cabb5ec555a267531c9747370555ac9ec93ec9f399"}, {file = "winrt_Windows.Devices.Bluetooth.Advertisement-2.0.0b1-cp310-cp310-win32.whl", hash = "sha256:ac9b703d16adc87c3541585525b8fcf6d84391e2fa010c2f001e714c405cc3b7"}, {file = "winrt_Windows.Devices.Bluetooth.Advertisement-2.0.0b1-cp310-cp310-win_amd64.whl", hash = "sha256:593cade7853a8b0770e8ef30462b5d5f477b82e17e0aa590094b1c26efd3e05a"}, {file = "winrt_Windows.Devices.Bluetooth.Advertisement-2.0.0b1-cp310-cp310-win_arm64.whl", hash = "sha256:574698c08895e2cfee7379bdf34a5f319fe440d7dfcc7bc9858f457c08e9712c"}, {file = "winrt_Windows.Devices.Bluetooth.Advertisement-2.0.0b1-cp311-cp311-win32.whl", hash = "sha256:652a096f8210036bbb539d7f971eaf1f472a3aeb60b7e31278e3d0d30a355292"}, {file = "winrt_Windows.Devices.Bluetooth.Advertisement-2.0.0b1-cp311-cp311-win_amd64.whl", hash = "sha256:e5cfb866c44dad644fb44b441f4fdbddafc9564075f1f68f756e20f438105c67"}, {file = "winrt_Windows.Devices.Bluetooth.Advertisement-2.0.0b1-cp311-cp311-win_arm64.whl", hash = "sha256:6c2503eaaf5cd988b5510b86347dba45ad6ee52656f9656a1a97abae6d35386e"}, {file = "winrt_Windows.Devices.Bluetooth.Advertisement-2.0.0b1-cp312-cp312-win32.whl", hash = "sha256:780c766725a55f4211f921c773c92c2331803e70f65d6ad6676a60f903d39a54"}, {file = "winrt_Windows.Devices.Bluetooth.Advertisement-2.0.0b1-cp312-cp312-win_amd64.whl", hash = "sha256:39c8633d01039eb2c2f6f20cfc43c045a333b9f3a45229e2ce443f71bb2a562c"}, {file = "winrt_Windows.Devices.Bluetooth.Advertisement-2.0.0b1-cp312-cp312-win_arm64.whl", hash = "sha256:eaa0d44b4158b16937eac8102249e792f0299dbb0aefc56cc9adc9552e8f9afe"}, {file = "winrt_Windows.Devices.Bluetooth.Advertisement-2.0.0b1-cp39-cp39-win32.whl", hash = "sha256:d171487e23f7671ad2923544bfa6545d0a29a1a9ae1f5c1d5e5e5f473a5d62b2"}, {file = "winrt_Windows.Devices.Bluetooth.Advertisement-2.0.0b1-cp39-cp39-win_amd64.whl", hash = "sha256:442eecac87653a03617e65bdb2ef79ddc0582dfdacc2be8af841fba541577f8b"}, {file = "winrt_Windows.Devices.Bluetooth.Advertisement-2.0.0b1-cp39-cp39-win_arm64.whl", hash = "sha256:b30ab9b8c1ecf818be08bac86bee425ef40f75060c4011d4e6c2e624a7b9916e"}, ] [package.dependencies] winrt-runtime = "2.0.0-beta.1" [package.extras] all = ["winrt-Windows.Devices.Bluetooth[all] (==2.0.0-beta.1)", "winrt-Windows.Foundation.Collections[all] (==2.0.0-beta.1)", "winrt-Windows.Foundation[all] (==2.0.0-beta.1)", "winrt-Windows.Storage.Streams[all] (==2.0.0-beta.1)"] [[package]] name = "winrt-windows-devices-bluetooth-genericattributeprofile" version = "2.0.0b1" description = "Python projection of Windows Runtime (WinRT) APIs" optional = false python-versions = "<3.13,>=3.9" files = [ {file = "winrt-Windows.Devices.Bluetooth.GenericAttributeProfile-2.0.0b1.tar.gz", hash = "sha256:93b745d51ecfb3e9d3a21623165cc065735c9e0146cb7a26744182c164e63e14"}, {file = "winrt_Windows.Devices.Bluetooth.GenericAttributeProfile-2.0.0b1-cp310-cp310-win32.whl", hash = "sha256:db740aaedd80cca5b1a390663b26c7733eb08f4c57ade6a04b055d548e9d042b"}, {file = "winrt_Windows.Devices.Bluetooth.GenericAttributeProfile-2.0.0b1-cp310-cp310-win_amd64.whl", hash = "sha256:7c81aa6c066cdab58bcc539731f208960e094a6d48b59118898e1e804dbbdf7f"}, {file = "winrt_Windows.Devices.Bluetooth.GenericAttributeProfile-2.0.0b1-cp310-cp310-win_arm64.whl", hash = "sha256:92277a6bbcbe2225ad1be92968af597dc77bc37a63cd729690d2d9fb5094ae25"}, {file = "winrt_Windows.Devices.Bluetooth.GenericAttributeProfile-2.0.0b1-cp311-cp311-win32.whl", hash = "sha256:6b48209669c1e214165530793cf9916ae44a0ae2618a9be7a489e8c94f7e745f"}, {file = "winrt_Windows.Devices.Bluetooth.GenericAttributeProfile-2.0.0b1-cp311-cp311-win_amd64.whl", hash = "sha256:2f17216e6ce748eaef02fb0658213515d3ff31e2dbb18f070a614876f818c90d"}, {file = "winrt_Windows.Devices.Bluetooth.GenericAttributeProfile-2.0.0b1-cp311-cp311-win_arm64.whl", hash = "sha256:db798a0f0762e390da5a9f02f822daff00692bd951a492224bf46782713b2938"}, {file = "winrt_Windows.Devices.Bluetooth.GenericAttributeProfile-2.0.0b1-cp312-cp312-win32.whl", hash = "sha256:b8d9dba04b9cfa53971c35117fc3c68c94bfa5e2ed18ce680f731743598bf246"}, {file = "winrt_Windows.Devices.Bluetooth.GenericAttributeProfile-2.0.0b1-cp312-cp312-win_amd64.whl", hash = "sha256:e5260b3f33dee8a896604297e05efc04d04298329c205a74ded8e2d6333e84b7"}, {file = "winrt_Windows.Devices.Bluetooth.GenericAttributeProfile-2.0.0b1-cp312-cp312-win_arm64.whl", hash = "sha256:822ef539389ecb546004345c4dce8b9b7788e2e99a1d6f0947a4b123dceb7fed"}, {file = "winrt_Windows.Devices.Bluetooth.GenericAttributeProfile-2.0.0b1-cp39-cp39-win32.whl", hash = "sha256:11e6863e7a94d2b6dd76ddcd19c01e311895810a4ce6ad08c7b5534294753243"}, {file = "winrt_Windows.Devices.Bluetooth.GenericAttributeProfile-2.0.0b1-cp39-cp39-win_amd64.whl", hash = "sha256:20de8d04c301c406362c93e78d41912aea0af23c4b430704aba329420d7c2cdf"}, {file = "winrt_Windows.Devices.Bluetooth.GenericAttributeProfile-2.0.0b1-cp39-cp39-win_arm64.whl", hash = "sha256:918059796f2f123216163b928ecde8ecec17994fb7a94042af07fda82c132a6d"}, ] [package.dependencies] winrt-runtime = "2.0.0-beta.1" [package.extras] all = ["winrt-Windows.Devices.Bluetooth[all] (==2.0.0-beta.1)", "winrt-Windows.Devices.Enumeration[all] (==2.0.0-beta.1)", "winrt-Windows.Foundation.Collections[all] (==2.0.0-beta.1)", "winrt-Windows.Foundation[all] (==2.0.0-beta.1)", "winrt-Windows.Storage.Streams[all] (==2.0.0-beta.1)"] [[package]] name = "winrt-windows-devices-enumeration" version = "2.0.0b1" description = "Python projection of Windows Runtime (WinRT) APIs" optional = false python-versions = "<3.13,>=3.9" files = [ {file = "winrt-Windows.Devices.Enumeration-2.0.0b1.tar.gz", hash = "sha256:8f214040e4edbe57c4943488887db89f4a00d028c34169aafd2205e228026100"}, {file = "winrt_Windows.Devices.Enumeration-2.0.0b1-cp310-cp310-win32.whl", hash = "sha256:dcb9e7d230aefec8531a46d393ecb1063b9d4b97c9f3ff2fc537ce22bdfa2444"}, {file = "winrt_Windows.Devices.Enumeration-2.0.0b1-cp310-cp310-win_amd64.whl", hash = "sha256:22a3e1fef40786cc8d51320b6f11ff25de6c674475f3ba608a46915e1dadf0f5"}, {file = "winrt_Windows.Devices.Enumeration-2.0.0b1-cp310-cp310-win_arm64.whl", hash = "sha256:2edcfeb70a71d40622873cad96982a28e92a7ee71f33968212dd3598b2d8d469"}, {file = "winrt_Windows.Devices.Enumeration-2.0.0b1-cp311-cp311-win32.whl", hash = "sha256:ce4eb88add7f5946d2666761a97a3bb04cac2a061d264f03229c1e15dbd7ce91"}, {file = "winrt_Windows.Devices.Enumeration-2.0.0b1-cp311-cp311-win_amd64.whl", hash = "sha256:a9001f17991572abdddab7ab074e08046e74e05eeeaf3b2b01b8b47d2879b64c"}, {file = "winrt_Windows.Devices.Enumeration-2.0.0b1-cp311-cp311-win_arm64.whl", hash = "sha256:0440b91ce144111e207f084cec6b1277162ef2df452d321951e989ce87dc9ced"}, {file = "winrt_Windows.Devices.Enumeration-2.0.0b1-cp312-cp312-win32.whl", hash = "sha256:e4fae13126f13a8d9420b74fb5a5ff6a6b2f91f7718c4be2d4a8dc1337c58f59"}, {file = "winrt_Windows.Devices.Enumeration-2.0.0b1-cp312-cp312-win_amd64.whl", hash = "sha256:e352eebc23dc94fb79e67a056c057fb0e16c20c8cb881dc826094c20ed4791e3"}, {file = "winrt_Windows.Devices.Enumeration-2.0.0b1-cp312-cp312-win_arm64.whl", hash = "sha256:b43f5c1f053a170e6e4b44ba69838ac223f9051adca1a56506d4c46e98d1485f"}, {file = "winrt_Windows.Devices.Enumeration-2.0.0b1-cp39-cp39-win32.whl", hash = "sha256:ed245fad8de6a134d5c3a630204e7f8238aa944a40388005bce0ce3718c410fa"}, {file = "winrt_Windows.Devices.Enumeration-2.0.0b1-cp39-cp39-win_amd64.whl", hash = "sha256:22a9eefdbfe520778512266d0b48ff239eaa8d272fce6f5cb1ff352bed0619f4"}, {file = "winrt_Windows.Devices.Enumeration-2.0.0b1-cp39-cp39-win_arm64.whl", hash = "sha256:397d43f8fd2621a7719b9eab6a4a8e72a1d6fa2d9c36525a30812f8e7bad3bdf"}, ] [package.dependencies] winrt-runtime = "2.0.0-beta.1" [package.extras] all = ["winrt-Windows.ApplicationModel.Background[all] (==2.0.0-beta.1)", "winrt-Windows.Foundation.Collections[all] (==2.0.0-beta.1)", "winrt-Windows.Foundation[all] (==2.0.0-beta.1)", "winrt-Windows.Security.Credentials[all] (==2.0.0-beta.1)", "winrt-Windows.Storage.Streams[all] (==2.0.0-beta.1)", "winrt-Windows.UI.Popups[all] (==2.0.0-beta.1)", "winrt-Windows.UI[all] (==2.0.0-beta.1)"] [[package]] name = "winrt-windows-foundation" version = "2.0.0b1" description = "Python projection of Windows Runtime (WinRT) APIs" optional = false python-versions = "<3.13,>=3.9" files = [ {file = "winrt-Windows.Foundation-2.0.0b1.tar.gz", hash = "sha256:976b6da942747a7ca5a179a35729d8dc163f833e03b085cf940332a5e9070d54"}, {file = "winrt_Windows.Foundation-2.0.0b1-cp310-cp310-win32.whl", hash = "sha256:5337ac1ec260132fbff868603e73a3738d4001911226e72669b3d69c8a256d5e"}, {file = "winrt_Windows.Foundation-2.0.0b1-cp310-cp310-win_amd64.whl", hash = "sha256:af969e5bb9e2e41e4e86a361802528eafb5eb8fe87ec1dba6048c0702d63caa8"}, {file = "winrt_Windows.Foundation-2.0.0b1-cp310-cp310-win_arm64.whl", hash = "sha256:bbbfa6b3c444a1074a630fd4a1b71171be7a5c9bb07c827ad9259fadaed56cf2"}, {file = "winrt_Windows.Foundation-2.0.0b1-cp311-cp311-win32.whl", hash = "sha256:b91bd92b1854c073acd81aa87cf8df571d2151b1dd050b6181aa36f7acc43df4"}, {file = "winrt_Windows.Foundation-2.0.0b1-cp311-cp311-win_amd64.whl", hash = "sha256:2f5359f25703347e827dbac982150354069030f1deecd616f7ce37ad90cbcb00"}, {file = "winrt_Windows.Foundation-2.0.0b1-cp311-cp311-win_arm64.whl", hash = "sha256:0f1f1978173ddf0ee6262c2edb458f62d628b9fa0df10cd1e8c78c833af3197e"}, {file = "winrt_Windows.Foundation-2.0.0b1-cp312-cp312-win32.whl", hash = "sha256:c1d23b737f733104b91c89c507b58d0b3ef5f3234a1b608ef6dfb6dbbb8777ea"}, {file = "winrt_Windows.Foundation-2.0.0b1-cp312-cp312-win_amd64.whl", hash = "sha256:95de6c29e9083fe63f127b965b54dfa52a6424a93a94ce87cfad4c1900a6e887"}, {file = "winrt_Windows.Foundation-2.0.0b1-cp312-cp312-win_arm64.whl", hash = "sha256:4707063a5a6980e3f71aebeea5ac93101c753ec13a0b47be9ea4dbc0d5ff361e"}, {file = "winrt_Windows.Foundation-2.0.0b1-cp39-cp39-win32.whl", hash = "sha256:d0259f1f4a1b8e20d0cbd935a889c0f7234f720645590260f9cf3850fdc1e1fa"}, {file = "winrt_Windows.Foundation-2.0.0b1-cp39-cp39-win_amd64.whl", hash = "sha256:15c7b324d0f59839fb4492d84bb1c870881c5c67cb94ac24c664a7c4dce1c475"}, {file = "winrt_Windows.Foundation-2.0.0b1-cp39-cp39-win_arm64.whl", hash = "sha256:16ad741f4d38e99f8409ba5760299d0052003255f970f49f4b8ba2e0b609c8b7"}, ] [package.dependencies] winrt-runtime = "2.0.0-beta.1" [package.extras] all = ["winrt-Windows.Foundation.Collections[all] (==2.0.0-beta.1)"] [[package]] name = "winrt-windows-foundation-collections" version = "2.0.0b1" description = "Python projection of Windows Runtime (WinRT) APIs" optional = false python-versions = "<3.13,>=3.9" files = [ {file = "winrt-Windows.Foundation.Collections-2.0.0b1.tar.gz", hash = "sha256:185d30f8103934124544a40aac005fa5918a9a7cb3179f45e9863bb86e22ad43"}, {file = "winrt_Windows.Foundation.Collections-2.0.0b1-cp310-cp310-win32.whl", hash = "sha256:042142e916a170778b7154498aae61254a1a94c552954266b73479479d24f01d"}, {file = "winrt_Windows.Foundation.Collections-2.0.0b1-cp310-cp310-win_amd64.whl", hash = "sha256:9f68e66055121fc1e04c4fda627834aceee6fbe922e77d6ccaecf9582e714c57"}, {file = "winrt_Windows.Foundation.Collections-2.0.0b1-cp310-cp310-win_arm64.whl", hash = "sha256:a4609411263cc7f5e93a9a5677b21e2ef130e26f9030bfa960b3e82595324298"}, {file = "winrt_Windows.Foundation.Collections-2.0.0b1-cp311-cp311-win32.whl", hash = "sha256:5296858aa44c53936460a119794b80eedd6bd094016c1bf96822f92cb95ea419"}, {file = "winrt_Windows.Foundation.Collections-2.0.0b1-cp311-cp311-win_amd64.whl", hash = "sha256:3db1e1c80c97474e7c88b6052bd8982ca61723fd58ace11dc91a5522662e0b2a"}, {file = "winrt_Windows.Foundation.Collections-2.0.0b1-cp311-cp311-win_arm64.whl", hash = "sha256:c3a594e660c59f9fab04ae2f40bda7c809e8ec4748bada4424dfb02b43d4bfe1"}, {file = "winrt_Windows.Foundation.Collections-2.0.0b1-cp312-cp312-win32.whl", hash = "sha256:0f355ee943ec5b835e694d97e9e93545a42d6fb984a61f442467789550d62c3f"}, {file = "winrt_Windows.Foundation.Collections-2.0.0b1-cp312-cp312-win_amd64.whl", hash = "sha256:c4a0cd2eb9f47c7ca3b66d12341cc822250bf26854a93fd58ab77f7a48dfab3a"}, {file = "winrt_Windows.Foundation.Collections-2.0.0b1-cp312-cp312-win_arm64.whl", hash = "sha256:744dbef50e8b8f34904083cae9ad43ac6e28facb9e166c4f123ce8e758141067"}, {file = "winrt_Windows.Foundation.Collections-2.0.0b1-cp39-cp39-win32.whl", hash = "sha256:b7c767184aec3a3d7cba2cd84fadcd68106854efabef1a61092052294d6d6f4f"}, {file = "winrt_Windows.Foundation.Collections-2.0.0b1-cp39-cp39-win_amd64.whl", hash = "sha256:7c1ffe99c12f14fc4ab7027757780e6d850fa2fb23ec404a54311fbd9f1970d3"}, {file = "winrt_Windows.Foundation.Collections-2.0.0b1-cp39-cp39-win_arm64.whl", hash = "sha256:870fa040ed36066e4c240c35973d8b2e0d7c38cc6050a42d993715ec9e3b748c"}, ] [package.dependencies] winrt-runtime = "2.0.0-beta.1" [package.extras] all = ["winrt-Windows.Foundation[all] (==2.0.0-beta.1)"] [[package]] name = "winrt-windows-storage-streams" version = "2.0.0b1" description = "Python projection of Windows Runtime (WinRT) APIs" optional = false python-versions = "<3.13,>=3.9" files = [ {file = "winrt-Windows.Storage.Streams-2.0.0b1.tar.gz", hash = "sha256:029d67cdc9b092d56c682740fe3c42f267dc5d3346b5c0b12ebc03f38e7d2f1f"}, {file = "winrt_Windows.Storage.Streams-2.0.0b1-cp310-cp310-win32.whl", hash = "sha256:49c90d4bfd539f6676226dfcb4b3574ddd6be528ffc44aa214c55af88c2de89e"}, {file = "winrt_Windows.Storage.Streams-2.0.0b1-cp310-cp310-win_amd64.whl", hash = "sha256:22cc82779cada84aa2633841e25b33f3357737d912a1d9ecc1ee5a8b799b5171"}, {file = "winrt_Windows.Storage.Streams-2.0.0b1-cp310-cp310-win_arm64.whl", hash = "sha256:b1750a111be32466f4f0781cbb5df195ac940690571dff4564492b921b162563"}, {file = "winrt_Windows.Storage.Streams-2.0.0b1-cp311-cp311-win32.whl", hash = "sha256:e79b1183ab26d9b95cf3e6dbe3f488a40605174a5a112694dbb7dbfb50899daf"}, {file = "winrt_Windows.Storage.Streams-2.0.0b1-cp311-cp311-win_amd64.whl", hash = "sha256:3e90a1207eb3076f051a7785132f7b056b37343a68e9481a50c6defb3f660099"}, {file = "winrt_Windows.Storage.Streams-2.0.0b1-cp311-cp311-win_arm64.whl", hash = "sha256:4da06522b4fa9cfcc046b604cc4aa1c6a887cc4bb5b8a637ed9bff8028a860bb"}, {file = "winrt_Windows.Storage.Streams-2.0.0b1-cp312-cp312-win32.whl", hash = "sha256:6f74f8ab8ac0d8de61c709043315361d8ac63f8144f3098d428472baadf8246a"}, {file = "winrt_Windows.Storage.Streams-2.0.0b1-cp312-cp312-win_amd64.whl", hash = "sha256:5cf7c8d67836c60392d167bfe4f98ac7abcb691bfba2d19e322d0f9181f58347"}, {file = "winrt_Windows.Storage.Streams-2.0.0b1-cp312-cp312-win_arm64.whl", hash = "sha256:f7f679f2c0f71791eca835856f57942ee5245094c1840a6c34bc7c2176b1bcd6"}, {file = "winrt_Windows.Storage.Streams-2.0.0b1-cp39-cp39-win32.whl", hash = "sha256:5beb53429fa9a11ede56b4a7cefe28c774b352dd355f7951f2a4dd7e9ec9b39a"}, {file = "winrt_Windows.Storage.Streams-2.0.0b1-cp39-cp39-win_amd64.whl", hash = "sha256:f84233c4b500279d8f5840cb8c47776bc040fcecba05c6c9ab9767053698fc8b"}, {file = "winrt_Windows.Storage.Streams-2.0.0b1-cp39-cp39-win_arm64.whl", hash = "sha256:cfb163ddbb435906f75ef92a768573b0190e194e1438cea5a4c1d4d32a6b9386"}, ] [package.dependencies] winrt-runtime = "2.0.0-beta.1" [package.extras] all = ["winrt-Windows.Foundation.Collections[all] (==2.0.0-beta.1)", "winrt-Windows.Foundation[all] (==2.0.0-beta.1)", "winrt-Windows.Storage[all] (==2.0.0-beta.1)", "winrt-Windows.System[all] (==2.0.0-beta.1)"] [[package]] name = "zipp" version = "3.19.2" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.8" files = [ {file = "zipp-3.19.2-py3-none-any.whl", hash = "sha256:f091755f667055f2d02b32c53771a7a6c8b47e1fdbc4b72a8b9072b3eef8015c"}, {file = "zipp-3.19.2.tar.gz", hash = "sha256:bf1dcf6450f873a13e952a29504887c89e6de7506209e5b1bcc3460135d4de19"}, ] [package.extras] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"] [extras] tunnel = [] [metadata] lock-version = "2.0" python-versions = "^3.8,<3.13" content-hash = "8e82c70af84ffd1525ece9c446bf06c9a1a1235cdf3bb6c563413daf389de353" python-2.3.14/protobufs/000077500000000000000000000000001464266072200151565ustar00rootroot00000000000000python-2.3.14/pyproject.toml000066400000000000000000000025221464266072200160500ustar00rootroot00000000000000[tool.poetry] name = "meshtastic" version = "2.3.13" description = "Python API & client shell for talking to Meshtastic devices" authors = ["Meshtastic Developers "] license = "GPL-3.0-only" readme = "README.md" [tool.poetry.dependencies] python = "^3.8,<3.13" # was 3.7 for production but, 3.8 is needed for pytap2, 3.9 is needed for pandas, bleak requires a max of 3.13 for some reason pyserial = "^3.5" protobuf = ">=5.26.0" dotmap = "^1.3.30" pexpect = "^4.9.0" pyqrcode = "^1.2.1" tabulate = "^0.9.0" webencodings = "^0.5.1" requests = "^2.31.0" pyparsing = "^3.1.2" pyyaml = "^6.0.1" pypubsub = "^4.0.3" bleak = "^0.21.1" packaging = "^24.0" print-color = "^0.4.6" [tool.poetry.group.dev.dependencies] hypothesis = "^6.103.2" pytest = "^8.2.2" pytest-cov = "^5.0.0" pdoc3 = "^0.10.0" autopep8 = "^2.1.0" pylint = "^3.2.3" pytap2 = "^2.3.0" pyinstaller = "^6.8.0" mypy = "^1.10.0" mypy-protobuf = "^3.6.0" types-protobuf = "^5.26.0.20240422" types-tabulate = "^0.9.0.20240106" types-requests = "^2.31.0.20240406" types-setuptools = "^69.5.0.20240423" types-pyyaml = "^6.0.12.20240311" [tool.poetry.extras] tunnel = ["pytap2"] [tool.poetry.scripts] meshtastic = "meshtastic.__main__:main" mesh-tunnel = "meshtastic.__main__:tunnelMain [tunnel]" [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" python-2.3.14/pytest.ini000066400000000000000000000011511464266072200151620ustar00rootroot00000000000000[pytest] addopts = -m "not int and not smoke1 and not smoke2 and not smokewifi and not examples and not smokevirt" filterwarnings = ignore::DeprecationWarning markers = unit: marks tests as unit tests unitslow: marks slow unit tests int: marks tests as integration tests smokevirt: marks tests as smoke tests against virtual device smoke1: runs smoke tests on a single device connected via USB smoke2: runs smoke tests on a two devices connected via USB smokewifi: runs smoke test on an esp32 device setup with wifi examples: runs the examples tests which validates the library python-2.3.14/standalone_readme.txt000066400000000000000000000006541464266072200173460ustar00rootroot00000000000000readme.txt for single standalone executable zip files that can be downloaded from https://github.com/meshtastic/python/releases If you do not want to install python and/or the python libraries, you can download one of these files to run the Meshtastic command line interface (CLI) as a standalone executable. See https://meshtastic.org/docs/software/python/cli/installation/#standalone-installation-ubuntu-only for more info. python-2.3.14/tests/000077500000000000000000000000001464266072200142755ustar00rootroot00000000000000python-2.3.14/tests/close-bug.py000066400000000000000000000005331464266072200165300ustar00rootroot00000000000000import datetime import logging import sys from pubsub import pub import meshtastic # logging.basicConfig(level=logging.DEBUG) print(str(datetime.datetime.now()) + ": start") interface = meshtastic.TCPInterface(sys.argv[1]) print(str(datetime.datetime.now()) + ": middle") interface.close() print(str(datetime.datetime.now()) + ": after close") python-2.3.14/tests/hello_world.py000066400000000000000000000003771464266072200171700ustar00rootroot00000000000000import meshtastic.serial_interface interface = ( meshtastic.serial_interface.SerialInterface() ) # By default will try to find a meshtastic device, otherwise provide a device path like /dev/ttyUSB0 interface.sendText("hello mesh") interface.close() python-2.3.14/tests/tcp-test.py000066400000000000000000000005361464266072200164160ustar00rootroot00000000000000# reported by @ScriptBlock import sys from pubsub import pub import meshtastic def onConnection( interface, topic=pub.AUTO_TOPIC ): # called when we (re)connect to the radio print(interface.myInfo) interface.close() pub.subscribe(onConnection, "meshtastic.connection.established") interface = meshtastic.TCPInterface(sys.argv[1]) python-2.3.14/tests/tuntest.py000066400000000000000000000060401464266072200163550ustar00rootroot00000000000000# delete me eventually # Note python-pytuntap was too buggy # using pip3 install pytap2 # make sure to "sudo setcap cap_net_admin+eip /usr/bin/python3.8" so python can access tun device without being root # sudo ip tuntap del mode tun tun0 # FIXME: set MTU correctly # select local ip address based on nodeid # print known node ids as IP addresses import logging from _thread import start_new_thread from pytap2 import TapDevice """A list of chatty UDP services we should never accidentally forward to our slow network""" udpBlacklist = { 1900, # SSDP 5353, # multicast DNS } """A list of TCP services to block""" tcpBlacklist = {} """A list of protocols we ignore""" protocolBlacklist = { 0x02, # IGMP 0x80, # Service-Specific Connection-Oriented Protocol in a Multilink and Connectionless Environment } def hexstr(barray): """Print a string of hex digits""" return ":".join("{:02x}".format(x) for x in barray) def ipstr(barray): """Print a string of ip digits""" return ".".join("{}".format(x) for x in barray) def readnet_u16(p, offset): """Read big endian u16 (network byte order)""" return p[offset] * 256 + p[offset + 1] def readtest(tap): while True: p = tap.read() protocol = p[8 + 1] srcaddr = p[12:16] destaddr = p[16:20] subheader = 20 ignore = False # Assume we will be forwarding the packet if protocol in protocolBlacklist: ignore = True logging.debug(f"Ignoring blacklisted protocol 0x{protocol:02x}") elif protocol == 0x01: # ICMP logging.warn("Generating fake ping reply") # reply to pings (swap src and dest but keep rest of packet unchanged) pingback = p[:12] + p[16:20] + p[12:16] + p[20:] tap.write(pingback) elif protocol == 0x11: # UDP srcport = readnet_u16(p, subheader) destport = readnet_u16(p, subheader + 2) logging.debug(f"udp srcport={srcport}, destport={destport}") if destport in udpBlacklist: ignore = True logging.debug(f"ignoring blacklisted UDP port {destport}") elif protocol == 0x06: # TCP srcport = readnet_u16(p, subheader) destport = readnet_u16(p, subheader + 2) logging.debug(f"tcp srcport={srcport}, destport={destport}") if destport in tcpBlacklist: ignore = True logging.debug(f"ignoring blacklisted TCP port {destport}") else: logging.warning( f"unexpected protocol 0x{protocol:02x}, src={ipstr(srcaddr)}, dest={ipstr(destaddr)}" ) if not ignore: logging.debug( f"Forwarding packet bytelen={len(p)} src={ipstr(srcaddr)}, dest={ipstr(destaddr)}" ) logging.basicConfig(level=logging.DEBUG) tun = TapDevice(mtu=200) # tun.create() tun.up() tun.ifconfig(address="10.115.1.2", netmask="255.255.0.0") start_new_thread(readtest, (tun,)) input("press return key to quit!") tun.close() python-2.3.14/vercel.json000066400000000000000000000000511464266072200153020ustar00rootroot00000000000000{ "github": { "silent": true } }