├── .coveragerc ├── .gitattributes ├── .github ├── meshtastic_logo.png └── workflows │ ├── ci.yml │ ├── cleanup_artifacts.yml │ ├── release.yml │ └── update_protobufs.yml ├── .gitignore ├── .gitmodules ├── .pylintrc ├── .reuse └── dep5 ├── .trunk ├── .gitignore ├── configs │ ├── .isort.cfg │ ├── .markdownlint.yaml │ ├── .shellcheckrc │ ├── .yamllint.yaml │ └── ruff.toml └── trunk.yaml ├── .vscode ├── launch.json └── settings.json ├── LICENSES └── GPL-3.0-only.txt ├── MANIFEST.in ├── Makefile ├── README.md ├── TODO.md ├── bin ├── build-bin.sh ├── git-resolve-poetry-lock.sh ├── prerelease-tests.sh ├── regen-docs.sh ├── regen-protobufs.sh ├── run-ci-local.sh ├── test-release.sh └── upload-release.sh ├── exampleConfig.yaml ├── example_config.yaml ├── examples ├── get_hw.py ├── hello_world_serial.py ├── info_example.py ├── pub_sub_example.py ├── pub_sub_example2.py ├── scan_for_devices.py ├── set_owner.py ├── show_ports.py ├── tcp_gps_example.py └── waypoint.py ├── extra └── meshtastic_tun.rules ├── info ├── mac │ ├── heltec.txt │ ├── nano_g1.txt │ ├── rak11200.txt │ ├── rak4631_19003.txt │ ├── rak4631_5005.txt │ ├── tbeam.txt │ ├── techo.txt │ ├── tlora.txt │ └── tlora_2.1.6.txt ├── readme.txt ├── ubuntu │ ├── diy.txt │ ├── heltec_v2.txt │ ├── nano_g1.txt │ ├── rak4631_19003.txt │ ├── rak4631_5005.txt │ ├── readme.txt │ ├── tbeam.txt │ ├── techo.txt │ └── tlora.txt └── windows │ ├── heltec.txt │ ├── nano_g1.txt │ ├── rak4631_19003.txt │ ├── rak4631_5005.txt │ ├── tbeam.txt │ ├── techo.txt │ ├── tlora.txt │ └── tlora_v1.txt ├── meshtastic ├── .gitignore ├── __init__.py ├── __main__.py ├── analysis │ ├── __init__.py │ └── __main__.py ├── ble_interface.py ├── mesh_interface.py ├── mt_config.py ├── node.py ├── powermon │ ├── __init__.py │ ├── power_supply.py │ ├── ppk2.py │ ├── riden.py │ ├── sim.py │ └── stress.py ├── protobuf │ ├── __init__.py │ ├── admin_pb2.py │ ├── admin_pb2.pyi │ ├── apponly_pb2.py │ ├── apponly_pb2.pyi │ ├── atak_pb2.py │ ├── atak_pb2.pyi │ ├── cannedmessages_pb2.py │ ├── cannedmessages_pb2.pyi │ ├── channel_pb2.py │ ├── channel_pb2.pyi │ ├── clientonly_pb2.py │ ├── clientonly_pb2.pyi │ ├── config_pb2.py │ ├── config_pb2.pyi │ ├── connection_status_pb2.py │ ├── connection_status_pb2.pyi │ ├── device_ui_pb2.py │ ├── device_ui_pb2.pyi │ ├── deviceonly_pb2.py │ ├── deviceonly_pb2.pyi │ ├── interdevice_pb2.py │ ├── interdevice_pb2.pyi │ ├── localonly_pb2.py │ ├── localonly_pb2.pyi │ ├── mesh_pb2.py │ ├── mesh_pb2.pyi │ ├── module_config_pb2.py │ ├── module_config_pb2.pyi │ ├── mqtt_pb2.py │ ├── mqtt_pb2.pyi │ ├── nanopb_pb2.py │ ├── nanopb_pb2.pyi │ ├── paxcount_pb2.py │ ├── paxcount_pb2.pyi │ ├── portnums_pb2.py │ ├── portnums_pb2.pyi │ ├── powermon_pb2.py │ ├── powermon_pb2.pyi │ ├── remote_hardware_pb2.py │ ├── remote_hardware_pb2.pyi │ ├── rtttl_pb2.py │ ├── rtttl_pb2.pyi │ ├── storeforward_pb2.py │ ├── storeforward_pb2.pyi │ ├── telemetry_pb2.py │ ├── telemetry_pb2.pyi │ ├── xmodem_pb2.py │ └── xmodem_pb2.pyi ├── remote_hardware.py ├── serial_interface.py ├── slog │ ├── __init__.py │ ├── arrow.py │ └── slog.py ├── stream_interface.py ├── supported_device.py ├── tcp_interface.py ├── test.py ├── tests │ ├── __init__.py │ ├── conftest.py │ ├── slog-test-input │ │ ├── power.feather │ │ ├── raw.txt │ │ └── slog.feather │ ├── test_analysis.py │ ├── test_examples.py │ ├── test_init.py │ ├── test_int.py │ ├── test_main.py │ ├── test_mesh_interface.py │ ├── test_node.py │ ├── test_remote_hardware.py │ ├── test_serial_interface.py │ ├── test_smoke1.py │ ├── test_smoke2.py │ ├── test_smoke_wifi.py │ ├── test_smokevirt.py │ ├── test_stream_interface.py │ ├── test_tcp_interface.py │ ├── test_tunnel.py │ └── test_util.py ├── tunnel.py ├── util.py └── version.py ├── poetry.lock ├── pyproject.toml ├── pytest.ini ├── standalone_readme.txt ├── tests ├── close-bug.py ├── hello_world.py ├── tcp-test.py └── tuntest.py └── vercel.json /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | omit = meshtastic/protobuf/*_pb2.py,meshtastic/tests/*.py,meshtastic/test.py 3 | 4 | [report] 5 | exclude_lines = 6 | if __name__ == .__main__.: 7 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | *.{cmd,[cC][mM][dD]} text eol=crlf 3 | *.{bat,[bB][aA][tT]} text eol=crlf 4 | *.{sh,[sS][hH]} text eol=lf 5 | meshtastic/protobuf/* linguist-generated=true 6 | -------------------------------------------------------------------------------- /.github/meshtastic_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meshtastic/python/622a435465872fd316054f1a4a7dd6d4d44bb3ec/.github/meshtastic_logo.png -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: 5 | - master 6 | pull_request: 7 | branches: 8 | - master 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | strategy: 14 | matrix: 15 | python-version: 16 | - "3.9" 17 | - "3.10" 18 | - "3.11" 19 | - "3.12" 20 | steps: 21 | - uses: actions/checkout@v4 22 | - name: Install Python 3 23 | uses: actions/setup-python@v5 24 | - name: Uninstall meshtastic 25 | run: | 26 | pip3 uninstall -y meshtastic 27 | - name: Install dependencies 28 | run: | 29 | python -m pip install --upgrade pip 30 | pip3 install poetry 31 | - name: Install meshtastic from local 32 | run: | 33 | poetry install --all-extras --with dev,powermon 34 | poetry run meshtastic --version 35 | - name: Run pylint 36 | run: poetry run pylint meshtastic examples/ --ignore-patterns ".*_pb2.pyi?$" 37 | - name: Check types with mypy 38 | run: poetry run mypy meshtastic/ 39 | - name: Run tests with pytest 40 | run: poetry run pytest --cov=meshtastic 41 | - name: Generate coverage report 42 | run: | 43 | poetry run pytest --cov=meshtastic --cov-report=xml 44 | - name: Upload coverage to Codecov 45 | uses: codecov/codecov-action@v4 46 | with: 47 | token: ${{ secrets.CODECOV_TOKEN }} 48 | env_vars: OS, PYTHON 49 | files: ./coverage.xml 50 | flags: unittests 51 | name: codecov-umbrella 52 | fail_ci_if_error: true 53 | verbose: true 54 | validate: 55 | runs-on: ubuntu-latest 56 | strategy: 57 | matrix: 58 | python-version: 59 | - "3.9" 60 | - "3.10" 61 | - "3.11" 62 | - "3.12" 63 | steps: 64 | - uses: actions/checkout@v4 65 | - name: Install Python 3 66 | uses: actions/setup-python@v5 67 | - name: Install meshtastic from local 68 | run: | 69 | python -m pip install --upgrade pip 70 | pip3 install poetry 71 | poetry install 72 | poetry run meshtastic --version 73 | -------------------------------------------------------------------------------- /.github/workflows/cleanup_artifacts.yml: -------------------------------------------------------------------------------- 1 | name: Remove old artifacts 2 | 3 | on: 4 | schedule: 5 | # Every day at 1am 6 | - cron: "0 1 * * *" 7 | 8 | workflow_dispatch: 9 | 10 | jobs: 11 | remove-old-artifacts: 12 | runs-on: ubuntu-latest 13 | timeout-minutes: 10 14 | 15 | steps: 16 | - name: Remove old artifacts 17 | uses: c-hive/gha-remove-artifacts@v1 18 | with: 19 | age: "1 month" 20 | skip-tags: true 21 | -------------------------------------------------------------------------------- /.github/workflows/update_protobufs.yml: -------------------------------------------------------------------------------- 1 | name: "Update protobufs" 2 | on: workflow_dispatch 3 | 4 | jobs: 5 | update-protobufs: 6 | runs-on: ubuntu-latest 7 | 8 | steps: 9 | - name: Checkout code 10 | uses: actions/checkout@v4 11 | with: 12 | submodules: true 13 | 14 | - name: Update Submodule 15 | run: | 16 | git pull --recurse-submodules 17 | git submodule update --remote --recursive 18 | 19 | - name: Download nanopb 20 | run: | 21 | wget https://jpa.kapsi.fi/nanopb/download/nanopb-0.4.8-linux-x86.tar.gz 22 | tar xvzf nanopb-0.4.8-linux-x86.tar.gz 23 | mv nanopb-0.4.8-linux-x86 nanopb-0.4.8 24 | 25 | - name: Install poetry (needed by regen-protobufs.sh) 26 | run: | 27 | python -m pip install --upgrade pip 28 | pip3 install poetry 29 | 30 | - name: Re-generate protocol buffers 31 | run: | 32 | ./bin/regen-protobufs.sh 33 | 34 | - name: Commit update 35 | run: | 36 | git config --global user.name 'github-actions' 37 | git config --global user.email 'bot@noreply.github.com' 38 | git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }} 39 | git add protobufs 40 | git add meshtastic/protobuf 41 | git commit -m "Update protobuf submodule" && git push || echo "No changes to commit" 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | README 2 | docs/ 3 | build 4 | dist 5 | *.egg-info 6 | log_* 7 | .eggs 8 | nanopb-* 9 | .*swp 10 | .coverage 11 | *.py-E 12 | venv/ 13 | *pyc 14 | .DS_Store 15 | __pycache__ 16 | examples/__pycache__ 17 | meshtastic.spec 18 | .hypothesis/ 19 | coverage.xml 20 | .ipynb_checkpoints -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "protobufs"] 2 | path = protobufs 3 | url = http://github.com/meshtastic/protobufs 4 | -------------------------------------------------------------------------------- /.pylintrc: -------------------------------------------------------------------------------- 1 | # pylint configuration file 2 | # 3 | # Note: "pylint --generate-rcfile" is helpful to see what values to add to this file 4 | 5 | 6 | [MASTER] 7 | 8 | # Add files or directories matching the regex patterns to the blacklist. The 9 | # regex matches against base names, not paths. 10 | 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 11 | 12 | 13 | 14 | [MESSAGES CONTROL] 15 | 16 | # Disable the message, report, category or checker with the given id(s). You 17 | # can either give multiple identifiers separated by comma (,) or put this 18 | # option multiple times (only on the command line, not in the configuration 19 | # file where it should appear only once).You can also use "--disable=all" to 20 | # disable everything first and then reenable specific checks. For example, if 21 | # you want to run only the similarities checker, you can use "--disable=all 22 | # --enable=similarities". If you want to run only the classes checker, but have 23 | # no Warning level messages displayed, use"--disable=all --enable=classes 24 | # --disable=W" 25 | # 26 | 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,nested-min-max 27 | 28 | [BASIC] 29 | 30 | # Good variable names which should always be accepted, separated by a comma 31 | good-names=i,j,k,ex,Run,_ 32 | 33 | # Bad variable names which should always be refused, separated by a comma 34 | bad-names=foo,bar,baz,toto,tutu,tata 35 | 36 | 37 | [FORMAT] 38 | 39 | # Maximum number of characters on a single line. 40 | max-line-length=150 41 | 42 | # Maximum number of lines in a module 43 | max-module-lines=1600 44 | 45 | 46 | 47 | [REFACTORING] 48 | 49 | # Maximum number of nested blocks for function / method body 50 | max-nested-blocks=10 51 | 52 | 53 | 54 | [MISCELLANEOUS] 55 | 56 | # List of note tags to take in consideration, separated by a comma. 57 | notes=FIXME,fixme,XXX,TODO 58 | 59 | 60 | [SIMILARITIES] 61 | 62 | # Minimum lines number of a similarity. 63 | min-similarity-lines=30 64 | 65 | # Ignore comments when computing similarities. 66 | ignore-comments=yes 67 | 68 | # Ignore docstrings when computing similarities. 69 | ignore-docstrings=yes 70 | 71 | # Ignore imports when computing similarities. 72 | ignore-imports=yes 73 | 74 | 75 | 76 | [DESIGN] 77 | 78 | # Maximum number of arguments for function / method. 79 | max-args=10 80 | 81 | # Maximum number of attributes for a class (see R0902). 82 | max-attributes=20 83 | -------------------------------------------------------------------------------- /.reuse/dep5: -------------------------------------------------------------------------------- 1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: Meshtastic Python 3 | Upstream-Contact: Various Authors 4 | Source: https://github.com/meshtastic/python 5 | 6 | Files: * 7 | Copyright: 8 | 2020-2020 Nils Werner 9 | 2020-2020 Paul Vivier 10 | 2020-2020 Tim Gunter 11 | 2020-2021 Charles Crossan 12 | 2020-2021 IZ1IVA 13 | 2020-2022 Jm Casler 14 | 2020-2024 geeksville 15 | 2021-2021 Andrew Cabey 16 | 2021-2021 dylan 17 | 2021-2021 Fabian Affolter 18 | 2021-2021 Foster Irwin 19 | 2021-2021 Manuel Giolo 20 | 2021-2021 meehow 21 | 2021-2021 srichs 22 | 2021-2021 ChuckNorrison 23 | 2021-2021 Aivaras-s 24 | 2021-2021 a-f-G-U-C 25 | 2021-2021 26 | 2021-2021 jdstroy 27 | 2021-2021 linagee 28 | 2021-2021 Simonas 29 | 2021-2022 30 | 2021-2023 Sacha Weatherstone 31 | 2021-2024 Ben Meadors 32 | 2022-2022 Balázs Kelemen <10376327+prampec 33 | 2022-2022 34 | 2022-2022 35 | 2022-2022 36 | 2022-2022 37 | 2022-2022 38 | 2022-2022 Rohan King 39 | 2022-2022 Tom Douile 40 | 2022-2023 Thomas Göttgens 41 | 2022-2024 thijs@havinga.eu> 42 | 2023-2023 Eli Schleifer 43 | 2023-2023 Manuel 44 | 2023-2023 Marek Küthe 45 | 2023-2023 46 | 2023-2023 47 | 2023-2023 48 | 2023-2023 luzpaz 49 | 2023-2023 50 | 2023-2023 Toby Murray 51 | 2023-2024 Brad Midgley 52 | 2024-2024 Ian McEwen 53 | 2024-2024 John Hollowell 54 | 2024-2024 Jonathan Bennett 55 | 2024-2024 56 | 2024-2024 57 | 2024-2024 58 | 2024-2024 59 | 2024-2024 rc14193 60 | 2024-2024 Steve Holden 61 | 2024-2024 Thomas Herrmann 62 | 2024-2024 Timothy Harder 63 | 2024-2024 Wolfgang Nagele 64 | License: GPL-3.0-only 65 | -------------------------------------------------------------------------------- /.trunk/.gitignore: -------------------------------------------------------------------------------- 1 | *out 2 | *logs 3 | *actions 4 | *notifications 5 | *tools 6 | plugins 7 | user_trunk.yaml 8 | user.yaml 9 | -------------------------------------------------------------------------------- /.trunk/configs/.isort.cfg: -------------------------------------------------------------------------------- 1 | [settings] 2 | profile=black 3 | -------------------------------------------------------------------------------- /.trunk/configs/.markdownlint.yaml: -------------------------------------------------------------------------------- 1 | # Autoformatter friendly markdownlint config (all formatting rules disabled) 2 | default: true 3 | blank_lines: false 4 | bullet: false 5 | html: false 6 | indentation: false 7 | line_length: false 8 | spaces: false 9 | url: false 10 | whitespace: false 11 | -------------------------------------------------------------------------------- /.trunk/configs/.shellcheckrc: -------------------------------------------------------------------------------- 1 | enable=all 2 | source-path=SCRIPTDIR 3 | disable=SC2154 4 | 5 | # If you're having issues with shellcheck following source, disable the errors via: 6 | # disable=SC1090 7 | # disable=SC1091 8 | -------------------------------------------------------------------------------- /.trunk/configs/.yamllint.yaml: -------------------------------------------------------------------------------- 1 | rules: 2 | quoted-strings: 3 | required: only-when-needed 4 | extra-allowed: ["{|}"] 5 | empty-values: 6 | forbid-in-block-mappings: true 7 | forbid-in-flow-mappings: true 8 | key-duplicates: {} 9 | octal-values: 10 | forbid-implicit-octal: true 11 | -------------------------------------------------------------------------------- /.trunk/configs/ruff.toml: -------------------------------------------------------------------------------- 1 | # Generic, formatter-friendly config. 2 | select = ["B", "D3", "D4", "E", "F"] 3 | 4 | # Never enforce `E501` (line length violations). This should be handled by formatters. 5 | ignore = ["E501"] 6 | -------------------------------------------------------------------------------- /.trunk/trunk.yaml: -------------------------------------------------------------------------------- 1 | version: 0.1 2 | cli: 3 | version: 1.15.0 4 | plugins: 5 | sources: 6 | - id: trunk 7 | ref: v1.2.2 8 | uri: https://github.com/trunk-io/plugins 9 | lint: 10 | disabled: 11 | - bandit 12 | ignore: 13 | - linters: [ALL] 14 | paths: 15 | # Ignore generated files 16 | - meshtastic/*_pb2.py 17 | enabled: 18 | - actionlint@1.6.25 19 | - black@23.7.0 20 | - checkov@2.4.9 21 | - git-diff-check 22 | - gitleaks@8.18.0 23 | - isort@5.12.0 24 | - markdownlint@0.36.0 25 | - osv-scanner@1.3.6 26 | - prettier@3.0.3 27 | - pylint@2.17.5 28 | - ruff@0.0.287 29 | - shellcheck@0.9.0 30 | - shfmt@3.6.0 31 | - taplo@0.8.1 32 | - trivy@0.44.1 33 | - trufflehog@3.54.3 34 | - yamllint@1.32.0 35 | runtimes: 36 | enabled: 37 | - go@1.21.0 38 | - node@18.12.1 39 | - python@3.10.8 40 | actions: 41 | disabled: 42 | - trunk-announce 43 | - trunk-check-pre-push 44 | - trunk-fmt-pre-commit 45 | enabled: 46 | - trunk-upgrade-available 47 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": [ 3 | "bitmask", 4 | "boardid", 5 | "DEEPSLEEP", 6 | "Meshtastic", 7 | "milliwatt", 8 | "portnums", 9 | "powermon", 10 | "POWERSTRESS", 11 | "pyarrow", 12 | "TORADIO", 13 | "Vids" 14 | ], 15 | "python.pythonPath": "/usr/bin/python3", 16 | "flake8.enabled": false, 17 | "python.testing.pytestArgs": [ 18 | "meshtastic/tests" 19 | ], 20 | "python.testing.unittestEnabled": false, 21 | "python.testing.pytestEnabled": true // we are using trunk for formatting/linting rules, don't yell at us about line length 22 | } -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.md 2 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # only run the fast unit tests 2 | test: 3 | pytest -m unit 4 | 5 | # only run the smoke tests against the virtual device 6 | virt: 7 | pytest -m smokevirt 8 | 9 | # run the smoke1 test (after doing a factory reset and unplugging/replugging in device) 10 | smoke1: 11 | pytest -m smoke1 -s -vv 12 | 13 | # local install 14 | install: 15 | pip install . 16 | 17 | # generate the docs (for local use) 18 | docs: 19 | pdoc3 --html -f --output-dir docs meshtastic 20 | 21 | # lint the codebase 22 | lint: 23 | pylint meshtastic examples 24 | 25 | # show the slowest unit tests 26 | slow: 27 | pytest -m unit --durations=5 28 | 29 | protobufs: FORCE 30 | git submodule update --init --recursive 31 | git pull --rebase 32 | git submodule update --remote --merge 33 | ./bin/regen-protobufs.sh 34 | 35 | # run the coverage report and open results in a browser 36 | cov: 37 | pytest --cov-report html --cov=meshtastic 38 | # on mac, this will open the coverage report in a browser 39 | open htmlcov/index.html 40 | 41 | # run cli examples 42 | examples: FORCE 43 | pytest -mexamples 44 | 45 | # Makefile hack to get the examples to always run 46 | FORCE: ; 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | Meshtastic Logo 4 | 5 |

Meshtastic Python 6 |

7 |

A Python library and client for use with Meshtastic devices.

8 | 9 | [![codecov](https://codecov.io/gh/meshtastic/python/branch/master/graph/badge.svg?token=TIWPJL73KV)](https://codecov.io/gh/meshtastic/python) 10 | ![PyPI - Downloads](https://img.shields.io/pypi/dm/meshtastic) 11 | [![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) 12 | [![CLA assistant](https://cla-assistant.io/readme/badge/meshtastic/python)](https://cla-assistant.io/meshtastic/python) 13 | [![Fiscal Contributors](https://opencollective.com/meshtastic/tiers/badge.svg?label=Fiscal%20Contributors&color=deeppink)](https://opencollective.com/meshtastic/) 14 | ![GPL-3.0](https://img.shields.io/badge/License-GPL%20v3-blue.svg) 15 | 16 |
17 | 18 | 23 | 24 | ## Overview 25 | 26 | This small library (and example application) provides an easy API for sending and receiving messages over mesh radios. 27 | It also provides access to any of the operations/data available in the device user interface or the Android application. 28 | Events are delivered using a publish-subscribe model, and you can subscribe to only the message types you are interested in. 29 | 30 | ## Call for Contributors 31 | 32 | 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. 33 | 34 | If you're interested in contributing but don't have specific things you'd like to work on, look at the roadmap below! 35 | 36 | ## Roadmap 37 | 38 | 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. 39 | 40 | * Types 41 | * type annotations throughout the codebase, and upgrading mypy running in CI to `--strict` 42 | * async-friendliness 43 | * CLI completeness & consistency 44 | * the CLI should support all features of the firmware 45 | * there should be a consistent output format available for shell scripting 46 | * CLI input validation & documentation 47 | * what arguments and options are compatible & incompatible with one another? 48 | * can the options be restructured in a way that is more self-documenting? 49 | * pubsub events should be documented clearly 50 | * helpers for third-party code 51 | * 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 52 | * data storage & processing 53 | * there should be a standardized way of recording packets for later use, debugging, etc. 54 | * a persistence layer could also keep track of nodes beyond nodedb, as the apps do 55 | * a sqlite database schema and tools for writing to it may be a good starting point 56 | * enable maps, charts, visualizations 57 | 58 | ## Stats 59 | 60 | ![Alt](https://repobeats.axiom.co/api/embed/c71ee8fc4a79690402e5d2807a41eec5e96d9039.svg "Repobeats analytics image") 61 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # TODO 2 | 3 | Basic functionality is complete now. 4 | 5 | ## Eventual tasks 6 | 7 | - Improve documentation on properties/fields 8 | - include more examples: textchat.py, replymessage.py all as one little demo 9 | 10 | - possibly use tk to make a multiwindow test console: https://stackoverflow.com/questions/12351786/how-to-redirect-print-statements-to-tkinter-text-widget 11 | 12 | ## MeshtasticShell todos 13 | 14 | - Possibly use multiple windows: https://stackoverflow.com/questions/12351786/how-to-redirect-print-statements-to-tkinter-text-widget 15 | - make pingpong test 16 | 17 | ## Bluetooth support 18 | 19 | - ./bin/run.sh --ble-scan # To look for Meshtastic devices 20 | - ./bin/run.sh --ble 24:62:AB:DD:DF:3A --info 21 | 22 | ## Done 23 | 24 | - DONE use port enumeration to find ports https://pyserial.readthedocs.io/en/latest/shortintro.html 25 | - DONE make serial debug output optional (by providing a null stream) 26 | - DONE make pubsub work 27 | - DONE make docs decent 28 | - DONE keep everything in dicts 29 | - DONE have device send a special packet at boot so the serial client can detect if it rebooted 30 | - DONE add fromId and toId to received messages dictionaries 31 | - make command line options for displaying/changing config 32 | - update nodedb as nodes change 33 | - localConfig - getter/setter syntax: https://www.python-course.eu/python3_properties.php 34 | - let user change radio params via commandline options 35 | - keep nodedb up-to-date based on received MeshPackets 36 | - handle radio reboots and redownload db when that happens. Look for a special FromRadio.rebooted packet 37 | -------------------------------------------------------------------------------- /bin/build-bin.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | echo Building ubuntu binary 6 | poetry install 7 | source $(poetry env info --path)/bin/activate 8 | pyinstaller -F -n meshtastic --collect-all meshtastic meshtastic/__main__.py 9 | 10 | -------------------------------------------------------------------------------- /bin/git-resolve-poetry-lock.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | # This is a little helper you can use to resolve git merge conflicts in poetry.lock 6 | # with minimal changes vs the requested lib versions 7 | # Based on this article with a good description of best practices: 8 | # https://www.peterbe.com/plog/how-to-resolve-a-git-conflict-in-poetry.lock 9 | 10 | echo "Resolving poetry.lock merge conflicts, you'll need to run git commit yourself..." 11 | 12 | # Get poetry.lock to look like it does in master 13 | git checkout --theirs poetry.lock 14 | # Rewrite the lock file 15 | poetry lock --no-update 16 | git add poetry.lock 17 | 18 | # Update your poetry env to match the new merged lock file 19 | poetry install 20 | -------------------------------------------------------------------------------- /bin/prerelease-tests.sh: -------------------------------------------------------------------------------- 1 | set -e 2 | 3 | # You may consider running: "pytest -m smoke1" instead of this test. 4 | 5 | echo "Linting" 6 | poetry run pylint meshtastic examples/ --ignore-patterns ".*_pb2.pyi?$" 7 | 8 | echo "Checking types" 9 | poetry run mypy meshtastic/ 10 | 11 | echo "Running (crude) prerelease tests to verify sanity" 12 | 13 | # Use the python environment created by poetry 14 | source $(poetry env info --path)/bin/activate 15 | 16 | echo running hello 17 | python3 tests/hello_world.py 18 | # meshtastic --help 19 | echo toggling router 20 | meshtastic --set is_router true 21 | meshtastic --set is_router false 22 | # TODO: This does not seem to work. 23 | echo setting channel 24 | meshtastic --seturl "https://www.meshtastic.org/c/#GAMiENTxuzogKQdZ8Lz_q89Oab8qB0RlZmF1bHQ=" 25 | echo setting owner 26 | meshtastic --set-owner "Test Build" 27 | echo setting position 28 | meshtastic --setlat 32.7767 --setlon -96.7970 --setalt 1337 29 | echo dumping info 30 | meshtastic run meshtastic --info 31 | echo sending closing message 32 | meshtastic --sendtext "Sanity complete" 33 | -------------------------------------------------------------------------------- /bin/regen-docs.sh: -------------------------------------------------------------------------------- 1 | # Note: Docs are generated from this command below, albeit from Vercel. 2 | # The docs/ dir is not used and is no longer committed. 3 | # see sachaw if you have questions 4 | pdoc3 --html -f --output-dir docs meshtastic 5 | -------------------------------------------------------------------------------- /bin/regen-protobufs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | #Uncomment to run hack 6 | #gsed -i 's/import "\//import ".\//g' ./protobufs/meshtastic/* 7 | #gsed -i 's/package meshtastic;//g' ./protobufs/meshtastic/* 8 | 9 | # protoc looks for mypy plugin in the python path 10 | source $(poetry env info --path)/bin/activate 11 | 12 | # Put our temp files in the poetry build directory 13 | TMPDIR=./build/meshtastic/protofixup 14 | echo "Fixing up protobuf paths in ${TMPDIR} temp directory" 15 | 16 | 17 | # Ensure a clean build 18 | [ -e "${TMPDIR}" ] && rm -r "${TMPDIR}" 19 | 20 | INDIR=${TMPDIR}/in/meshtastic/protobuf 21 | OUTDIR=${TMPDIR}/out 22 | PYIDIR=${TMPDIR}/out 23 | mkdir -p "${OUTDIR}" "${INDIR}" "${PYIDIR}" 24 | cp ./protobufs/meshtastic/*.proto "${INDIR}" 25 | cp ./protobufs/nanopb.proto "${INDIR}" 26 | 27 | # OS-X sed is apparently a little different and expects an arg for -i 28 | if [[ $OSTYPE == 'darwin'* ]]; then 29 | SEDCMD="sed -i '' -E" 30 | else 31 | SEDCMD="sed -i -E" 32 | fi 33 | 34 | 35 | # change the package names to meshtastic.protobuf 36 | $SEDCMD 's/^package meshtastic;/package meshtastic.protobuf;/' "${INDIR}/"*.proto 37 | # fix the imports to match 38 | $SEDCMD 's/^import "meshtastic\//import "meshtastic\/protobuf\//' "${INDIR}/"*.proto 39 | 40 | $SEDCMD 's/^import "nanopb.proto"/import "meshtastic\/protobuf\/nanopb.proto"/' "${INDIR}/"*.proto 41 | 42 | # Generate the python files 43 | ./nanopb-0.4.8/generator-bin/protoc -I=$TMPDIR/in --python_out "${OUTDIR}" "--mypy_out=${PYIDIR}" $INDIR/*.proto 44 | 45 | # Change "from meshtastic.protobuf import" to "from . import" 46 | $SEDCMD 's/^from meshtastic.protobuf import/from . import/' "${OUTDIR}"/meshtastic/protobuf/*pb2*.py[i] 47 | 48 | # Create a __init__.py in the out directory 49 | touch "${OUTDIR}/meshtastic/protobuf/__init__.py" 50 | 51 | # Copy to the source controlled tree 52 | mkdir -p meshtastic/protobuf 53 | rm -rf meshtastic/protobuf/*pb2*.py 54 | cp "${OUTDIR}/meshtastic/protobuf"/* meshtastic/protobuf 55 | 56 | exit 0 57 | -------------------------------------------------------------------------------- /bin/run-ci-local.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script lets you run github ci actions locally 4 | # You need to have act installed. You can get it at https://nektosact.com/ 5 | 6 | # by default it simulates a push event 7 | # other useful options 8 | # -j build-and-publish-ubuntu 9 | 10 | # also: we only run one of the 4 matrix tests, because otherwise it absolutely hammers the CPU (so many containers and threads) 11 | act -P ubuntu-latest=-self-hosted --matrix "python-version:3.8" "$@" -------------------------------------------------------------------------------- /bin/test-release.sh: -------------------------------------------------------------------------------- 1 | rm dist/* 2 | set -e 3 | 4 | bin/regen-docs.sh 5 | pandoc --from=markdown --to=rst --output=README README.md 6 | 7 | poetry publish -r test-pypi --build 8 | echo "view the upload at https://test.pypi.org/ it it looks good upload for real" 9 | -------------------------------------------------------------------------------- /bin/upload-release.sh: -------------------------------------------------------------------------------- 1 | rm dist/* 2 | set -e 3 | 4 | poetry build 5 | poetry run pytest 6 | poetry publish 7 | #python3 setup.py sdist bdist_wheel 8 | #python3 -m twine upload dist/* 9 | -------------------------------------------------------------------------------- /exampleConfig.yaml: -------------------------------------------------------------------------------- 1 | # example config using camelCase keys 2 | owner: Bob TBeam 3 | ownerShort: BOB 4 | 5 | channelUrl: https://www.meshtastic.org/d/#CgUYAyIBAQ 6 | 7 | location: 8 | lat: 35.88888 9 | lon: -93.88888 10 | alt: 304 11 | 12 | userPrefs: 13 | region: 1 14 | isAlwaysPowered: "true" 15 | screenOnSecs: 31536000 16 | waitBluetoothSecs: 31536000 17 | -------------------------------------------------------------------------------- /example_config.yaml: -------------------------------------------------------------------------------- 1 | # example configuration file with snake_case keys 2 | owner: Bob TBeam 3 | owner_short: BOB 4 | 5 | channel_url: https://www.meshtastic.org/e/#CgMSAQESCDgBQANIAVAe 6 | 7 | location: 8 | lat: 35.88888 9 | lon: -93.88888 10 | alt: 304 11 | 12 | config: 13 | bluetooth: 14 | enabled: true 15 | fixedPin: 123456 16 | device: 17 | serialEnabled: true 18 | display: 19 | screenOnSecs: 600 20 | lora: 21 | region: US 22 | hopLimit: 3 23 | txEnabled: true 24 | txPower: 30 25 | network: 26 | ntpServer: 0.pool.ntp.org 27 | position: 28 | gpsAttemptTime: 900 29 | gpsEnabled: true 30 | gpsUpdateInterval: 120 31 | positionBroadcastSecs: 900 32 | positionBroadcastSmartEnabled: true 33 | positionFlags: 3 34 | power: 35 | lsSecs: 300 36 | meshSdsTimeoutSecs: 7200 37 | minWakeSecs: 10 38 | sdsSecs: 4294967295 39 | 40 | module_config: 41 | telemetry: 42 | deviceUpdateInterval: 900 43 | environmentUpdateInterval: 900 44 | -------------------------------------------------------------------------------- /examples/get_hw.py: -------------------------------------------------------------------------------- 1 | """Simple program to demo how to use meshtastic library. 2 | To run: python examples/get_hw.py 3 | """ 4 | 5 | import sys 6 | 7 | import meshtastic 8 | import meshtastic.serial_interface 9 | 10 | # simple arg check 11 | if len(sys.argv) != 1: 12 | print(f"usage: {sys.argv[0]}") 13 | print("Print the hardware model for the local node.") 14 | sys.exit(3) 15 | 16 | iface = meshtastic.serial_interface.SerialInterface() 17 | if iface.nodes: 18 | for n in iface.nodes.values(): 19 | if n["num"] == iface.myInfo.my_node_num: 20 | print(n["user"]["hwModel"]) 21 | iface.close() 22 | -------------------------------------------------------------------------------- /examples/hello_world_serial.py: -------------------------------------------------------------------------------- 1 | """Simple program to demo how to use meshtastic library. 2 | To run: python examples/hello_world_serial.py 3 | """ 4 | 5 | import sys 6 | 7 | import meshtastic 8 | import meshtastic.serial_interface 9 | 10 | # simple arg check 11 | if len(sys.argv) < 2: 12 | print(f"usage: {sys.argv[0]} message") 13 | sys.exit(3) 14 | 15 | # By default will try to find a meshtastic device, 16 | # otherwise provide a device path like /dev/ttyUSB0 17 | iface = meshtastic.serial_interface.SerialInterface() 18 | iface.sendText(sys.argv[1]) 19 | iface.close() 20 | -------------------------------------------------------------------------------- /examples/info_example.py: -------------------------------------------------------------------------------- 1 | """Simple program to demo how to use meshtastic library. 2 | To run: python examples/info.py 3 | """ 4 | 5 | import meshtastic 6 | import meshtastic.serial_interface 7 | 8 | iface = meshtastic.serial_interface.SerialInterface() 9 | 10 | # call showInfo() just to ensure values are populated 11 | # info = iface.showInfo() 12 | 13 | 14 | if iface.nodes: 15 | for n in iface.nodes.values(): 16 | if n["num"] == iface.myInfo.my_node_num: 17 | print(n["user"]["hwModel"]) 18 | break 19 | 20 | iface.close() 21 | -------------------------------------------------------------------------------- /examples/pub_sub_example.py: -------------------------------------------------------------------------------- 1 | """Simple program to demo how to use meshtastic library. 2 | To run: python examples/pub_sub_example.py 3 | """ 4 | 5 | import sys 6 | 7 | from pubsub import pub 8 | 9 | import meshtastic 10 | import meshtastic.tcp_interface 11 | 12 | # simple arg check 13 | if len(sys.argv) < 2: 14 | print(f"usage: {sys.argv[0]} host") 15 | sys.exit(1) 16 | 17 | 18 | def onConnection(interface, topic=pub.AUTO_TOPIC): # pylint: disable=unused-argument 19 | """This is called when we (re)connect to the radio.""" 20 | print(interface.myInfo) 21 | interface.close() 22 | 23 | 24 | pub.subscribe(onConnection, "meshtastic.connection.established") 25 | 26 | try: 27 | iface = meshtastic.tcp_interface.TCPInterface(sys.argv[1]) 28 | except: 29 | print(f"Error: Could not connect to {sys.argv[1]}") 30 | sys.exit(1) 31 | -------------------------------------------------------------------------------- /examples/pub_sub_example2.py: -------------------------------------------------------------------------------- 1 | """Simple program to demo how to use meshtastic library. 2 | To run: python examples/pub_sub_example2.py 3 | """ 4 | 5 | import sys 6 | import time 7 | 8 | from pubsub import pub 9 | 10 | import meshtastic 11 | import meshtastic.tcp_interface 12 | 13 | # simple arg check 14 | if len(sys.argv) < 2: 15 | print(f"usage: {sys.argv[0]} host") 16 | sys.exit(1) 17 | 18 | 19 | def onReceive(packet, interface): # pylint: disable=unused-argument 20 | """called when a packet arrives""" 21 | print(f"Received: {packet}") 22 | 23 | 24 | def onConnection(interface, topic=pub.AUTO_TOPIC): # pylint: disable=unused-argument 25 | """called when we (re)connect to the radio""" 26 | # defaults to broadcast, specify a destination ID if you wish 27 | interface.sendText("hello mesh") 28 | 29 | 30 | pub.subscribe(onReceive, "meshtastic.receive") 31 | pub.subscribe(onConnection, "meshtastic.connection.established") 32 | try: 33 | iface = meshtastic.tcp_interface.TCPInterface(hostname=sys.argv[1]) 34 | while True: 35 | time.sleep(1000) 36 | iface.close() 37 | except Exception as ex: 38 | print(f"Error: Could not connect to {sys.argv[1]} {ex}") 39 | sys.exit(1) 40 | -------------------------------------------------------------------------------- /examples/scan_for_devices.py: -------------------------------------------------------------------------------- 1 | """Program to scan for hardware 2 | To run: python examples/scan_for_devices.py 3 | """ 4 | 5 | import sys 6 | 7 | from meshtastic.util import ( 8 | active_ports_on_supported_devices, 9 | detect_supported_devices, 10 | get_unique_vendor_ids, 11 | ) 12 | 13 | # simple arg check 14 | if len(sys.argv) != 1: 15 | print(f"usage: {sys.argv[0]}") 16 | print("Detect which device we might have.") 17 | sys.exit(3) 18 | 19 | vids = get_unique_vendor_ids() 20 | print(f"Searching for all devices with these vendor ids {vids}") 21 | 22 | sds = detect_supported_devices() 23 | if len(sds) > 0: 24 | print("Detected possible devices:") 25 | for d in sds: 26 | print(f" name:{d.name}{d.version} firmware:{d.for_firmware}") 27 | 28 | ports = active_ports_on_supported_devices(sds) 29 | print(f"ports:{ports}") 30 | -------------------------------------------------------------------------------- /examples/set_owner.py: -------------------------------------------------------------------------------- 1 | """Simple program to demo how to use meshtastic library. 2 | To run: python examples/set_owner.py Bobby 333 3 | """ 4 | 5 | import sys 6 | 7 | import meshtastic 8 | import meshtastic.serial_interface 9 | 10 | # simple arg check 11 | if len(sys.argv) < 2: 12 | print(f"usage: {sys.argv[0]} long_name [short_name]") 13 | sys.exit(3) 14 | 15 | iface = meshtastic.serial_interface.SerialInterface() 16 | long_name = sys.argv[1] 17 | short_name = None 18 | if len(sys.argv) > 2: 19 | short_name = sys.argv[2] 20 | iface.localNode.setOwner(long_name, short_name) 21 | iface.close() 22 | -------------------------------------------------------------------------------- /examples/show_ports.py: -------------------------------------------------------------------------------- 1 | """Simple program to show serial ports. 2 | """ 3 | 4 | from meshtastic.util import findPorts 5 | 6 | print(findPorts()) 7 | -------------------------------------------------------------------------------- /examples/tcp_gps_example.py: -------------------------------------------------------------------------------- 1 | """Demonstration of how to look up a radio's location via its LAN connection. 2 | Before running, connect your machine to the same WiFi network as the radio. 3 | """ 4 | 5 | import meshtastic 6 | import meshtastic.tcp_interface 7 | 8 | radio_hostname = "meshtastic.local" # Can also be an IP 9 | iface = meshtastic.tcp_interface.TCPInterface(radio_hostname) 10 | my_node_num = iface.myInfo.my_node_num 11 | pos = iface.nodesByNum[my_node_num]["position"] 12 | print(pos) 13 | 14 | iface.close() 15 | -------------------------------------------------------------------------------- /examples/waypoint.py: -------------------------------------------------------------------------------- 1 | """Program to create and delete waypoint 2 | To run: 3 | python3 examples/waypoint.py --port /dev/ttyUSB0 create 45 test the_desc_2 '2024-12-18T23:05:23' 48.74 7.35 4 | python3 examples/waypoint.py delete 45 5 | """ 6 | 7 | import argparse 8 | import datetime 9 | import sys 10 | 11 | import meshtastic 12 | import meshtastic.serial_interface 13 | 14 | parser = argparse.ArgumentParser( 15 | prog='waypoint', 16 | description='Create and delete Meshtastic waypoint') 17 | parser.add_argument('--port', default=None) 18 | parser.add_argument('--debug', default=False, action='store_true') 19 | 20 | subparsers = parser.add_subparsers(dest='cmd') 21 | parser_delete = subparsers.add_parser('delete', help='Delete a waypoint') 22 | parser_delete.add_argument('id', help="id of the waypoint") 23 | 24 | parser_create = subparsers.add_parser('create', help='Create a new waypoint') 25 | parser_create.add_argument('id', help="id of the waypoint") 26 | parser_create.add_argument('name', help="name of the waypoint") 27 | parser_create.add_argument('description', help="description of the waypoint") 28 | parser_create.add_argument('expire', help="expiration date of the waypoint as interpreted by datetime.fromisoformat") 29 | parser_create.add_argument('latitude', help="latitude of the waypoint") 30 | parser_create.add_argument('longitude', help="longitude of the waypoint") 31 | 32 | args = parser.parse_args() 33 | print(args) 34 | 35 | # By default will try to find a meshtastic device, 36 | # otherwise provide a device path like /dev/ttyUSB0 37 | if args.debug: 38 | d = sys.stderr 39 | else: 40 | d = None 41 | with meshtastic.serial_interface.SerialInterface(args.port, debugOut=d) as iface: 42 | if args.cmd == 'create': 43 | p = iface.sendWaypoint( 44 | waypoint_id=int(args.id), 45 | name=args.name, 46 | description=args.description, 47 | expire=int(datetime.datetime.fromisoformat(args.expire).timestamp()), 48 | latitude=float(args.latitude), 49 | longitude=float(args.longitude), 50 | ) 51 | else: 52 | p = iface.deleteWaypoint(int(args.id)) 53 | print(p) 54 | 55 | # iface.close() 56 | -------------------------------------------------------------------------------- /extra/meshtastic_tun.rules: -------------------------------------------------------------------------------- 1 | # for userspace access to the TUN network interface by meshtastic. Add your user to the netdev group. 2 | # gives access via /dev/net/tun permissions 3 | # install via sudo cp meshtastic_tun.rules /etc/udev/rules.d/ 4 | KERNEL=="tun", GROUP="netdev", MODE="0660", OPTIONS+="static_node=net/tun" 5 | -------------------------------------------------------------------------------- /info/mac/heltec.txt: -------------------------------------------------------------------------------- 1 | ioreg -p IOUSB > /tmp/d 2 | 3 | > | +-o CP2102 USB to UART Bridge Controller@14400000 4 | 5 | 6 | system_profiler SPUSBDataType > /tmp/b 7 | 8 | 37a38,50 9 | > CP2102 USB to UART Bridge Controller: 10 | > 11 | > Product ID: 0xea60 12 | > Vendor ID: 0x10c4 (Silicon Laboratories, Inc.) 13 | > Version: 1.00 14 | > Serial Number: 0001 15 | > Speed: Up to 12 Mb/s 16 | > Manufacturer: Silicon Labs 17 | > Location ID: 0x14400000 / 53 18 | > Current Available (mA): 500 19 | > Current Required (mA): 100 20 | > Extra Operating Current (mA): 0 21 | -------------------------------------------------------------------------------- /info/mac/nano_g1.txt: -------------------------------------------------------------------------------- 1 | meshtastic detected port: /dev/cu.wchusbserial53820208781 2 | 3 | ioreg -p IOUSB 4 | 5 | shows this: 6 | 7 | | +-o USB Single Serial@14300000 8 | 9 | 10 | 11 | 12 | system_profiler SPUSBDataType > /tmp/a 13 | with device plugged in 14 | 15 | system_profiler SPUSBDataType > /tmp/b 16 | with device not plugged in 17 | 18 | diff /tmp/a /tmp/b 19 | 20 | < USB Single Serial: 21 | < 22 | < Product ID: 0x55d4 23 | < Vendor ID: 0x1a86 24 | < Version: 4.43 25 | < Serial Number: 5382020878 26 | < Speed: Up to 12 Mb/s 27 | < Location ID: 0x14300000 / 63 28 | < Current Available (mA): 500 29 | < Current Required (mA): 134 30 | < Extra Operating Current (mA): 0 31 | -------------------------------------------------------------------------------- /info/mac/rak11200.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | % ioreg -p IOUSB > /tmp/a 4 | # only a solid red light (pins GRND and BOOT0 jumpered) 5 | 6 | % ioreg -p IOUSB > /tmp/b 7 | # solid red light and solid green light 8 | 9 | % ioreg -p IOUSB > /tmp/c 10 | # nothing plugged in 11 | 12 | 13 | 14 | % diff /tmp/a /tmp/c 15 | 13c13 16 | < +-o AppleUSBXHCI Root Hub Simulation@14000000 17 | --- 18 | > +-o AppleUSBXHCI Root Hub Simulation@14000000 19 | 18d17 20 | < +-o USB Serial@14300000 21 | 22 | 23 | % diff /tmp/b /tmp/c 24 | 13c13 25 | < +-o AppleUSBXHCI Root Hub Simulation@14000000 26 | --- 27 | > +-o AppleUSBXHCI Root Hub Simulation@14000000 28 | 18d17 29 | < +-o USB Serial@14300000 30 | 31 | 32 | 33 | system_profiler SPUSBDataType > /tmp/d 34 | # red solid 35 | 36 | 37 | system_profiler SPUSBDataType > /tmp/e 38 | # nothing 39 | 40 | 41 | % diff /tmp/d /tmp/e 42 | 38,48d37 43 | < USB Serial: 44 | < 45 | < Product ID: 0x7523 46 | < Vendor ID: 0x1a86 47 | < Version: 2.64 48 | < Speed: Up to 12 Mb/s 49 | < Location ID: 0x14300000 / 33 50 | < Current Available (mA): 500 51 | < Current Required (mA): 98 52 | < Extra Operating Current (mA): 0 53 | -------------------------------------------------------------------------------- /info/mac/rak4631_19003.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | > | +-o WisCore RAK4631 Board@14400000 4 | 5 | 6 | /dev/cu.usbmodem14401 7 | 8 | % ls -al /dev/*modem* 9 | crw-rw-rw- 1 root wheel 0x9000005 Jan 29 15:32 /dev/cu.usbmodem14401 10 | crw-rw-rw- 1 root wheel 0x9000004 Jan 29 15:31 /dev/tty.usbmodem14401 11 | 12 | 13 | Note: On a Mac Air, output is: 14 | 15 | % system_profiler SPUSBDataType 16 | USB: 17 | USB 3.1 Bus: 18 | Host Controller Driver: AppleT8103USBXHCI 19 | USB 3.1 Bus: 20 | Host Controller Driver: AppleT8103USBXHCI 21 | WisCore RAK4631 Board: 22 | Product ID: 0x8029 23 | Vendor ID: 0x239a 24 | Version: 1.00 25 | Serial Number: E6CF9502B1D410D8 26 | Speed: Up to 12 Mb/s 27 | Manufacturer: RAKwireless 28 | Location ID: 0x01100000 / 2 29 | Current Available (mA): 500 30 | Current Required (mA): 100 31 | Extra Operating Current (mA): 0 32 | 33 | However, in FTHR840BOOT mode, it shows this: 34 | 35 | % system_profiler SPUSBDataType 36 | USB: 37 | USB 3.1 Bus: 38 | Host Controller Driver: AppleT8103USBXHCI 39 | USB 3.1 Bus: 40 | Host Controller Driver: AppleT8103USBXHCI 41 | Feather nRF52840 Express: 42 | Product ID: 0x0029 43 | Vendor ID: 0x239a 44 | Version: 1.00 45 | Serial Number: E6CF9502B1D410D8 46 | Speed: Up to 12 Mb/s 47 | Manufacturer: Adafruit Industries 48 | Location ID: 0x01100000 / 1 49 | Current Available (mA): 500 50 | Current Required (mA): 100 51 | Extra Operating Current (mA): 0 52 | Media: 53 | nRF UF2: 54 | Capacity: 33.7 MB (33,690,112 bytes) 55 | Removable Media: Yes 56 | BSD Name: disk4 57 | Logical Unit: 0 58 | Partition Map Type: Unknown 59 | S.M.A.R.T. status: Verified 60 | USB Interface: 2 61 | 62 | 63 | $ cat /Volumes/FTHR840BOOT/INFO_UF2.TXT 64 | UF2 Bootloader 0.3.2-109-gd6b28e6-dirty lib/nrfx (v2.0.0) lib/tinyusb (0.6.0-272-g4e6aa0d8) lib/uf2 (heads/master) 65 | Model: Adafruit Feather nRF52840 Express 66 | Board-ID: nRF52840-Feather-revD 67 | SoftDevice: S140 version 6.1.1 68 | Date: Jun 16 2020 69 | -------------------------------------------------------------------------------- /info/mac/rak4631_5005.txt: -------------------------------------------------------------------------------- 1 | 2 | no device plugged in 3 | % ioreg -p IOUSB > /tmp/a 4 | 5 | device plugged in, in "boot" mode 6 | % ioreg -p IOUSB > /tmp/b 7 | 8 | device plugged in, botted to Meshtastic firmware 9 | % ioreg -p IOUSB > /tmp/c 10 | 11 | (venv) sweet Meshtastic-python % diff /tmp/a /tmp/b (with most info removed) 12 | > | +-o Feather nRF52840 Express@14400000 13 | 14 | diff /tmp/a /tmp/c (with most info removed) 15 | > | +-o WisCore RAK4631 Board@14400000 16 | 17 | 18 | Meshtastic detected port on /dev/cu.usbmodem14401 19 | -------------------------------------------------------------------------------- /info/mac/tbeam.txt: -------------------------------------------------------------------------------- 1 | 2 | meshtastic detected port: /dev/cu.usbmodem53230050571 3 | 4 | ioreg -p IOUSB > /tmp/c 5 | 6 | > | +-o USB Single Serial@14400000 7 | 8 | 9 | system_profiler SPUSBDataType > /tmp/a 10 | 11 | > USB Single Serial: 12 | > 13 | > Product ID: 0x55d4 14 | > Vendor ID: 0x1a86 15 | > Version: 4.43 16 | > Serial Number: 5323005057 17 | > Speed: Up to 12 Mb/s 18 | > Location ID: 0x14400000 / 50 19 | > Current Available (mA): 500 20 | > Current Required (mA): 134 21 | > Extra Operating Current (mA): 0 22 | -------------------------------------------------------------------------------- /info/mac/techo.txt: -------------------------------------------------------------------------------- 1 | 2 | in boot mode: 3 | 4 | % mount 5 | 6 | /dev/disk122 on /Volumes/TECHOBOOT (msdos, local, nodev, nosuid, noowners) 7 | 8 | % ls -al /Volumes/TECHOBOOT 9 | total 3735 10 | drwxrwxrwx@ 1 bob staff 2048 Feb 1 16:47 . 11 | drwxr-xr-x 5 root wheel 160 Feb 1 16:47 .. 12 | drwxrwxrwx 1 bob staff 512 Feb 1 16:47 .fseventsd 13 | -rwxrwxrwx 1 bob staff 1908736 Oct 13 08:37 CURRENT.UF2 14 | -rwxrwxrwx 1 bob staff 129 Oct 13 08:37 INDEX.HTM 15 | -rwxrwxrwx 1 bob staff 237 Oct 13 08:37 INFO_UF2.TXT 16 | 17 | 18 | # nothing plugged in 19 | % ioreg -p IOUSB > /tmp/a 20 | # not boot mode 21 | % ioreg -p IOUSB > /tmp/b 22 | # bootmode 23 | % ioreg -p IOUSB > /tmp/c 24 | 25 | 26 | % diff /tmp/a /tmp/b 27 | 28 | > | +-o TTGO_eink@14300000 29 | 30 | % diff /tmp/a /tmp/c 31 | 32 | > | +-o T-Echo v1@14300000 33 | 34 | contents of: INFO_UF2.TXT 35 | 36 | 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) 37 | Model: LilyGo T-Echo 38 | Board-ID: nRF52840-TEcho-v1 39 | SoftDevice: S140 version 6.1.1 40 | Date: Oct 13 2021 41 | -------------------------------------------------------------------------------- /info/mac/tlora.txt: -------------------------------------------------------------------------------- 1 | diff of ioreg -p IOUSB 2 | 3 | > | +-o USB Single Serial@14300000 4 | 5 | 6 | Diff of system_profiler SPUSBDataType 7 | < USB Single Serial: 8 | < 9 | < Product ID: 0x55d4 10 | < Vendor ID: 0x1a86 11 | < Version: 4.43 12 | < Speed: Up to 12 Mb/s 13 | < Location ID: 0x14300000 / 46 14 | < Current Available (mA): 500 15 | < Current Required (mA): 134 16 | < Extra Operating Current (mA): 0 17 | -------------------------------------------------------------------------------- /info/mac/tlora_2.1.6.txt: -------------------------------------------------------------------------------- 1 | lsusb 2 | 3 | Bus 001 Device 001: ID 0bda:2172 Realtek Semiconductor Corp. BillBoard Device Serial: 00000000000000000 4 | Bus 000 Device 002: ID 2109:0817 VIA Labs, Inc. USB3.0 Hub Serial: 000000000 5 | Bus 000 Device 003: ID 2109:0715 VIA Labs, Inc. VLI Product String Serial: 000000075003 6 | Bus 000 Device 004: ID 0bda:0306 Realtek Semiconductor Corp. USB3.0-CRW Serial: 60000719201300000 7 | Bus 000 Device 005: ID 2109:0817 VIA Labs, Inc. USB3.0 Hub Serial: 000000000 8 | Bus 000 Device 001: ID 2109:2817 VIA Labs, Inc. USB2.0 Hub Serial: 000000000 9 | Bus 000 Device 009: ID 1a86:55d4 1a86 USB Single Serial Serial: 533C005215 10 | Bus 000 Device 006: ID 2109:2817 VIA Labs, Inc. USB2.0 Hub Serial: 000000000 11 | Bus 000 Device 007: ID 2109:8817 VIA Labs, Inc. USB Billboard Device Serial: 0000000000000001 12 | Bus 000 Device 008: ID 2109:8817 VIA Labs, Inc. USB Billboard Device Serial: 0000000000000001 13 | Bus 002 Device 001: ID 1a40:0101 TERMINUS TECHNOLOGY INC. USB 2.0 Hub 14 | Bus 002 Device 003: ID 0922:001f Dymo Corporation DYMO LabelWriter 4XL Serial: 17032316350940 15 | Bus 002 Device 002: ID 046d:082d Logitech Inc. HD Pro Webcam C920 Serial: A21C905F 16 | Bus 000 Device 000: ID 0bda:2172 Realtek Semiconductor Corp. USB 3.1 Bus 17 | Bus 000 Device 000: ID 2109:0817 VIA Labs, Inc. USB 3.1 Bus 18 | Bus 000 Device 001: ID 1d6b:1100 Linux Foundation USB 3.0 Bus 19 | 20 | % lsusb -v (with parts snipped) 21 | 22 | USB2.0 Hub : 23 | 24 | Product ID: 0x2817 25 | Vendor ID: 0x2109 (VIA Labs, Inc.) 26 | Version: 6.03 27 | Serial Number: 000000000 28 | Speed: Up to 480 Mb/s 29 | Manufacturer: VIA Labs, Inc. 30 | Location ID: 0x00100000 / 1 31 | Current Available (mA): 500 32 | Current Required (mA): 0 33 | Extra Operating Current (mA): 0 34 | 35 | USB Single Serial: 36 | 37 | Product ID: 0x55d4 38 | Vendor ID: 0x1a86 39 | Version: 4.43 40 | Serial Number: 533C005215 41 | Speed: Up to 12 Mb/s 42 | Location ID: 0x00140000 / 9 43 | Current Available (mA): 500 44 | Current Required (mA): 134 45 | Extra Operating Current (mA): 0 46 | 47 | USB2.0 Hub : 48 | 49 | Product ID: 0x2817 50 | Vendor ID: 0x2109 (VIA Labs, Inc.) 51 | Version: 6.03 52 | Serial Number: 000000000 53 | Speed: Up to 480 Mb/s 54 | Manufacturer: VIA Labs, Inc. 55 | Location ID: 0x00110000 / 6 56 | Current Available (mA): 500 57 | Current Required (mA): 0 58 | Extra Operating Current (mA): 0 59 | 60 | USB Billboard Device : 61 | 62 | Product ID: 0x8817 63 | Vendor ID: 0x2109 (VIA Labs, Inc.) 64 | Version: 0.01 65 | Serial Number: 0000000000000001 66 | Speed: Up to 480 Mb/s 67 | Manufacturer: VIA Labs, Inc. 68 | Location ID: 0x00115000 / 7 69 | Current Available (mA): 500 70 | Current Required (mA): 100 71 | Extra Operating Current (mA): 0 72 | 73 | USB Billboard Device : 74 | 75 | Product ID: 0x8817 76 | Vendor ID: 0x2109 (VIA Labs, Inc.) 77 | Version: 0.01 78 | Serial Number: 0000000000000001 79 | Speed: Up to 480 Mb/s 80 | Manufacturer: VIA Labs, Inc. 81 | Location ID: 0x00150000 / 8 82 | Current Available (mA): 500 83 | Current Required (mA): 100 84 | Extra Operating Current (mA): 0 85 | 86 | -------------------------------------------------------------------------------- /info/readme.txt: -------------------------------------------------------------------------------- 1 | Gathering OS-level info for devices. 2 | 3 | This info might be helpful for developers detecting info about devices. 4 | -------------------------------------------------------------------------------- /info/ubuntu/diy.txt: -------------------------------------------------------------------------------- 1 | lsusb 2 | 3 | Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub 4 | Bus 001 Device 004: ID 10c4:ea60 Silicon Labs CP210x UART Bridge 5 | Bus 001 Device 003: ID 0bda:2838 Realtek Semiconductor Corp. RTL2838 DVB-T 6 | Bus 001 Device 002: ID 2109:3431 VIA Labs, Inc. Hub 7 | Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub 8 | 9 | lsusb -d 10c4: -v 10 | 11 | Bus 001 Device 004: ID 10c4:ea60 Silicon Labs CP210x UART Bridge 12 | Couldn't open device, some information will be missing 13 | Device Descriptor: 14 | bLength 18 15 | bDescriptorType 1 16 | bcdUSB 2.00 17 | bDeviceClass 0 18 | bDeviceSubClass 0 19 | bDeviceProtocol 0 20 | bMaxPacketSize0 64 21 | idVendor 0x10c4 Silicon Labs 22 | idProduct 0xea60 CP210x UART Bridge 23 | bcdDevice 1.00 24 | iManufacturer 1 25 | iProduct 2 26 | iSerial 3 27 | bNumConfigurations 1 28 | Configuration Descriptor: 29 | bLength 9 30 | bDescriptorType 2 31 | wTotalLength 0x0020 32 | bNumInterfaces 1 33 | bConfigurationValue 1 34 | iConfiguration 0 35 | bmAttributes 0x80 36 | (Bus Powered) 37 | MaxPower 100mA 38 | Interface Descriptor: 39 | bLength 9 40 | bDescriptorType 4 41 | bInterfaceNumber 0 42 | bAlternateSetting 0 43 | bNumEndpoints 2 44 | bInterfaceClass 255 Vendor Specific Class 45 | bInterfaceSubClass 0 46 | bInterfaceProtocol 0 47 | iInterface 2 48 | Endpoint Descriptor: 49 | bLength 7 50 | bDescriptorType 5 51 | bEndpointAddress 0x01 EP 1 OUT 52 | bmAttributes 2 53 | Transfer Type Bulk 54 | Synch Type None 55 | Usage Type Data 56 | wMaxPacketSize 0x0040 1x 64 bytes 57 | bInterval 0 58 | Endpoint Descriptor: 59 | bLength 7 60 | bDescriptorType 5 61 | bEndpointAddress 0x82 EP 2 IN 62 | bmAttributes 2 63 | Transfer Type Bulk 64 | Synch Type None 65 | Usage Type Data 66 | wMaxPacketSize 0x0040 1x 64 bytes 67 | bInterval 0 68 | -------------------------------------------------------------------------------- /info/ubuntu/heltec_v2.txt: -------------------------------------------------------------------------------- 1 | Run on Ubuntu 20 2 | 3 | Command: 4 | lsusb -d 10c4: -v 5 | 6 | Output: 7 | Bus 001 Device 091: ID 10c4:ea60 Silicon Labs CP210x UART Bridge 8 | Device Descriptor: 9 | bLength 18 10 | bDescriptorType 1 11 | bcdUSB 1.10 12 | bDeviceClass 0 13 | bDeviceSubClass 0 14 | bDeviceProtocol 0 15 | bMaxPacketSize0 64 16 | idVendor 0x10c4 Silicon Labs 17 | idProduct 0xea60 CP210x UART Bridge 18 | bcdDevice 1.00 19 | iManufacturer 1 20 | iProduct 2 21 | iSerial 3 22 | bNumConfigurations 1 23 | Configuration Descriptor: 24 | bLength 9 25 | bDescriptorType 2 26 | wTotalLength 0x0020 27 | bNumInterfaces 1 28 | bConfigurationValue 1 29 | iConfiguration 0 30 | bmAttributes 0x80 31 | (Bus Powered) 32 | MaxPower 100mA 33 | Interface Descriptor: 34 | bLength 9 35 | bDescriptorType 4 36 | bInterfaceNumber 0 37 | bAlternateSetting 0 38 | bNumEndpoints 2 39 | bInterfaceClass 255 Vendor Specific Class 40 | bInterfaceSubClass 0 41 | bInterfaceProtocol 0 42 | iInterface 2 43 | Endpoint Descriptor: 44 | bLength 7 45 | bDescriptorType 5 46 | bEndpointAddress 0x81 EP 1 IN 47 | bmAttributes 2 48 | Transfer Type Bulk 49 | Synch Type None 50 | Usage Type Data 51 | wMaxPacketSize 0x0040 1x 64 bytes 52 | bInterval 0 53 | Endpoint Descriptor: 54 | bLength 7 55 | bDescriptorType 5 56 | bEndpointAddress 0x01 EP 1 OUT 57 | bmAttributes 2 58 | Transfer Type Bulk 59 | Synch Type None 60 | Usage Type Data 61 | wMaxPacketSize 0x0040 1x 64 bytes 62 | bInterval 0 63 | 64 | 65 | Command: 66 | esptool.py chip_id 67 | 68 | Output: 69 | esptool.py v3.2 70 | Found 3 serial ports 71 | Serial port /dev/ttyUSB0 72 | Connecting.... 73 | Detecting chip type... Unsupported detection protocol, switching and trying again... 74 | Connecting..... 75 | Detecting chip type... ESP32 76 | Chip is ESP32-D0WDQ6 (revision 1) 77 | Features: WiFi, BT, Dual Core, 240MHz, VRef calibration in efuse, Coding Scheme None 78 | Crystal is 40MHz 79 | MAC: 24:0a:c4:fc:be:f0 80 | Uploading stub... 81 | Running stub... 82 | Stub running... 83 | Warning: ESP32 has no Chip ID. Reading MAC instead. 84 | MAC: 24:0a:c4:fc:be:f0 85 | Hard resetting via RTS pin... 86 | -------------------------------------------------------------------------------- /info/ubuntu/nano_g1.txt: -------------------------------------------------------------------------------- 1 | lsusb -d 1a86: -v 2 | 3 | Bus 001 Device 013: ID 1a86:55d4 QinHeng Electronics 4 | Couldn't open device, some information will be missing 5 | Device Descriptor: 6 | bLength 18 7 | bDescriptorType 1 8 | bcdUSB 1.10 9 | bDeviceClass 2 Communications 10 | bDeviceSubClass 0 11 | bDeviceProtocol 0 12 | bMaxPacketSize0 8 13 | idVendor 0x1a86 QinHeng Electronics 14 | idProduct 0x55d4 15 | bcdDevice 4.43 16 | iManufacturer 0 17 | iProduct 2 18 | iSerial 3 19 | bNumConfigurations 1 20 | Configuration Descriptor: 21 | bLength 9 22 | bDescriptorType 2 23 | wTotalLength 0x0043 24 | bNumInterfaces 2 25 | bConfigurationValue 1 26 | iConfiguration 0 27 | bmAttributes 0xa0 28 | (Bus Powered) 29 | Remote Wakeup 30 | MaxPower 134mA 31 | Interface Descriptor: 32 | bLength 9 33 | bDescriptorType 4 34 | bInterfaceNumber 0 35 | bAlternateSetting 0 36 | bNumEndpoints 1 37 | bInterfaceClass 2 Communications 38 | bInterfaceSubClass 2 Abstract (modem) 39 | bInterfaceProtocol 1 AT-commands (v.25ter) 40 | iInterface 0 41 | CDC Header: 42 | bcdCDC 1.10 43 | CDC Call Management: 44 | bmCapabilities 0x00 45 | bDataInterface 1 46 | CDC ACM: 47 | bmCapabilities 0x02 48 | line coding and serial state 49 | CDC Union: 50 | bMasterInterface 0 51 | bSlaveInterface 1 52 | Endpoint Descriptor: 53 | bLength 7 54 | bDescriptorType 5 55 | bEndpointAddress 0x83 EP 3 IN 56 | bmAttributes 3 57 | Transfer Type Interrupt 58 | Synch Type None 59 | Usage Type Data 60 | wMaxPacketSize 0x0010 1x 16 bytes 61 | bInterval 1 62 | Interface Descriptor: 63 | bLength 9 64 | bDescriptorType 4 65 | bInterfaceNumber 1 66 | bAlternateSetting 0 67 | bNumEndpoints 2 68 | bInterfaceClass 10 CDC Data 69 | bInterfaceSubClass 0 70 | bInterfaceProtocol 0 71 | iInterface 0 72 | Endpoint Descriptor: 73 | bLength 7 74 | bDescriptorType 5 75 | bEndpointAddress 0x02 EP 2 OUT 76 | bmAttributes 2 77 | Transfer Type Bulk 78 | Synch Type None 79 | Usage Type Data 80 | wMaxPacketSize 0x0020 1x 32 bytes 81 | bInterval 0 82 | Endpoint Descriptor: 83 | bLength 7 84 | bDescriptorType 5 85 | bEndpointAddress 0x82 EP 2 IN 86 | bmAttributes 2 87 | Transfer Type Bulk 88 | Synch Type None 89 | Usage Type Data 90 | wMaxPacketSize 0x0040 1x 64 bytes 91 | bInterval 0 92 | -------------------------------------------------------------------------------- /info/ubuntu/rak4631_19003.txt: -------------------------------------------------------------------------------- 1 | 2 | Note: Meshtastic firmware was installed when running these commands 3 | 4 | $ ls -al /dev/ttyACM* 5 | crw-rw---- 1 root dialout 166, 0 Jan 29 21:50 /dev/ttyACM0 6 | 7 | lsusb -d 239a: -v 8 | 9 | Bus 001 Device 097: ID 239a:8029 10 | Couldn't open device, some information will be missing 11 | Device Descriptor: 12 | bLength 18 13 | bDescriptorType 1 14 | bcdUSB 2.00 15 | bDeviceClass 239 Miscellaneous Device 16 | bDeviceSubClass 2 17 | bDeviceProtocol 1 Interface Association 18 | bMaxPacketSize0 64 19 | idVendor 0x239a 20 | idProduct 0x8029 21 | bcdDevice 1.00 22 | iManufacturer 1 23 | iProduct 2 24 | iSerial 3 25 | bNumConfigurations 1 26 | Configuration Descriptor: 27 | bLength 9 28 | bDescriptorType 2 29 | wTotalLength 0x004b 30 | bNumInterfaces 2 31 | bConfigurationValue 1 32 | iConfiguration 0 33 | bmAttributes 0xa0 34 | (Bus Powered) 35 | Remote Wakeup 36 | MaxPower 100mA 37 | Interface Association: 38 | bLength 8 39 | bDescriptorType 11 40 | bFirstInterface 0 41 | bInterfaceCount 2 42 | bFunctionClass 2 Communications 43 | bFunctionSubClass 2 Abstract (modem) 44 | bFunctionProtocol 0 45 | iFunction 0 46 | Interface Descriptor: 47 | bLength 9 48 | bDescriptorType 4 49 | bInterfaceNumber 0 50 | bAlternateSetting 0 51 | bNumEndpoints 1 52 | bInterfaceClass 2 Communications 53 | bInterfaceSubClass 2 Abstract (modem) 54 | bInterfaceProtocol 0 55 | iInterface 4 56 | CDC Header: 57 | bcdCDC 1.20 58 | CDC Call Management: 59 | bmCapabilities 0x00 60 | bDataInterface 1 61 | CDC ACM: 62 | bmCapabilities 0x02 63 | line coding and serial state 64 | CDC Union: 65 | bMasterInterface 0 66 | bSlaveInterface 1 67 | Endpoint Descriptor: 68 | bLength 7 69 | bDescriptorType 5 70 | bEndpointAddress 0x81 EP 1 IN 71 | bmAttributes 3 72 | Transfer Type Interrupt 73 | Synch Type None 74 | Usage Type Data 75 | wMaxPacketSize 0x0008 1x 8 bytes 76 | bInterval 16 77 | Interface Descriptor: 78 | bLength 9 79 | bDescriptorType 4 80 | bInterfaceNumber 1 81 | bAlternateSetting 0 82 | bNumEndpoints 2 83 | bInterfaceClass 10 CDC Data 84 | bInterfaceSubClass 0 85 | bInterfaceProtocol 0 86 | iInterface 0 87 | Endpoint Descriptor: 88 | bLength 7 89 | bDescriptorType 5 90 | bEndpointAddress 0x01 EP 1 OUT 91 | bmAttributes 2 92 | Transfer Type Bulk 93 | Synch Type None 94 | Usage Type Data 95 | wMaxPacketSize 0x0040 1x 64 bytes 96 | bInterval 0 97 | Endpoint Descriptor: 98 | bLength 7 99 | bDescriptorType 5 100 | bEndpointAddress 0x82 EP 2 IN 101 | bmAttributes 2 102 | Transfer Type Bulk 103 | Synch Type None 104 | Usage Type Data 105 | wMaxPacketSize 0x0040 1x 64 bytes 106 | bInterval 0 107 | 108 | $ lsusb 109 | Bus 002 Device 005: ID 046d:c31c Logitech, Inc. Keyboard K120 110 | Bus 002 Device 002: ID 8087:8000 Intel Corp. 111 | Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub 112 | Bus 001 Device 097: ID 239a:8029 113 | Bus 001 Device 002: ID 8087:8008 Intel Corp. 114 | Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub 115 | Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub 116 | Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub 117 | 118 | Note: esptool.py chip_id does not detect device 119 | -------------------------------------------------------------------------------- /info/ubuntu/readme.txt: -------------------------------------------------------------------------------- 1 | info run on ubuntu 2 | -------------------------------------------------------------------------------- /info/ubuntu/tbeam.txt: -------------------------------------------------------------------------------- 1 | Run on Ubuntu 20 2 | 3 | Command: 4 | lsusb -d 1a86: -v 5 | 6 | Output: 7 | Bus 001 Device 096: ID 1a86:55d4 QinHeng Electronics 8 | Device Descriptor: 9 | bLength 18 10 | bDescriptorType 1 11 | bcdUSB 1.10 12 | bDeviceClass 2 Communications 13 | bDeviceSubClass 0 14 | bDeviceProtocol 0 15 | bMaxPacketSize0 8 16 | idVendor 0x1a86 QinHeng Electronics 17 | idProduct 0x55d4 18 | bcdDevice 4.43 19 | iManufacturer 0 20 | iProduct 2 21 | iSerial 3 22 | bNumConfigurations 1 23 | Configuration Descriptor: 24 | bLength 9 25 | bDescriptorType 2 26 | wTotalLength 0x0043 27 | bNumInterfaces 2 28 | bConfigurationValue 1 29 | iConfiguration 0 30 | bmAttributes 0xa0 31 | (Bus Powered) 32 | Remote Wakeup 33 | MaxPower 134mA 34 | Interface Descriptor: 35 | bLength 9 36 | bDescriptorType 4 37 | bInterfaceNumber 0 38 | bAlternateSetting 0 39 | bNumEndpoints 1 40 | bInterfaceClass 2 Communications 41 | bInterfaceSubClass 2 Abstract (modem) 42 | bInterfaceProtocol 1 AT-commands (v.25ter) 43 | iInterface 0 44 | CDC Header: 45 | bcdCDC 1.10 46 | CDC Call Management: 47 | bmCapabilities 0x00 48 | bDataInterface 1 49 | CDC ACM: 50 | bmCapabilities 0x02 51 | line coding and serial state 52 | CDC Union: 53 | bMasterInterface 0 54 | bSlaveInterface 1 55 | Endpoint Descriptor: 56 | bLength 7 57 | bDescriptorType 5 58 | bEndpointAddress 0x83 EP 3 IN 59 | bmAttributes 3 60 | Transfer Type Interrupt 61 | Synch Type None 62 | Usage Type Data 63 | wMaxPacketSize 0x0010 1x 16 bytes 64 | bInterval 1 65 | Interface Descriptor: 66 | bLength 9 67 | bDescriptorType 4 68 | bInterfaceNumber 1 69 | bAlternateSetting 0 70 | bNumEndpoints 2 71 | bInterfaceClass 10 CDC Data 72 | bInterfaceSubClass 0 73 | bInterfaceProtocol 0 74 | iInterface 0 75 | Endpoint Descriptor: 76 | bLength 7 77 | bDescriptorType 5 78 | bEndpointAddress 0x02 EP 2 OUT 79 | bmAttributes 2 80 | Transfer Type Bulk 81 | Synch Type None 82 | Usage Type Data 83 | wMaxPacketSize 0x0020 1x 32 bytes 84 | bInterval 0 85 | Endpoint Descriptor: 86 | bLength 7 87 | bDescriptorType 5 88 | bEndpointAddress 0x82 EP 2 IN 89 | bmAttributes 2 90 | Transfer Type Bulk 91 | Synch Type None 92 | Usage Type Data 93 | wMaxPacketSize 0x0040 1x 64 bytes 94 | bInterval 0 95 | -------------------------------------------------------------------------------- /info/ubuntu/techo.txt: -------------------------------------------------------------------------------- 1 | 2 | $ lsusb 3 | Bus 002 Device 005: ID 046d:c31c Logitech, Inc. Keyboard K120 4 | Bus 002 Device 002: ID 8087:8000 Intel Corp. 5 | Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub 6 | Bus 001 Device 107: ID 239a:0029 7 | Bus 001 Device 002: ID 8087:8008 Intel Corp. 8 | Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub 9 | Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub 10 | Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub 11 | 12 | $ lsusb -d 239a: -v 13 | 14 | Bus 001 Device 107: ID 239a:0029 15 | Couldn't open device, some information will be missing 16 | Device Descriptor: 17 | bLength 18 18 | bDescriptorType 1 19 | bcdUSB 2.00 20 | bDeviceClass 239 Miscellaneous Device 21 | bDeviceSubClass 2 22 | bDeviceProtocol 1 Interface Association 23 | bMaxPacketSize0 64 24 | idVendor 0x239a 25 | idProduct 0x0029 26 | bcdDevice 1.00 27 | iManufacturer 1 28 | iProduct 2 29 | iSerial 3 30 | bNumConfigurations 1 31 | Configuration Descriptor: 32 | bLength 9 33 | bDescriptorType 2 34 | wTotalLength 0x0062 35 | bNumInterfaces 3 36 | bConfigurationValue 1 37 | iConfiguration 0 38 | bmAttributes 0xa0 39 | (Bus Powered) 40 | Remote Wakeup 41 | MaxPower 100mA 42 | Interface Association: 43 | bLength 8 44 | bDescriptorType 11 45 | bFirstInterface 0 46 | bInterfaceCount 2 47 | bFunctionClass 2 Communications 48 | bFunctionSubClass 2 Abstract (modem) 49 | bFunctionProtocol 0 50 | iFunction 0 51 | Interface Descriptor: 52 | bLength 9 53 | bDescriptorType 4 54 | bInterfaceNumber 0 55 | bAlternateSetting 0 56 | bNumEndpoints 1 57 | bInterfaceClass 2 Communications 58 | bInterfaceSubClass 2 Abstract (modem) 59 | bInterfaceProtocol 0 60 | iInterface 4 61 | CDC Header: 62 | bcdCDC 1.20 63 | CDC Call Management: 64 | bmCapabilities 0x00 65 | bDataInterface 1 66 | CDC ACM: 67 | bmCapabilities 0x02 68 | line coding and serial state 69 | CDC Union: 70 | bMasterInterface 0 71 | bSlaveInterface 1 72 | Endpoint Descriptor: 73 | bLength 7 74 | bDescriptorType 5 75 | bEndpointAddress 0x81 EP 1 IN 76 | bmAttributes 3 77 | Transfer Type Interrupt 78 | Synch Type None 79 | Usage Type Data 80 | wMaxPacketSize 0x0008 1x 8 bytes 81 | bInterval 16 82 | Interface Descriptor: 83 | bLength 9 84 | bDescriptorType 4 85 | bInterfaceNumber 1 86 | bAlternateSetting 0 87 | bNumEndpoints 2 88 | bInterfaceClass 10 CDC Data 89 | bInterfaceSubClass 0 90 | bInterfaceProtocol 0 91 | iInterface 0 92 | Endpoint Descriptor: 93 | bLength 7 94 | bDescriptorType 5 95 | bEndpointAddress 0x02 EP 2 OUT 96 | bmAttributes 2 97 | Transfer Type Bulk 98 | Synch Type None 99 | Usage Type Data 100 | wMaxPacketSize 0x0040 1x 64 bytes 101 | bInterval 0 102 | Endpoint Descriptor: 103 | bLength 7 104 | bDescriptorType 5 105 | bEndpointAddress 0x82 EP 2 IN 106 | bmAttributes 2 107 | Transfer Type Bulk 108 | Synch Type None 109 | Usage Type Data 110 | wMaxPacketSize 0x0040 1x 64 bytes 111 | bInterval 0 112 | Interface Descriptor: 113 | bLength 9 114 | bDescriptorType 4 115 | bInterfaceNumber 2 116 | bAlternateSetting 0 117 | bNumEndpoints 2 118 | bInterfaceClass 8 Mass Storage 119 | bInterfaceSubClass 6 SCSI 120 | bInterfaceProtocol 80 Bulk-Only 121 | iInterface 5 122 | Endpoint Descriptor: 123 | bLength 7 124 | bDescriptorType 5 125 | bEndpointAddress 0x03 EP 3 OUT 126 | bmAttributes 2 127 | Transfer Type Bulk 128 | Synch Type None 129 | Usage Type Data 130 | wMaxPacketSize 0x0040 1x 64 bytes 131 | bInterval 0 132 | Endpoint Descriptor: 133 | bLength 7 134 | bDescriptorType 5 135 | bEndpointAddress 0x83 EP 3 IN 136 | bmAttributes 2 137 | Transfer Type Bulk 138 | Synch Type None 139 | Usage Type Data 140 | wMaxPacketSize 0x0040 1x 64 bytes 141 | bInterval 0 142 | -------------------------------------------------------------------------------- /info/ubuntu/tlora.txt: -------------------------------------------------------------------------------- 1 | 2 | Run on Ubuntu 20 3 | 4 | Note: Device has Meshtastic firmware installed 5 | 6 | 7 | $ lsusb 8 | Bus 002 Device 005: ID 046d:c31c Logitech, Inc. Keyboard K120 9 | Bus 002 Device 002: ID 8087:8000 Intel Corp. 10 | Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub 11 | Bus 001 Device 099: ID 1a86:55d4 QinHeng Electronics 12 | Bus 001 Device 002: ID 8087:8008 Intel Corp. 13 | Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub 14 | Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub 15 | Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub 16 | 17 | $ lsusb -d 1a86: -v 18 | 19 | Bus 001 Device 099: ID 1a86:55d4 QinHeng Electronics 20 | Couldn't open device, some information will be missing 21 | Device Descriptor: 22 | bLength 18 23 | bDescriptorType 1 24 | bcdUSB 1.10 25 | bDeviceClass 2 Communications 26 | bDeviceSubClass 0 27 | bDeviceProtocol 0 28 | bMaxPacketSize0 8 29 | idVendor 0x1a86 QinHeng Electronics 30 | idProduct 0x55d4 31 | bcdDevice 4.43 32 | iManufacturer 0 33 | iProduct 2 34 | iSerial 0 35 | bNumConfigurations 1 36 | Configuration Descriptor: 37 | bLength 9 38 | bDescriptorType 2 39 | wTotalLength 0x0043 40 | bNumInterfaces 2 41 | bConfigurationValue 1 42 | iConfiguration 0 43 | bmAttributes 0xa0 44 | (Bus Powered) 45 | Remote Wakeup 46 | MaxPower 134mA 47 | Interface Descriptor: 48 | bLength 9 49 | bDescriptorType 4 50 | bInterfaceNumber 0 51 | bAlternateSetting 0 52 | bNumEndpoints 1 53 | bInterfaceClass 2 Communications 54 | bInterfaceSubClass 2 Abstract (modem) 55 | bInterfaceProtocol 1 AT-commands (v.25ter) 56 | iInterface 0 57 | CDC Header: 58 | bcdCDC 1.10 59 | CDC Call Management: 60 | bmCapabilities 0x00 61 | bDataInterface 1 62 | CDC ACM: 63 | bmCapabilities 0x02 64 | line coding and serial state 65 | CDC Union: 66 | bMasterInterface 0 67 | bSlaveInterface 1 68 | Endpoint Descriptor: 69 | bLength 7 70 | bDescriptorType 5 71 | bEndpointAddress 0x83 EP 3 IN 72 | bmAttributes 3 73 | Transfer Type Interrupt 74 | Synch Type None 75 | Usage Type Data 76 | wMaxPacketSize 0x0010 1x 16 bytes 77 | bInterval 1 78 | Interface Descriptor: 79 | bLength 9 80 | bDescriptorType 4 81 | bInterfaceNumber 1 82 | bAlternateSetting 0 83 | bNumEndpoints 2 84 | bInterfaceClass 10 CDC Data 85 | bInterfaceSubClass 0 86 | bInterfaceProtocol 0 87 | iInterface 0 88 | Endpoint Descriptor: 89 | bLength 7 90 | bDescriptorType 5 91 | bEndpointAddress 0x02 EP 2 OUT 92 | bmAttributes 2 93 | Transfer Type Bulk 94 | Synch Type None 95 | Usage Type Data 96 | wMaxPacketSize 0x0020 1x 32 bytes 97 | bInterval 0 98 | Endpoint Descriptor: 99 | bLength 7 100 | bDescriptorType 5 101 | bEndpointAddress 0x82 EP 2 IN 102 | bmAttributes 2 103 | Transfer Type Bulk 104 | Synch Type None 105 | Usage Type Data 106 | wMaxPacketSize 0x0040 1x 64 bytes 107 | bInterval 0 108 | -------------------------------------------------------------------------------- /info/windows/nano_g1.txt: -------------------------------------------------------------------------------- 1 | Get-PnpDevice -PresentOnly | Format-List >a 2 | Get-PnpDevice -PresentOnly | Format-List >b 3 | Compare-Object (get-content a) (Get-Content b) 4 | 5 | InputObject SideIndicator 6 | ----------- ------------- 7 | Caption : USB-Enhanced-SERIAL CH9102 (COM9) => 8 | Description : USB-Enhanced-SERIAL CH9102 => 9 | Name : USB-Enhanced-SERIAL CH9102 (COM9) => 10 | DeviceID : USB\VID_1A86&PID_55D4\5382020745 => 11 | PNPDeviceID : USB\VID_1A86&PID_55D4\5382020745 => 12 | ClassGuid : {4d36e978-e325-11ce-bfc1-08002be10318} => 13 | CompatibleID : {USB\Class_02&SubClass_02&Prot_01, USB\Class_02&SubClass_02, USB\Class_02} => 14 | HardwareID : {USB\VID_1A86&PID_55D4&REV_0443, USB\VID_1A86&PID_55D4} => 15 | Manufacturer : wch.cn => 16 | PNPClass : Ports => 17 | Service : CH343SER_A64 => 18 | Class : Ports => 19 | FriendlyName : USB-Enhanced-SERIAL CH9102 (COM9) => 20 | InstanceId : USB\VID_1A86&PID_55D4\5382020745 => 21 | InstallDate : => 22 | Status : OK => 23 | Availability : => 24 | ConfigManagerErrorCode : CM_PROB_NONE => 25 | ConfigManagerUserConfig : False => 26 | CreationClassName : Win32_PnPEntity => 27 | ErrorCleared : => 28 | ErrorDescription : => 29 | LastErrorCode : => 30 | PowerManagementCapabilities : => 31 | PowerManagementSupported : => 32 | StatusInfo : => 33 | SystemCreationClassName : Win32_ComputerSystem => 34 | SystemName : DESKTOP-FRFQN8H => 35 | Present : True => 36 | PSComputerName : => 37 | Problem : CM_PROB_NONE => 38 | ProblemDescription : => 39 | => 40 | -------------------------------------------------------------------------------- /info/windows/tbeam.txt: -------------------------------------------------------------------------------- 1 | Run from Windows 10 2 | 3 | > Get-PnpDevice -PresentOnly | Format-List >a 4 | > Get-PnpDevice -PresentOnly | Format-List >b 5 | > Compare-Object (get-content a) (Get-Content b) 6 | 7 | InputObject SideIndicator 8 | ----------- ------------- 9 | Caption : USB-Enhanced-SERIAL CH9102 (COM7) => 10 | Description : USB-Enhanced-SERIAL CH9102 => 11 | Name : USB-Enhanced-SERIAL CH9102 (COM7) => 12 | DeviceID : USB\VID_1A86&PID_55D4\5323005057 => 13 | PNPDeviceID : USB\VID_1A86&PID_55D4\5323005057 => 14 | ClassGuid : {4d36e978-e325-11ce-bfc1-08002be10318} => 15 | CompatibleID : {USB\Class_02&SubClass_02&Prot_01, USB\Class_02&SubClass_02, USB\Class_02} => 16 | HardwareID : {USB\VID_1A86&PID_55D4&REV_0443, USB\VID_1A86&PID_55D4} => 17 | Manufacturer : wch.cn => 18 | PNPClass : Ports => 19 | Service : CH343SER_A64 => 20 | Class : Ports => 21 | FriendlyName : USB-Enhanced-SERIAL CH9102 (COM7) => 22 | InstanceId : USB\VID_1A86&PID_55D4\5323005057 => 23 | InstallDate : => 24 | Status : OK => 25 | Availability : => 26 | ConfigManagerErrorCode : CM_PROB_NONE => 27 | ConfigManagerUserConfig : False => 28 | CreationClassName : Win32_PnPEntity => 29 | ErrorCleared : => 30 | ErrorDescription : => 31 | LastErrorCode : => 32 | PowerManagementCapabilities : => 33 | PowerManagementSupported : => 34 | StatusInfo : => 35 | SystemCreationClassName : Win32_ComputerSystem => 36 | SystemName : DESKTOP-FRFQN8H => 37 | Present : True => 38 | PSComputerName : => 39 | Problem : CM_PROB_NONE => 40 | ProblemDescription : => 41 | => 42 | 43 | -------------------------------------------------------------------------------- /info/windows/tlora.txt: -------------------------------------------------------------------------------- 1 | Run from Windows 10 2 | 3 | PS > Get-PnpDevice -PresentOnly | Format-List > a 4 | PS > Get-PnpDevice -PresentOnly | Format-List > b 5 | PS > Compare-Object (get-content a) (Get-Content b) 6 | 7 | InputObject SideIndicator 8 | ----------- ------------- 9 | Caption : USB-Enhanced-SERIAL CH9102 (COM3) <= 10 | Description : USB-Enhanced-SERIAL CH9102 <= 11 | Name : USB-Enhanced-SERIAL CH9102 (COM3) <= 12 | DeviceID : USB\VID_1A86&PID_55D4\5&27435A1F&0&1 <= 13 | PNPDeviceID : USB\VID_1A86&PID_55D4\5&27435A1F&0&1 <= 14 | ClassGuid : {4d36e978-e325-11ce-bfc1-08002be10318} <= 15 | CompatibleID : {USB\Class_02&SubClass_02&Prot_01, USB\Class_02&SubClass_02, USB\Class_02} <= 16 | HardwareID : {USB\VID_1A86&PID_55D4&REV_0443, USB\VID_1A86&PID_55D4} <= 17 | Manufacturer : wch.cn <= 18 | PNPClass : Ports <= 19 | Service : CH343SER_A64 <= 20 | Class : Ports <= 21 | FriendlyName : USB-Enhanced-SERIAL CH9102 (COM3) <= 22 | InstanceId : USB\VID_1A86&PID_55D4\5&27435A1F&0&1 <= 23 | InstallDate : <= 24 | Status : OK <= 25 | Availability : <= 26 | ConfigManagerErrorCode : CM_PROB_NONE <= 27 | ConfigManagerUserConfig : False <= 28 | CreationClassName : Win32_PnPEntity <= 29 | ErrorCleared : <= 30 | ErrorDescription : <= 31 | LastErrorCode : <= 32 | PowerManagementCapabilities : <= 33 | PowerManagementSupported : <= 34 | StatusInfo : <= 35 | SystemCreationClassName : Win32_ComputerSystem <= 36 | SystemName : DESKTOP-FRFQN8H <= 37 | Present : True <= 38 | PSComputerName : <= 39 | Problem : CM_PROB_NONE <= 40 | ProblemDescription : <= 41 | <= 42 | -------------------------------------------------------------------------------- /meshtastic/.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | -------------------------------------------------------------------------------- /meshtastic/analysis/__init__.py: -------------------------------------------------------------------------------- 1 | """Post-run analysis tools for meshtastic.""" 2 | -------------------------------------------------------------------------------- /meshtastic/mt_config.py: -------------------------------------------------------------------------------- 1 | """ 2 | Globals singleton class. 3 | 4 | The Global object is gone, as are all its setters and getters. Instead the 5 | module itself is the singleton namespace, which can be imported into 6 | whichever module is used. The associated tests have also been removed, 7 | since we now rely on built in Python mechanisms. 8 | 9 | This is intended to make the Python read more naturally, and to make the 10 | intention of the code clearer and more compact. It is merely a sticking 11 | plaster over the use of shared mt_config, but the coupling issues wil be dealt 12 | with rather more easily once the code is simplified by this change. 13 | 14 | """ 15 | 16 | from typing import Any, Optional 17 | 18 | def reset(): 19 | """ 20 | Restore the namespace to pristine condition. 21 | """ 22 | # pylint: disable=W0603 23 | global args, parser, channel_index, logfile, tunnelInstance, camel_case 24 | args = None 25 | parser = None 26 | channel_index = None 27 | logfile = None 28 | tunnelInstance = None 29 | # TODO: to migrate to camel_case for v1.3 change this value to True 30 | camel_case = False 31 | 32 | # These assignments are used instead of calling reset() 33 | # purely to shut pylint up. 34 | args = None 35 | parser = None 36 | channel_index = None 37 | logfile = None 38 | tunnelInstance: Optional[Any] = None 39 | camel_case = False 40 | -------------------------------------------------------------------------------- /meshtastic/powermon/__init__.py: -------------------------------------------------------------------------------- 1 | """Support for logging from power meters/supplies.""" 2 | 3 | from .power_supply import PowerError, PowerMeter, PowerSupply 4 | from .ppk2 import PPK2PowerSupply 5 | from .riden import RidenPowerSupply 6 | from .sim import SimPowerSupply 7 | from .stress import PowerStress 8 | -------------------------------------------------------------------------------- /meshtastic/powermon/power_supply.py: -------------------------------------------------------------------------------- 1 | """code logging power consumption of meshtastic devices.""" 2 | 3 | import math 4 | from datetime import datetime 5 | 6 | 7 | class PowerError(Exception): 8 | """An exception class for powermon errors""" 9 | 10 | def __init__(self, message): 11 | self.message = message 12 | super().__init__(self.message) 13 | 14 | 15 | class PowerMeter: 16 | """Abstract class for power meters.""" 17 | 18 | def __init__(self): 19 | """Initialize the PowerMeter object.""" 20 | self.prevPowerTime = datetime.now() 21 | 22 | def close(self) -> None: 23 | """Close the power meter.""" 24 | 25 | def get_average_current_mA(self) -> float: 26 | """Returns average current of last measurement in mA (since last call to this method)""" 27 | return math.nan 28 | 29 | def get_min_current_mA(self): 30 | """Returns max current in mA (since last call to this method).""" 31 | # Subclasses must override for a better implementation 32 | return self.get_average_current_mA() 33 | 34 | def get_max_current_mA(self): 35 | """Returns max current in mA (since last call to this method).""" 36 | # Subclasses must override for a better implementation 37 | return self.get_average_current_mA() 38 | 39 | def reset_measurements(self): 40 | """Reset current measurements.""" 41 | 42 | 43 | class PowerSupply(PowerMeter): 44 | """Abstract class for power supplies.""" 45 | 46 | def __init__(self): 47 | """Initialize the PowerSupply object.""" 48 | super().__init__() 49 | self.v = 0.0 50 | 51 | def powerOn(self): 52 | """Turn on the power supply (using the voltage set in self.v).""" 53 | -------------------------------------------------------------------------------- /meshtastic/powermon/riden.py: -------------------------------------------------------------------------------- 1 | """code logging power consumption of meshtastic devices.""" 2 | 3 | import logging 4 | from datetime import datetime 5 | 6 | from riden import Riden 7 | 8 | from .power_supply import PowerSupply 9 | 10 | 11 | class RidenPowerSupply(PowerSupply): 12 | """Interface for talking to Riden programmable bench-top power supplies. 13 | Only RD6006 tested but others should be similar. 14 | """ 15 | 16 | def __init__(self, portName: str = "/dev/ttyUSB0"): 17 | """Initialize the RidenPowerSupply object. 18 | 19 | portName (str, optional): The port name of the power supply. Defaults to "/dev/ttyUSB0". 20 | """ 21 | self.r = r = Riden(port=portName, baudrate=115200, address=1) 22 | logging.info( 23 | f"Connected to Riden power supply: model {r.type}, sn {r.sn}, firmware {r.fw}. Date/time updated." 24 | ) 25 | r.set_date_time(datetime.now()) 26 | self.prevWattHour = self._getRawWattHour() 27 | self.nowWattHour = self.prevWattHour 28 | super().__init__() # we call this late so that the port is already open and _getRawWattHour callback works 29 | 30 | def setMaxCurrent(self, i: float): 31 | """Set the maximum current the supply will provide.""" 32 | self.r.set_i_set(i) 33 | 34 | def powerOn(self): 35 | """Power on the supply, with reasonable defaults for meshtastic devices.""" 36 | self.r.set_v_set( 37 | self.v 38 | ) # my WM1110 devboard header is directly connected to the 3.3V rail 39 | self.r.set_output(1) 40 | 41 | def get_average_current_mA(self) -> float: 42 | """Returns average current of last measurement in mA (since last call to this method)""" 43 | now = datetime.now() 44 | nowWattHour = self._getRawWattHour() 45 | watts = ( 46 | (nowWattHour - self.prevWattHour) 47 | / (now - self.prevPowerTime).total_seconds() 48 | * 3600 49 | ) 50 | self.prevPowerTime = now 51 | self.prevWattHour = nowWattHour 52 | return watts / 1000 53 | 54 | def _getRawWattHour(self) -> float: 55 | """Get the current watt-hour reading.""" 56 | self.r.update() 57 | return self.r.wh 58 | -------------------------------------------------------------------------------- /meshtastic/powermon/sim.py: -------------------------------------------------------------------------------- 1 | """code logging power consumption of meshtastic devices.""" 2 | 3 | import math 4 | import time 5 | 6 | from .power_supply import PowerSupply 7 | 8 | 9 | class SimPowerSupply(PowerSupply): 10 | """A simulated power supply for testing.""" 11 | 12 | def get_average_current_mA(self) -> float: 13 | """Returns average current of last measurement in mA (since last call to this method)""" 14 | 15 | # Sim a 20mW load that varies sinusoidally 16 | return (20.0 + 5 * math.sin(time.time())) 17 | -------------------------------------------------------------------------------- /meshtastic/powermon/stress.py: -------------------------------------------------------------------------------- 1 | """Power stress testing support. 2 | """ 3 | import logging 4 | import time 5 | 6 | from ..protobuf import portnums_pb2, powermon_pb2 7 | 8 | 9 | def onPowerStressResponse(packet, interface): 10 | """Delete me? FIXME""" 11 | logging.debug(f"packet:{packet} interface:{interface}") 12 | # interface.gotResponse = True 13 | 14 | 15 | class PowerStressClient: 16 | """ 17 | The client stub for talking to the firmware PowerStress module. 18 | """ 19 | 20 | def __init__(self, iface, node_id=None): 21 | """ 22 | Create a new PowerStressClient instance. 23 | 24 | iface is the already open MeshInterface instance 25 | """ 26 | self.iface = iface 27 | 28 | if not node_id: 29 | node_id = iface.myInfo.my_node_num 30 | 31 | self.node_id = node_id 32 | # No need to subscribe - because we 33 | # pub.subscribe(onGPIOreceive, "meshtastic.receive.powerstress") 34 | 35 | def sendPowerStress( 36 | self, 37 | cmd: powermon_pb2.PowerStressMessage.Opcode.ValueType, 38 | num_seconds: float = 0.0, 39 | onResponse=None, 40 | ): 41 | """Client goo for talking with the device side agent.""" 42 | r = powermon_pb2.PowerStressMessage() 43 | r.cmd = cmd 44 | r.num_seconds = num_seconds 45 | 46 | return self.iface.sendData( 47 | r, 48 | self.node_id, 49 | portnums_pb2.POWERSTRESS_APP, 50 | wantAck=True, 51 | wantResponse=True, 52 | onResponse=onResponse, 53 | onResponseAckPermitted=True, 54 | ) 55 | 56 | def syncPowerStress( 57 | self, 58 | cmd: powermon_pb2.PowerStressMessage.Opcode.ValueType, 59 | num_seconds: float = 0.0, 60 | ): 61 | """Send a power stress command and wait for the ack.""" 62 | gotAck = False 63 | 64 | def onResponse(packet: dict): # pylint: disable=unused-argument 65 | nonlocal gotAck 66 | gotAck = True 67 | 68 | logging.info( 69 | f"Sending power stress command {powermon_pb2.PowerStressMessage.Opcode.Name(cmd)}" 70 | ) 71 | self.sendPowerStress(cmd, onResponse=onResponse, num_seconds=num_seconds) 72 | 73 | if num_seconds == 0.0: 74 | # Wait for the response and then continue 75 | while not gotAck: 76 | time.sleep(0.1) 77 | else: 78 | # we wait a little bit longer than the time the UUT would be waiting (to make sure all of its messages are handled first) 79 | time.sleep( 80 | num_seconds + 0.2 81 | ) # completely block our thread for the duration of the test 82 | if not gotAck: 83 | logging.error("Did not receive ack for power stress command!") 84 | 85 | 86 | class PowerStress: 87 | """Walk the UUT through a set of power states so we can capture repeatable power consumption measurements.""" 88 | 89 | def __init__(self, iface): 90 | self.client = PowerStressClient(iface) 91 | 92 | def run(self): 93 | """Run the power stress test.""" 94 | try: 95 | self.client.syncPowerStress(powermon_pb2.PowerStressMessage.PRINT_INFO) 96 | 97 | num_seconds = 5.0 98 | states = [ 99 | powermon_pb2.PowerStressMessage.LED_ON, 100 | powermon_pb2.PowerStressMessage.LED_OFF, 101 | powermon_pb2.PowerStressMessage.BT_OFF, 102 | powermon_pb2.PowerStressMessage.BT_ON, 103 | powermon_pb2.PowerStressMessage.CPU_FULLON, 104 | powermon_pb2.PowerStressMessage.CPU_IDLE, 105 | # FIXME - can't test deepsleep yet because the ttyACM device disappears. Fix the python code to retry connections 106 | # powermon_pb2.PowerStressMessage.CPU_DEEPSLEEP, 107 | ] 108 | for s in states: 109 | s_name = powermon_pb2.PowerStressMessage.Opcode.Name(s) 110 | logging.info( 111 | f"Running power stress test {s_name} for {num_seconds} seconds" 112 | ) 113 | self.client.syncPowerStress(s, num_seconds) 114 | 115 | logging.info("Power stress test complete.") 116 | except KeyboardInterrupt as e: 117 | logging.warning(f"Power stress interrupted: {e}") 118 | -------------------------------------------------------------------------------- /meshtastic/protobuf/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meshtastic/python/622a435465872fd316054f1a4a7dd6d4d44bb3ec/meshtastic/protobuf/__init__.py -------------------------------------------------------------------------------- /meshtastic/protobuf/apponly_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # source: meshtastic/protobuf/apponly.proto 4 | """Generated protocol buffer code.""" 5 | from google.protobuf import descriptor as _descriptor 6 | from google.protobuf import descriptor_pool as _descriptor_pool 7 | from google.protobuf import symbol_database as _symbol_database 8 | from google.protobuf.internal import builder as _builder 9 | # @@protoc_insertion_point(imports) 10 | 11 | _sym_db = _symbol_database.Default() 12 | 13 | 14 | from meshtastic.protobuf import channel_pb2 as meshtastic_dot_protobuf_dot_channel__pb2 15 | from meshtastic.protobuf import config_pb2 as meshtastic_dot_protobuf_dot_config__pb2 16 | 17 | 18 | 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') 19 | 20 | _globals = globals() 21 | _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) 22 | _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.apponly_pb2', _globals) 23 | if _descriptor._USE_C_DESCRIPTORS == False: 24 | DESCRIPTOR._options = None 25 | DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\rAppOnlyProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' 26 | _globals['_CHANNELSET']._serialized_start=128 27 | _globals['_CHANNELSET']._serialized_end=257 28 | # @@protoc_insertion_point(module_scope) 29 | -------------------------------------------------------------------------------- /meshtastic/protobuf/apponly_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import collections.abc 8 | import google.protobuf.descriptor 9 | import google.protobuf.internal.containers 10 | import google.protobuf.message 11 | import meshtastic.protobuf.channel_pb2 12 | import meshtastic.protobuf.config_pb2 13 | import typing 14 | 15 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 16 | 17 | @typing.final 18 | class ChannelSet(google.protobuf.message.Message): 19 | """ 20 | This is the most compact possible representation for a set of channels. 21 | It includes only one PRIMARY channel (which must be first) and 22 | any SECONDARY channels. 23 | No DISABLED channels are included. 24 | 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 25 | """ 26 | 27 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 28 | 29 | SETTINGS_FIELD_NUMBER: builtins.int 30 | LORA_CONFIG_FIELD_NUMBER: builtins.int 31 | @property 32 | def settings(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[meshtastic.protobuf.channel_pb2.ChannelSettings]: 33 | """ 34 | Channel list with settings 35 | """ 36 | 37 | @property 38 | def lora_config(self) -> meshtastic.protobuf.config_pb2.Config.LoRaConfig: 39 | """ 40 | LoRa config 41 | """ 42 | 43 | def __init__( 44 | self, 45 | *, 46 | settings: collections.abc.Iterable[meshtastic.protobuf.channel_pb2.ChannelSettings] | None = ..., 47 | lora_config: meshtastic.protobuf.config_pb2.Config.LoRaConfig | None = ..., 48 | ) -> None: ... 49 | def HasField(self, field_name: typing.Literal["lora_config", b"lora_config"]) -> builtins.bool: ... 50 | def ClearField(self, field_name: typing.Literal["lora_config", b"lora_config", "settings", b"settings"]) -> None: ... 51 | 52 | global___ChannelSet = ChannelSet 53 | -------------------------------------------------------------------------------- /meshtastic/protobuf/atak_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # source: meshtastic/protobuf/atak.proto 4 | """Generated protocol buffer code.""" 5 | from google.protobuf import descriptor as _descriptor 6 | from google.protobuf import descriptor_pool as _descriptor_pool 7 | from google.protobuf import symbol_database as _symbol_database 8 | from google.protobuf.internal import builder as _builder 9 | # @@protoc_insertion_point(imports) 10 | 11 | _sym_db = _symbol_database.Default() 12 | 13 | 14 | 15 | 16 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1emeshtastic/protobuf/atak.proto\x12\x13meshtastic.protobuf\"\xa5\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\x12\x10\n\x06\x64\x65tail\x18\x07 \x01(\x0cH\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') 17 | 18 | _globals = globals() 19 | _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) 20 | _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.atak_pb2', _globals) 21 | if _descriptor._USE_C_DESCRIPTORS == False: 22 | DESCRIPTOR._options = None 23 | DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\nATAKProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' 24 | _globals['_TEAM']._serialized_start=721 25 | _globals['_TEAM']._serialized_end=913 26 | _globals['_MEMBERROLE']._serialized_start=915 27 | _globals['_MEMBERROLE']._serialized_end=1042 28 | _globals['_TAKPACKET']._serialized_start=56 29 | _globals['_TAKPACKET']._serialized_end=349 30 | _globals['_GEOCHAT']._serialized_start=351 31 | _globals['_GEOCHAT']._serialized_end=443 32 | _globals['_GROUP']._serialized_start=445 33 | _globals['_GROUP']._serialized_end=540 34 | _globals['_STATUS']._serialized_start=542 35 | _globals['_STATUS']._serialized_end=567 36 | _globals['_CONTACT']._serialized_start=569 37 | _globals['_CONTACT']._serialized_end=621 38 | _globals['_PLI']._serialized_start=623 39 | _globals['_PLI']._serialized_end=718 40 | # @@protoc_insertion_point(module_scope) 41 | -------------------------------------------------------------------------------- /meshtastic/protobuf/cannedmessages_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # source: meshtastic/protobuf/cannedmessages.proto 4 | """Generated protocol buffer code.""" 5 | from google.protobuf import descriptor as _descriptor 6 | from google.protobuf import descriptor_pool as _descriptor_pool 7 | from google.protobuf import symbol_database as _symbol_database 8 | from google.protobuf.internal import builder as _builder 9 | # @@protoc_insertion_point(imports) 10 | 11 | _sym_db = _symbol_database.Default() 12 | 13 | 14 | 15 | 16 | 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') 17 | 18 | _globals = globals() 19 | _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) 20 | _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.cannedmessages_pb2', _globals) 21 | if _descriptor._USE_C_DESCRIPTORS == False: 22 | DESCRIPTOR._options = None 23 | DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\031CannedMessageConfigProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' 24 | _globals['_CANNEDMESSAGEMODULECONFIG']._serialized_start=65 25 | _globals['_CANNEDMESSAGEMODULECONFIG']._serialized_end=110 26 | # @@protoc_insertion_point(module_scope) 27 | -------------------------------------------------------------------------------- /meshtastic/protobuf/cannedmessages_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import google.protobuf.descriptor 8 | import google.protobuf.message 9 | import typing 10 | 11 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 12 | 13 | @typing.final 14 | class CannedMessageModuleConfig(google.protobuf.message.Message): 15 | """ 16 | Canned message module configuration. 17 | """ 18 | 19 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 20 | 21 | MESSAGES_FIELD_NUMBER: builtins.int 22 | messages: builtins.str 23 | """ 24 | Predefined messages for canned message module separated by '|' characters. 25 | """ 26 | def __init__( 27 | self, 28 | *, 29 | messages: builtins.str = ..., 30 | ) -> None: ... 31 | def ClearField(self, field_name: typing.Literal["messages", b"messages"]) -> None: ... 32 | 33 | global___CannedMessageModuleConfig = CannedMessageModuleConfig 34 | -------------------------------------------------------------------------------- /meshtastic/protobuf/channel_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # source: meshtastic/protobuf/channel.proto 4 | """Generated protocol buffer code.""" 5 | from google.protobuf import descriptor as _descriptor 6 | from google.protobuf import descriptor_pool as _descriptor_pool 7 | from google.protobuf import symbol_database as _symbol_database 8 | from google.protobuf.internal import builder as _builder 9 | # @@protoc_insertion_point(imports) 10 | 11 | _sym_db = _symbol_database.Default() 12 | 13 | 14 | 15 | 16 | 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') 17 | 18 | _globals = globals() 19 | _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) 20 | _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.channel_pb2', _globals) 21 | if _descriptor._USE_C_DESCRIPTORS == False: 22 | DESCRIPTOR._options = None 23 | DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\rChannelProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' 24 | _CHANNELSETTINGS.fields_by_name['channel_num']._options = None 25 | _CHANNELSETTINGS.fields_by_name['channel_num']._serialized_options = b'\030\001' 26 | _globals['_CHANNELSETTINGS']._serialized_start=59 27 | _globals['_CHANNELSETTINGS']._serialized_end=252 28 | _globals['_MODULESETTINGS']._serialized_start=254 29 | _globals['_MODULESETTINGS']._serialized_end=323 30 | _globals['_CHANNEL']._serialized_start=326 31 | _globals['_CHANNEL']._serialized_end=505 32 | _globals['_CHANNEL_ROLE']._serialized_start=457 33 | _globals['_CHANNEL_ROLE']._serialized_end=505 34 | # @@protoc_insertion_point(module_scope) 35 | -------------------------------------------------------------------------------- /meshtastic/protobuf/clientonly_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # source: meshtastic/protobuf/clientonly.proto 4 | """Generated protocol buffer code.""" 5 | from google.protobuf import descriptor as _descriptor 6 | from google.protobuf import descriptor_pool as _descriptor_pool 7 | from google.protobuf import symbol_database as _symbol_database 8 | from google.protobuf.internal import builder as _builder 9 | # @@protoc_insertion_point(imports) 10 | 11 | _sym_db = _symbol_database.Default() 12 | 13 | 14 | from meshtastic.protobuf import localonly_pb2 as meshtastic_dot_protobuf_dot_localonly__pb2 15 | from meshtastic.protobuf import mesh_pb2 as meshtastic_dot_protobuf_dot_mesh__pb2 16 | 17 | 18 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n$meshtastic/protobuf/clientonly.proto\x12\x13meshtastic.protobuf\x1a#meshtastic/protobuf/localonly.proto\x1a\x1emeshtastic/protobuf/mesh.proto\"\xc4\x03\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\x12:\n\x0e\x66ixed_position\x18\x06 \x01(\x0b\x32\x1d.meshtastic.protobuf.PositionH\x05\x88\x01\x01\x12\x15\n\x08ringtone\x18\x07 \x01(\tH\x06\x88\x01\x01\x12\x1c\n\x0f\x63\x61nned_messages\x18\x08 \x01(\tH\x07\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_configB\x11\n\x0f_fixed_positionB\x0b\n\t_ringtoneB\x12\n\x10_canned_messagesBe\n\x13\x63om.geeksville.meshB\x10\x43lientOnlyProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') 19 | 20 | _globals = globals() 21 | _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) 22 | _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.clientonly_pb2', _globals) 23 | if _descriptor._USE_C_DESCRIPTORS == False: 24 | DESCRIPTOR._options = None 25 | DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\020ClientOnlyProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' 26 | _globals['_DEVICEPROFILE']._serialized_start=131 27 | _globals['_DEVICEPROFILE']._serialized_end=583 28 | # @@protoc_insertion_point(module_scope) 29 | -------------------------------------------------------------------------------- /meshtastic/protobuf/clientonly_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import google.protobuf.descriptor 8 | import google.protobuf.message 9 | import meshtastic.protobuf.localonly_pb2 10 | import meshtastic.protobuf.mesh_pb2 11 | import typing 12 | 13 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 14 | 15 | @typing.final 16 | class DeviceProfile(google.protobuf.message.Message): 17 | """ 18 | This abstraction is used to contain any configuration for provisioning a node on any client. 19 | It is useful for importing and exporting configurations. 20 | """ 21 | 22 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 23 | 24 | LONG_NAME_FIELD_NUMBER: builtins.int 25 | SHORT_NAME_FIELD_NUMBER: builtins.int 26 | CHANNEL_URL_FIELD_NUMBER: builtins.int 27 | CONFIG_FIELD_NUMBER: builtins.int 28 | MODULE_CONFIG_FIELD_NUMBER: builtins.int 29 | FIXED_POSITION_FIELD_NUMBER: builtins.int 30 | RINGTONE_FIELD_NUMBER: builtins.int 31 | CANNED_MESSAGES_FIELD_NUMBER: builtins.int 32 | long_name: builtins.str 33 | """ 34 | Long name for the node 35 | """ 36 | short_name: builtins.str 37 | """ 38 | Short name of the node 39 | """ 40 | channel_url: builtins.str 41 | """ 42 | The url of the channels from our node 43 | """ 44 | ringtone: builtins.str 45 | """ 46 | Ringtone for ExternalNotification 47 | """ 48 | canned_messages: builtins.str 49 | """ 50 | Predefined messages for CannedMessage 51 | """ 52 | @property 53 | def config(self) -> meshtastic.protobuf.localonly_pb2.LocalConfig: 54 | """ 55 | The Config of the node 56 | """ 57 | 58 | @property 59 | def module_config(self) -> meshtastic.protobuf.localonly_pb2.LocalModuleConfig: 60 | """ 61 | The ModuleConfig of the node 62 | """ 63 | 64 | @property 65 | def fixed_position(self) -> meshtastic.protobuf.mesh_pb2.Position: 66 | """ 67 | Fixed position data 68 | """ 69 | 70 | def __init__( 71 | self, 72 | *, 73 | long_name: builtins.str | None = ..., 74 | short_name: builtins.str | None = ..., 75 | channel_url: builtins.str | None = ..., 76 | config: meshtastic.protobuf.localonly_pb2.LocalConfig | None = ..., 77 | module_config: meshtastic.protobuf.localonly_pb2.LocalModuleConfig | None = ..., 78 | fixed_position: meshtastic.protobuf.mesh_pb2.Position | None = ..., 79 | ringtone: builtins.str | None = ..., 80 | canned_messages: builtins.str | None = ..., 81 | ) -> None: ... 82 | def HasField(self, field_name: typing.Literal["_canned_messages", b"_canned_messages", "_channel_url", b"_channel_url", "_config", b"_config", "_fixed_position", b"_fixed_position", "_long_name", b"_long_name", "_module_config", b"_module_config", "_ringtone", b"_ringtone", "_short_name", b"_short_name", "canned_messages", b"canned_messages", "channel_url", b"channel_url", "config", b"config", "fixed_position", b"fixed_position", "long_name", b"long_name", "module_config", b"module_config", "ringtone", b"ringtone", "short_name", b"short_name"]) -> builtins.bool: ... 83 | def ClearField(self, field_name: typing.Literal["_canned_messages", b"_canned_messages", "_channel_url", b"_channel_url", "_config", b"_config", "_fixed_position", b"_fixed_position", "_long_name", b"_long_name", "_module_config", b"_module_config", "_ringtone", b"_ringtone", "_short_name", b"_short_name", "canned_messages", b"canned_messages", "channel_url", b"channel_url", "config", b"config", "fixed_position", b"fixed_position", "long_name", b"long_name", "module_config", b"module_config", "ringtone", b"ringtone", "short_name", b"short_name"]) -> None: ... 84 | @typing.overload 85 | def WhichOneof(self, oneof_group: typing.Literal["_canned_messages", b"_canned_messages"]) -> typing.Literal["canned_messages"] | None: ... 86 | @typing.overload 87 | def WhichOneof(self, oneof_group: typing.Literal["_channel_url", b"_channel_url"]) -> typing.Literal["channel_url"] | None: ... 88 | @typing.overload 89 | def WhichOneof(self, oneof_group: typing.Literal["_config", b"_config"]) -> typing.Literal["config"] | None: ... 90 | @typing.overload 91 | def WhichOneof(self, oneof_group: typing.Literal["_fixed_position", b"_fixed_position"]) -> typing.Literal["fixed_position"] | None: ... 92 | @typing.overload 93 | def WhichOneof(self, oneof_group: typing.Literal["_long_name", b"_long_name"]) -> typing.Literal["long_name"] | None: ... 94 | @typing.overload 95 | def WhichOneof(self, oneof_group: typing.Literal["_module_config", b"_module_config"]) -> typing.Literal["module_config"] | None: ... 96 | @typing.overload 97 | def WhichOneof(self, oneof_group: typing.Literal["_ringtone", b"_ringtone"]) -> typing.Literal["ringtone"] | None: ... 98 | @typing.overload 99 | def WhichOneof(self, oneof_group: typing.Literal["_short_name", b"_short_name"]) -> typing.Literal["short_name"] | None: ... 100 | 101 | global___DeviceProfile = DeviceProfile 102 | -------------------------------------------------------------------------------- /meshtastic/protobuf/connection_status_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # source: meshtastic/protobuf/connection_status.proto 4 | """Generated protocol buffer code.""" 5 | from google.protobuf import descriptor as _descriptor 6 | from google.protobuf import descriptor_pool as _descriptor_pool 7 | from google.protobuf import symbol_database as _symbol_database 8 | from google.protobuf.internal import builder as _builder 9 | # @@protoc_insertion_point(imports) 10 | 11 | _sym_db = _symbol_database.Default() 12 | 13 | 14 | 15 | 16 | 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') 17 | 18 | _globals = globals() 19 | _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) 20 | _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.connection_status_pb2', _globals) 21 | if _descriptor._USE_C_DESCRIPTORS == False: 22 | DESCRIPTOR._options = None 23 | DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\020ConnStatusProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' 24 | _globals['_DEVICECONNECTIONSTATUS']._serialized_start=69 25 | _globals['_DEVICECONNECTIONSTATUS']._serialized_end=410 26 | _globals['_WIFICONNECTIONSTATUS']._serialized_start=412 27 | _globals['_WIFICONNECTIONSTATUS']._serialized_end=524 28 | _globals['_ETHERNETCONNECTIONSTATUS']._serialized_start=526 29 | _globals['_ETHERNETCONNECTIONSTATUS']._serialized_end=614 30 | _globals['_NETWORKCONNECTIONSTATUS']._serialized_start=616 31 | _globals['_NETWORKCONNECTIONSTATUS']._serialized_end=739 32 | _globals['_BLUETOOTHCONNECTIONSTATUS']._serialized_start=741 33 | _globals['_BLUETOOTHCONNECTIONSTATUS']._serialized_end=817 34 | _globals['_SERIALCONNECTIONSTATUS']._serialized_start=819 35 | _globals['_SERIALCONNECTIONSTATUS']._serialized_end=879 36 | # @@protoc_insertion_point(module_scope) 37 | -------------------------------------------------------------------------------- /meshtastic/protobuf/device_ui_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # source: meshtastic/protobuf/device_ui.proto 4 | """Generated protocol buffer code.""" 5 | from google.protobuf import descriptor as _descriptor 6 | from google.protobuf import descriptor_pool as _descriptor_pool 7 | from google.protobuf import symbol_database as _symbol_database 8 | from google.protobuf.internal import builder as _builder 9 | # @@protoc_insertion_point(imports) 10 | 11 | _sym_db = _symbol_database.Default() 12 | 13 | 14 | 15 | 16 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n#meshtastic/protobuf/device_ui.proto\x12\x13meshtastic.protobuf\"\xeb\x03\n\x0e\x44\x65viceUIConfig\x12\x0f\n\x07version\x18\x01 \x01(\r\x12\x19\n\x11screen_brightness\x18\x02 \x01(\r\x12\x16\n\x0escreen_timeout\x18\x03 \x01(\r\x12\x13\n\x0bscreen_lock\x18\x04 \x01(\x08\x12\x15\n\rsettings_lock\x18\x05 \x01(\x08\x12\x10\n\x08pin_code\x18\x06 \x01(\r\x12)\n\x05theme\x18\x07 \x01(\x0e\x32\x1a.meshtastic.protobuf.Theme\x12\x15\n\ralert_enabled\x18\x08 \x01(\x08\x12\x16\n\x0e\x62\x61nner_enabled\x18\t \x01(\x08\x12\x14\n\x0cring_tone_id\x18\n \x01(\r\x12/\n\x08language\x18\x0b \x01(\x0e\x32\x1d.meshtastic.protobuf.Language\x12\x34\n\x0bnode_filter\x18\x0c \x01(\x0b\x32\x1f.meshtastic.protobuf.NodeFilter\x12:\n\x0enode_highlight\x18\r \x01(\x0b\x32\".meshtastic.protobuf.NodeHighlight\x12\x18\n\x10\x63\x61libration_data\x18\x0e \x01(\x0c\x12*\n\x08map_data\x18\x0f \x01(\x0b\x32\x18.meshtastic.protobuf.Map\"\xa7\x01\n\nNodeFilter\x12\x16\n\x0eunknown_switch\x18\x01 \x01(\x08\x12\x16\n\x0eoffline_switch\x18\x02 \x01(\x08\x12\x19\n\x11public_key_switch\x18\x03 \x01(\x08\x12\x11\n\thops_away\x18\x04 \x01(\x05\x12\x17\n\x0fposition_switch\x18\x05 \x01(\x08\x12\x11\n\tnode_name\x18\x06 \x01(\t\x12\x0f\n\x07\x63hannel\x18\x07 \x01(\x05\"~\n\rNodeHighlight\x12\x13\n\x0b\x63hat_switch\x18\x01 \x01(\x08\x12\x17\n\x0fposition_switch\x18\x02 \x01(\x08\x12\x18\n\x10telemetry_switch\x18\x03 \x01(\x08\x12\x12\n\niaq_switch\x18\x04 \x01(\x08\x12\x11\n\tnode_name\x18\x05 \x01(\t\"=\n\x08GeoPoint\x12\x0c\n\x04zoom\x18\x01 \x01(\x05\x12\x10\n\x08latitude\x18\x02 \x01(\x05\x12\x11\n\tlongitude\x18\x03 \x01(\x05\"U\n\x03Map\x12+\n\x04home\x18\x01 \x01(\x0b\x32\x1d.meshtastic.protobuf.GeoPoint\x12\r\n\x05style\x18\x02 \x01(\t\x12\x12\n\nfollow_gps\x18\x03 \x01(\x08*%\n\x05Theme\x12\x08\n\x04\x44\x41RK\x10\x00\x12\t\n\x05LIGHT\x10\x01\x12\x07\n\x03RED\x10\x02*\x9a\x02\n\x08Language\x12\x0b\n\x07\x45NGLISH\x10\x00\x12\n\n\x06\x46RENCH\x10\x01\x12\n\n\x06GERMAN\x10\x02\x12\x0b\n\x07ITALIAN\x10\x03\x12\x0e\n\nPORTUGUESE\x10\x04\x12\x0b\n\x07SPANISH\x10\x05\x12\x0b\n\x07SWEDISH\x10\x06\x12\x0b\n\x07\x46INNISH\x10\x07\x12\n\n\x06POLISH\x10\x08\x12\x0b\n\x07TURKISH\x10\t\x12\x0b\n\x07SERBIAN\x10\n\x12\x0b\n\x07RUSSIAN\x10\x0b\x12\t\n\x05\x44UTCH\x10\x0c\x12\t\n\x05GREEK\x10\r\x12\r\n\tNORWEGIAN\x10\x0e\x12\r\n\tSLOVENIAN\x10\x0f\x12\r\n\tUKRAINIAN\x10\x10\x12\x16\n\x12SIMPLIFIED_CHINESE\x10\x1e\x12\x17\n\x13TRADITIONAL_CHINESE\x10\x1f\x42\x63\n\x13\x63om.geeksville.meshB\x0e\x44\x65viceUIProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') 17 | 18 | _globals = globals() 19 | _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) 20 | _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.device_ui_pb2', _globals) 21 | if _descriptor._USE_C_DESCRIPTORS == False: 22 | DESCRIPTOR._options = None 23 | DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\016DeviceUIProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' 24 | _globals['_THEME']._serialized_start=1002 25 | _globals['_THEME']._serialized_end=1039 26 | _globals['_LANGUAGE']._serialized_start=1042 27 | _globals['_LANGUAGE']._serialized_end=1324 28 | _globals['_DEVICEUICONFIG']._serialized_start=61 29 | _globals['_DEVICEUICONFIG']._serialized_end=552 30 | _globals['_NODEFILTER']._serialized_start=555 31 | _globals['_NODEFILTER']._serialized_end=722 32 | _globals['_NODEHIGHLIGHT']._serialized_start=724 33 | _globals['_NODEHIGHLIGHT']._serialized_end=850 34 | _globals['_GEOPOINT']._serialized_start=852 35 | _globals['_GEOPOINT']._serialized_end=913 36 | _globals['_MAP']._serialized_start=915 37 | _globals['_MAP']._serialized_end=1000 38 | # @@protoc_insertion_point(module_scope) 39 | -------------------------------------------------------------------------------- /meshtastic/protobuf/interdevice_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # source: meshtastic/protobuf/interdevice.proto 4 | """Generated protocol buffer code.""" 5 | from google.protobuf import descriptor as _descriptor 6 | from google.protobuf import descriptor_pool as _descriptor_pool 7 | from google.protobuf import symbol_database as _symbol_database 8 | from google.protobuf.internal import builder as _builder 9 | # @@protoc_insertion_point(imports) 10 | 11 | _sym_db = _symbol_database.Default() 12 | 13 | 14 | 15 | 16 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n%meshtastic/protobuf/interdevice.proto\x12\x13meshtastic.protobuf\"s\n\nSensorData\x12.\n\x04type\x18\x01 \x01(\x0e\x32 .meshtastic.protobuf.MessageType\x12\x15\n\x0b\x66loat_value\x18\x02 \x01(\x02H\x00\x12\x16\n\x0cuint32_value\x18\x03 \x01(\rH\x00\x42\x06\n\x04\x64\x61ta\"_\n\x12InterdeviceMessage\x12\x0e\n\x04nmea\x18\x01 \x01(\tH\x00\x12\x31\n\x06sensor\x18\x02 \x01(\x0b\x32\x1f.meshtastic.protobuf.SensorDataH\x00\x42\x06\n\x04\x64\x61ta*\xd5\x01\n\x0bMessageType\x12\x07\n\x03\x41\x43K\x10\x00\x12\x15\n\x10\x43OLLECT_INTERVAL\x10\xa0\x01\x12\x0c\n\x07\x42\x45\x45P_ON\x10\xa1\x01\x12\r\n\x08\x42\x45\x45P_OFF\x10\xa2\x01\x12\r\n\x08SHUTDOWN\x10\xa3\x01\x12\r\n\x08POWER_ON\x10\xa4\x01\x12\x0f\n\nSCD41_TEMP\x10\xb0\x01\x12\x13\n\x0eSCD41_HUMIDITY\x10\xb1\x01\x12\x0e\n\tSCD41_CO2\x10\xb2\x01\x12\x0f\n\nAHT20_TEMP\x10\xb3\x01\x12\x13\n\x0e\x41HT20_HUMIDITY\x10\xb4\x01\x12\x0f\n\nTVOC_INDEX\x10\xb5\x01\x42\x66\n\x13\x63om.geeksville.meshB\x11InterdeviceProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') 17 | 18 | _globals = globals() 19 | _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) 20 | _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.interdevice_pb2', _globals) 21 | if _descriptor._USE_C_DESCRIPTORS == False: 22 | DESCRIPTOR._options = None 23 | DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\021InterdeviceProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' 24 | _globals['_MESSAGETYPE']._serialized_start=277 25 | _globals['_MESSAGETYPE']._serialized_end=490 26 | _globals['_SENSORDATA']._serialized_start=62 27 | _globals['_SENSORDATA']._serialized_end=177 28 | _globals['_INTERDEVICEMESSAGE']._serialized_start=179 29 | _globals['_INTERDEVICEMESSAGE']._serialized_end=274 30 | # @@protoc_insertion_point(module_scope) 31 | -------------------------------------------------------------------------------- /meshtastic/protobuf/interdevice_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import google.protobuf.descriptor 8 | import google.protobuf.internal.enum_type_wrapper 9 | import google.protobuf.message 10 | import sys 11 | import typing 12 | 13 | if sys.version_info >= (3, 10): 14 | import typing as typing_extensions 15 | else: 16 | import typing_extensions 17 | 18 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 19 | 20 | class _MessageType: 21 | ValueType = typing.NewType("ValueType", builtins.int) 22 | V: typing_extensions.TypeAlias = ValueType 23 | 24 | class _MessageTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_MessageType.ValueType], builtins.type): 25 | DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor 26 | ACK: _MessageType.ValueType # 0 27 | COLLECT_INTERVAL: _MessageType.ValueType # 160 28 | """in ms""" 29 | BEEP_ON: _MessageType.ValueType # 161 30 | """duration ms""" 31 | BEEP_OFF: _MessageType.ValueType # 162 32 | """cancel prematurely""" 33 | SHUTDOWN: _MessageType.ValueType # 163 34 | POWER_ON: _MessageType.ValueType # 164 35 | SCD41_TEMP: _MessageType.ValueType # 176 36 | SCD41_HUMIDITY: _MessageType.ValueType # 177 37 | SCD41_CO2: _MessageType.ValueType # 178 38 | AHT20_TEMP: _MessageType.ValueType # 179 39 | AHT20_HUMIDITY: _MessageType.ValueType # 180 40 | TVOC_INDEX: _MessageType.ValueType # 181 41 | 42 | class MessageType(_MessageType, metaclass=_MessageTypeEnumTypeWrapper): 43 | """encapsulate up to 1k of NMEA string data""" 44 | 45 | ACK: MessageType.ValueType # 0 46 | COLLECT_INTERVAL: MessageType.ValueType # 160 47 | """in ms""" 48 | BEEP_ON: MessageType.ValueType # 161 49 | """duration ms""" 50 | BEEP_OFF: MessageType.ValueType # 162 51 | """cancel prematurely""" 52 | SHUTDOWN: MessageType.ValueType # 163 53 | POWER_ON: MessageType.ValueType # 164 54 | SCD41_TEMP: MessageType.ValueType # 176 55 | SCD41_HUMIDITY: MessageType.ValueType # 177 56 | SCD41_CO2: MessageType.ValueType # 178 57 | AHT20_TEMP: MessageType.ValueType # 179 58 | AHT20_HUMIDITY: MessageType.ValueType # 180 59 | TVOC_INDEX: MessageType.ValueType # 181 60 | global___MessageType = MessageType 61 | 62 | @typing.final 63 | class SensorData(google.protobuf.message.Message): 64 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 65 | 66 | TYPE_FIELD_NUMBER: builtins.int 67 | FLOAT_VALUE_FIELD_NUMBER: builtins.int 68 | UINT32_VALUE_FIELD_NUMBER: builtins.int 69 | type: global___MessageType.ValueType 70 | """The message type""" 71 | float_value: builtins.float 72 | uint32_value: builtins.int 73 | def __init__( 74 | self, 75 | *, 76 | type: global___MessageType.ValueType = ..., 77 | float_value: builtins.float = ..., 78 | uint32_value: builtins.int = ..., 79 | ) -> None: ... 80 | def HasField(self, field_name: typing.Literal["data", b"data", "float_value", b"float_value", "uint32_value", b"uint32_value"]) -> builtins.bool: ... 81 | def ClearField(self, field_name: typing.Literal["data", b"data", "float_value", b"float_value", "type", b"type", "uint32_value", b"uint32_value"]) -> None: ... 82 | def WhichOneof(self, oneof_group: typing.Literal["data", b"data"]) -> typing.Literal["float_value", "uint32_value"] | None: ... 83 | 84 | global___SensorData = SensorData 85 | 86 | @typing.final 87 | class InterdeviceMessage(google.protobuf.message.Message): 88 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 89 | 90 | NMEA_FIELD_NUMBER: builtins.int 91 | SENSOR_FIELD_NUMBER: builtins.int 92 | nmea: builtins.str 93 | @property 94 | def sensor(self) -> global___SensorData: ... 95 | def __init__( 96 | self, 97 | *, 98 | nmea: builtins.str = ..., 99 | sensor: global___SensorData | None = ..., 100 | ) -> None: ... 101 | def HasField(self, field_name: typing.Literal["data", b"data", "nmea", b"nmea", "sensor", b"sensor"]) -> builtins.bool: ... 102 | def ClearField(self, field_name: typing.Literal["data", b"data", "nmea", b"nmea", "sensor", b"sensor"]) -> None: ... 103 | def WhichOneof(self, oneof_group: typing.Literal["data", b"data"]) -> typing.Literal["nmea", "sensor"] | None: ... 104 | 105 | global___InterdeviceMessage = InterdeviceMessage 106 | -------------------------------------------------------------------------------- /meshtastic/protobuf/localonly_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # source: meshtastic/protobuf/localonly.proto 4 | """Generated protocol buffer code.""" 5 | from google.protobuf import descriptor as _descriptor 6 | from google.protobuf import descriptor_pool as _descriptor_pool 7 | from google.protobuf import symbol_database as _symbol_database 8 | from google.protobuf.internal import builder as _builder 9 | # @@protoc_insertion_point(imports) 10 | 11 | _sym_db = _symbol_database.Default() 12 | 13 | 14 | from meshtastic.protobuf import config_pb2 as meshtastic_dot_protobuf_dot_config__pb2 15 | from meshtastic.protobuf import module_config_pb2 as meshtastic_dot_protobuf_dot_module__config__pb2 16 | 17 | 18 | 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\"\xfa\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\x12<\n\x08security\x18\t \x01(\x0b\x32*.meshtastic.protobuf.Config.SecurityConfig\"\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') 19 | 20 | _globals = globals() 21 | _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) 22 | _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.localonly_pb2', _globals) 23 | if _descriptor._USE_C_DESCRIPTORS == False: 24 | DESCRIPTOR._options = None 25 | DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\017LocalOnlyProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' 26 | _globals['_LOCALCONFIG']._serialized_start=136 27 | _globals['_LOCALCONFIG']._serialized_end=642 28 | _globals['_LOCALMODULECONFIG']._serialized_start=645 29 | _globals['_LOCALMODULECONFIG']._serialized_end=1653 30 | # @@protoc_insertion_point(module_scope) 31 | -------------------------------------------------------------------------------- /meshtastic/protobuf/mqtt_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # source: meshtastic/protobuf/mqtt.proto 4 | """Generated protocol buffer code.""" 5 | from google.protobuf import descriptor as _descriptor 6 | from google.protobuf import descriptor_pool as _descriptor_pool 7 | from google.protobuf import symbol_database as _symbol_database 8 | from google.protobuf.internal import builder as _builder 9 | # @@protoc_insertion_point(imports) 10 | 11 | _sym_db = _symbol_database.Default() 12 | 13 | 14 | from meshtastic.protobuf import config_pb2 as meshtastic_dot_protobuf_dot_config__pb2 15 | from meshtastic.protobuf import mesh_pb2 as meshtastic_dot_protobuf_dot_mesh__pb2 16 | 17 | 18 | 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') 19 | 20 | _globals = globals() 21 | _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) 22 | _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.mqtt_pb2', _globals) 23 | if _descriptor._USE_C_DESCRIPTORS == False: 24 | DESCRIPTOR._options = None 25 | DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\nMQTTProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' 26 | _globals['_SERVICEENVELOPE']._serialized_start=121 27 | _globals['_SERVICEENVELOPE']._serialized_end=227 28 | _globals['_MAPREPORT']._serialized_start=230 29 | _globals['_MAPREPORT']._serialized_end=710 30 | # @@protoc_insertion_point(module_scope) 31 | -------------------------------------------------------------------------------- /meshtastic/protobuf/nanopb_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # source: meshtastic/protobuf/nanopb.proto 4 | """Generated protocol buffer code.""" 5 | from google.protobuf import descriptor as _descriptor 6 | from google.protobuf import descriptor_pool as _descriptor_pool 7 | from google.protobuf import symbol_database as _symbol_database 8 | from google.protobuf.internal import builder as _builder 9 | # @@protoc_insertion_point(imports) 10 | 11 | _sym_db = _symbol_database.Default() 12 | 13 | 14 | from google.protobuf import descriptor_pb2 as google_dot_protobuf_dot_descriptor__pb2 15 | 16 | 17 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n meshtastic/protobuf/nanopb.proto\x1a google/protobuf/descriptor.proto\"\xa4\x07\n\rNanoPBOptions\x12\x10\n\x08max_size\x18\x01 \x01(\x05\x12\x12\n\nmax_length\x18\x0e \x01(\x05\x12\x11\n\tmax_count\x18\x02 \x01(\x05\x12&\n\x08int_size\x18\x07 \x01(\x0e\x32\x08.IntSize:\nIS_DEFAULT\x12$\n\x04type\x18\x03 \x01(\x0e\x32\n.FieldType:\nFT_DEFAULT\x12\x18\n\nlong_names\x18\x04 \x01(\x08:\x04true\x12\x1c\n\rpacked_struct\x18\x05 \x01(\x08:\x05\x66\x61lse\x12\x1a\n\x0bpacked_enum\x18\n \x01(\x08:\x05\x66\x61lse\x12\x1b\n\x0cskip_message\x18\x06 \x01(\x08:\x05\x66\x61lse\x12\x18\n\tno_unions\x18\x08 \x01(\x08:\x05\x66\x61lse\x12\r\n\x05msgid\x18\t \x01(\r\x12\x1e\n\x0f\x61nonymous_oneof\x18\x0b \x01(\x08:\x05\x66\x61lse\x12\x15\n\x06proto3\x18\x0c \x01(\x08:\x05\x66\x61lse\x12#\n\x14proto3_singular_msgs\x18\x15 \x01(\x08:\x05\x66\x61lse\x12\x1d\n\x0e\x65num_to_string\x18\r \x01(\x08:\x05\x66\x61lse\x12\x1b\n\x0c\x66ixed_length\x18\x0f \x01(\x08:\x05\x66\x61lse\x12\x1a\n\x0b\x66ixed_count\x18\x10 \x01(\x08:\x05\x66\x61lse\x12\x1e\n\x0fsubmsg_callback\x18\x16 \x01(\x08:\x05\x66\x61lse\x12/\n\x0cmangle_names\x18\x11 \x01(\x0e\x32\x11.TypenameMangling:\x06M_NONE\x12(\n\x11\x63\x61llback_datatype\x18\x12 \x01(\t:\rpb_callback_t\x12\x34\n\x11\x63\x61llback_function\x18\x13 \x01(\t:\x19pb_default_field_callback\x12\x30\n\x0e\x64\x65scriptorsize\x18\x14 \x01(\x0e\x32\x0f.DescriptorSize:\x07\x44S_AUTO\x12\x1a\n\x0b\x64\x65\x66\x61ult_has\x18\x17 \x01(\x08:\x05\x66\x61lse\x12\x0f\n\x07include\x18\x18 \x03(\t\x12\x0f\n\x07\x65xclude\x18\x1a \x03(\t\x12\x0f\n\x07package\x18\x19 \x01(\t\x12\x41\n\rtype_override\x18\x1b \x01(\x0e\x32*.google.protobuf.FieldDescriptorProto.Type\x12\x19\n\x0bsort_by_tag\x18\x1c \x01(\x08:\x04true\x12.\n\rfallback_type\x18\x1d \x01(\x0e\x32\n.FieldType:\x0b\x46T_CALLBACK*i\n\tFieldType\x12\x0e\n\nFT_DEFAULT\x10\x00\x12\x0f\n\x0b\x46T_CALLBACK\x10\x01\x12\x0e\n\nFT_POINTER\x10\x04\x12\r\n\tFT_STATIC\x10\x02\x12\r\n\tFT_IGNORE\x10\x03\x12\r\n\tFT_INLINE\x10\x05*D\n\x07IntSize\x12\x0e\n\nIS_DEFAULT\x10\x00\x12\x08\n\x04IS_8\x10\x08\x12\t\n\x05IS_16\x10\x10\x12\t\n\x05IS_32\x10 \x12\t\n\x05IS_64\x10@*Z\n\x10TypenameMangling\x12\n\n\x06M_NONE\x10\x00\x12\x13\n\x0fM_STRIP_PACKAGE\x10\x01\x12\r\n\tM_FLATTEN\x10\x02\x12\x16\n\x12M_PACKAGE_INITIALS\x10\x03*E\n\x0e\x44\x65scriptorSize\x12\x0b\n\x07\x44S_AUTO\x10\x00\x12\x08\n\x04\x44S_1\x10\x01\x12\x08\n\x04\x44S_2\x10\x02\x12\x08\n\x04\x44S_4\x10\x04\x12\x08\n\x04\x44S_8\x10\x08:E\n\x0enanopb_fileopt\x12\x1c.google.protobuf.FileOptions\x18\xf2\x07 \x01(\x0b\x32\x0e.NanoPBOptions:G\n\rnanopb_msgopt\x12\x1f.google.protobuf.MessageOptions\x18\xf2\x07 \x01(\x0b\x32\x0e.NanoPBOptions:E\n\x0enanopb_enumopt\x12\x1c.google.protobuf.EnumOptions\x18\xf2\x07 \x01(\x0b\x32\x0e.NanoPBOptions:>\n\x06nanopb\x12\x1d.google.protobuf.FieldOptions\x18\xf2\x07 \x01(\x0b\x32\x0e.NanoPBOptionsB>\n\x18\x66i.kapsi.koti.jpa.nanopbZ\"github.com/meshtastic/go/generated') 18 | 19 | _globals = globals() 20 | _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) 21 | _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.nanopb_pb2', _globals) 22 | if _descriptor._USE_C_DESCRIPTORS == False: 23 | DESCRIPTOR._options = None 24 | DESCRIPTOR._serialized_options = b'\n\030fi.kapsi.koti.jpa.nanopbZ\"github.com/meshtastic/go/generated' 25 | _globals['_FIELDTYPE']._serialized_start=1005 26 | _globals['_FIELDTYPE']._serialized_end=1110 27 | _globals['_INTSIZE']._serialized_start=1112 28 | _globals['_INTSIZE']._serialized_end=1180 29 | _globals['_TYPENAMEMANGLING']._serialized_start=1182 30 | _globals['_TYPENAMEMANGLING']._serialized_end=1272 31 | _globals['_DESCRIPTORSIZE']._serialized_start=1274 32 | _globals['_DESCRIPTORSIZE']._serialized_end=1343 33 | _globals['_NANOPBOPTIONS']._serialized_start=71 34 | _globals['_NANOPBOPTIONS']._serialized_end=1003 35 | # @@protoc_insertion_point(module_scope) 36 | -------------------------------------------------------------------------------- /meshtastic/protobuf/paxcount_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # source: meshtastic/protobuf/paxcount.proto 4 | """Generated protocol buffer code.""" 5 | from google.protobuf import descriptor as _descriptor 6 | from google.protobuf import descriptor_pool as _descriptor_pool 7 | from google.protobuf import symbol_database as _symbol_database 8 | from google.protobuf.internal import builder as _builder 9 | # @@protoc_insertion_point(imports) 10 | 11 | _sym_db = _symbol_database.Default() 12 | 13 | 14 | 15 | 16 | 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') 17 | 18 | _globals = globals() 19 | _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) 20 | _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.paxcount_pb2', _globals) 21 | if _descriptor._USE_C_DESCRIPTORS == False: 22 | DESCRIPTOR._options = None 23 | DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\016PaxcountProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' 24 | _globals['_PAXCOUNT']._serialized_start=59 25 | _globals['_PAXCOUNT']._serialized_end=112 26 | # @@protoc_insertion_point(module_scope) 27 | -------------------------------------------------------------------------------- /meshtastic/protobuf/paxcount_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import google.protobuf.descriptor 8 | import google.protobuf.message 9 | import typing 10 | 11 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 12 | 13 | @typing.final 14 | class Paxcount(google.protobuf.message.Message): 15 | """ 16 | TODO: REPLACE 17 | """ 18 | 19 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 20 | 21 | WIFI_FIELD_NUMBER: builtins.int 22 | BLE_FIELD_NUMBER: builtins.int 23 | UPTIME_FIELD_NUMBER: builtins.int 24 | wifi: builtins.int 25 | """ 26 | seen Wifi devices 27 | """ 28 | ble: builtins.int 29 | """ 30 | Seen BLE devices 31 | """ 32 | uptime: builtins.int 33 | """ 34 | Uptime in seconds 35 | """ 36 | def __init__( 37 | self, 38 | *, 39 | wifi: builtins.int = ..., 40 | ble: builtins.int = ..., 41 | uptime: builtins.int = ..., 42 | ) -> None: ... 43 | def ClearField(self, field_name: typing.Literal["ble", b"ble", "uptime", b"uptime", "wifi", b"wifi"]) -> None: ... 44 | 45 | global___Paxcount = Paxcount 46 | -------------------------------------------------------------------------------- /meshtastic/protobuf/portnums_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # source: meshtastic/protobuf/portnums.proto 4 | """Generated protocol buffer code.""" 5 | from google.protobuf import descriptor as _descriptor 6 | from google.protobuf import descriptor_pool as _descriptor_pool 7 | from google.protobuf import symbol_database as _symbol_database 8 | from google.protobuf.internal import builder as _builder 9 | # @@protoc_insertion_point(imports) 10 | 11 | _sym_db = _symbol_database.Default() 12 | 13 | 14 | 15 | 16 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\"meshtastic/protobuf/portnums.proto\x12\x13meshtastic.protobuf*\xcb\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\tALERT_APP\x10\x0b\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\x13\n\x0fPOWERSTRESS_APP\x10J\x12\x18\n\x14RETICULUM_TUNNEL_APP\x10L\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') 17 | 18 | _globals = globals() 19 | _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) 20 | _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.portnums_pb2', _globals) 21 | if _descriptor._USE_C_DESCRIPTORS == False: 22 | DESCRIPTOR._options = None 23 | DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\010PortnumsZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' 24 | _globals['_PORTNUM']._serialized_start=60 25 | _globals['_PORTNUM']._serialized_end=647 26 | # @@protoc_insertion_point(module_scope) 27 | -------------------------------------------------------------------------------- /meshtastic/protobuf/powermon_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # source: meshtastic/protobuf/powermon.proto 4 | """Generated protocol buffer code.""" 5 | from google.protobuf import descriptor as _descriptor 6 | from google.protobuf import descriptor_pool as _descriptor_pool 7 | from google.protobuf import symbol_database as _symbol_database 8 | from google.protobuf.internal import builder as _builder 9 | # @@protoc_insertion_point(imports) 10 | 11 | _sym_db = _symbol_database.Default() 12 | 13 | 14 | 15 | 16 | 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\"\x88\x03\n\x12PowerStressMessage\x12;\n\x03\x63md\x18\x01 \x01(\x0e\x32..meshtastic.protobuf.PowerStressMessage.Opcode\x12\x13\n\x0bnum_seconds\x18\x02 \x01(\x02\"\x9f\x02\n\x06Opcode\x12\t\n\x05UNSET\x10\x00\x12\x0e\n\nPRINT_INFO\x10\x01\x12\x0f\n\x0b\x46ORCE_QUIET\x10\x02\x12\r\n\tEND_QUIET\x10\x03\x12\r\n\tSCREEN_ON\x10\x10\x12\x0e\n\nSCREEN_OFF\x10\x11\x12\x0c\n\x08\x43PU_IDLE\x10 \x12\x11\n\rCPU_DEEPSLEEP\x10!\x12\x0e\n\nCPU_FULLON\x10\"\x12\n\n\x06LED_ON\x10\x30\x12\x0b\n\x07LED_OFF\x10\x31\x12\x0c\n\x08LORA_OFF\x10@\x12\x0b\n\x07LORA_TX\x10\x41\x12\x0b\n\x07LORA_RX\x10\x42\x12\n\n\x06\x42T_OFF\x10P\x12\t\n\x05\x42T_ON\x10Q\x12\x0c\n\x08WIFI_OFF\x10`\x12\x0b\n\x07WIFI_ON\x10\x61\x12\x0b\n\x07GPS_OFF\x10p\x12\n\n\x06GPS_ON\x10qBc\n\x13\x63om.geeksville.meshB\x0ePowerMonProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') 17 | 18 | _globals = globals() 19 | _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) 20 | _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.powermon_pb2', _globals) 21 | if _descriptor._USE_C_DESCRIPTORS == False: 22 | DESCRIPTOR._options = None 23 | DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\016PowerMonProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' 24 | _globals['_POWERMON']._serialized_start=60 25 | _globals['_POWERMON']._serialized_end=284 26 | _globals['_POWERMON_STATE']._serialized_start=73 27 | _globals['_POWERMON_STATE']._serialized_end=284 28 | _globals['_POWERSTRESSMESSAGE']._serialized_start=287 29 | _globals['_POWERSTRESSMESSAGE']._serialized_end=679 30 | _globals['_POWERSTRESSMESSAGE_OPCODE']._serialized_start=392 31 | _globals['_POWERSTRESSMESSAGE_OPCODE']._serialized_end=679 32 | # @@protoc_insertion_point(module_scope) 33 | -------------------------------------------------------------------------------- /meshtastic/protobuf/remote_hardware_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # source: meshtastic/protobuf/remote_hardware.proto 4 | """Generated protocol buffer code.""" 5 | from google.protobuf import descriptor as _descriptor 6 | from google.protobuf import descriptor_pool as _descriptor_pool 7 | from google.protobuf import symbol_database as _symbol_database 8 | from google.protobuf.internal import builder as _builder 9 | # @@protoc_insertion_point(imports) 10 | 11 | _sym_db = _symbol_database.Default() 12 | 13 | 14 | 15 | 16 | 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') 17 | 18 | _globals = globals() 19 | _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) 20 | _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.remote_hardware_pb2', _globals) 21 | if _descriptor._USE_C_DESCRIPTORS == False: 22 | DESCRIPTOR._options = None 23 | DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\016RemoteHardwareZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' 24 | _globals['_HARDWAREMESSAGE']._serialized_start=67 25 | _globals['_HARDWAREMESSAGE']._serialized_end=290 26 | _globals['_HARDWAREMESSAGE_TYPE']._serialized_start=182 27 | _globals['_HARDWAREMESSAGE_TYPE']._serialized_end=290 28 | # @@protoc_insertion_point(module_scope) 29 | -------------------------------------------------------------------------------- /meshtastic/protobuf/remote_hardware_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import google.protobuf.descriptor 8 | import google.protobuf.internal.enum_type_wrapper 9 | import google.protobuf.message 10 | import sys 11 | import typing 12 | 13 | if sys.version_info >= (3, 10): 14 | import typing as typing_extensions 15 | else: 16 | import typing_extensions 17 | 18 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 19 | 20 | @typing.final 21 | class HardwareMessage(google.protobuf.message.Message): 22 | """ 23 | An example app to show off the module system. This message is used for 24 | REMOTE_HARDWARE_APP PortNums. 25 | Also provides easy remote access to any GPIO. 26 | In the future other remote hardware operations can be added based on user interest 27 | (i.e. serial output, spi/i2c input/output). 28 | FIXME - currently this feature is turned on by default which is dangerous 29 | because no security yet (beyond the channel mechanism). 30 | It should be off by default and then protected based on some TBD mechanism 31 | (a special channel once multichannel support is included?) 32 | """ 33 | 34 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 35 | 36 | class _Type: 37 | ValueType = typing.NewType("ValueType", builtins.int) 38 | V: typing_extensions.TypeAlias = ValueType 39 | 40 | class _TypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[HardwareMessage._Type.ValueType], builtins.type): 41 | DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor 42 | UNSET: HardwareMessage._Type.ValueType # 0 43 | """ 44 | Unset/unused 45 | """ 46 | WRITE_GPIOS: HardwareMessage._Type.ValueType # 1 47 | """ 48 | Set gpio gpios based on gpio_mask/gpio_value 49 | """ 50 | WATCH_GPIOS: HardwareMessage._Type.ValueType # 2 51 | """ 52 | We are now interested in watching the gpio_mask gpios. 53 | If the selected gpios change, please broadcast GPIOS_CHANGED. 54 | Will implicitly change the gpios requested to be INPUT gpios. 55 | """ 56 | GPIOS_CHANGED: HardwareMessage._Type.ValueType # 3 57 | """ 58 | The gpios listed in gpio_mask have changed, the new values are listed in gpio_value 59 | """ 60 | READ_GPIOS: HardwareMessage._Type.ValueType # 4 61 | """ 62 | Read the gpios specified in gpio_mask, send back a READ_GPIOS_REPLY reply with gpio_value populated 63 | """ 64 | READ_GPIOS_REPLY: HardwareMessage._Type.ValueType # 5 65 | """ 66 | A reply to READ_GPIOS. gpio_mask and gpio_value will be populated 67 | """ 68 | 69 | class Type(_Type, metaclass=_TypeEnumTypeWrapper): 70 | """ 71 | TODO: REPLACE 72 | """ 73 | 74 | UNSET: HardwareMessage.Type.ValueType # 0 75 | """ 76 | Unset/unused 77 | """ 78 | WRITE_GPIOS: HardwareMessage.Type.ValueType # 1 79 | """ 80 | Set gpio gpios based on gpio_mask/gpio_value 81 | """ 82 | WATCH_GPIOS: HardwareMessage.Type.ValueType # 2 83 | """ 84 | We are now interested in watching the gpio_mask gpios. 85 | If the selected gpios change, please broadcast GPIOS_CHANGED. 86 | Will implicitly change the gpios requested to be INPUT gpios. 87 | """ 88 | GPIOS_CHANGED: HardwareMessage.Type.ValueType # 3 89 | """ 90 | The gpios listed in gpio_mask have changed, the new values are listed in gpio_value 91 | """ 92 | READ_GPIOS: HardwareMessage.Type.ValueType # 4 93 | """ 94 | Read the gpios specified in gpio_mask, send back a READ_GPIOS_REPLY reply with gpio_value populated 95 | """ 96 | READ_GPIOS_REPLY: HardwareMessage.Type.ValueType # 5 97 | """ 98 | A reply to READ_GPIOS. gpio_mask and gpio_value will be populated 99 | """ 100 | 101 | TYPE_FIELD_NUMBER: builtins.int 102 | GPIO_MASK_FIELD_NUMBER: builtins.int 103 | GPIO_VALUE_FIELD_NUMBER: builtins.int 104 | type: global___HardwareMessage.Type.ValueType 105 | """ 106 | What type of HardwareMessage is this? 107 | """ 108 | gpio_mask: builtins.int 109 | """ 110 | What gpios are we changing. Not used for all MessageTypes, see MessageType for details 111 | """ 112 | gpio_value: builtins.int 113 | """ 114 | For gpios that were listed in gpio_mask as valid, what are the signal levels for those gpios. 115 | Not used for all MessageTypes, see MessageType for details 116 | """ 117 | def __init__( 118 | self, 119 | *, 120 | type: global___HardwareMessage.Type.ValueType = ..., 121 | gpio_mask: builtins.int = ..., 122 | gpio_value: builtins.int = ..., 123 | ) -> None: ... 124 | def ClearField(self, field_name: typing.Literal["gpio_mask", b"gpio_mask", "gpio_value", b"gpio_value", "type", b"type"]) -> None: ... 125 | 126 | global___HardwareMessage = HardwareMessage 127 | -------------------------------------------------------------------------------- /meshtastic/protobuf/rtttl_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # source: meshtastic/protobuf/rtttl.proto 4 | """Generated protocol buffer code.""" 5 | from google.protobuf import descriptor as _descriptor 6 | from google.protobuf import descriptor_pool as _descriptor_pool 7 | from google.protobuf import symbol_database as _symbol_database 8 | from google.protobuf.internal import builder as _builder 9 | # @@protoc_insertion_point(imports) 10 | 11 | _sym_db = _symbol_database.Default() 12 | 13 | 14 | 15 | 16 | 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') 17 | 18 | _globals = globals() 19 | _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) 20 | _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.rtttl_pb2', _globals) 21 | if _descriptor._USE_C_DESCRIPTORS == False: 22 | DESCRIPTOR._options = None 23 | DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\021RTTTLConfigProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' 24 | _globals['_RTTTLCONFIG']._serialized_start=56 25 | _globals['_RTTTLCONFIG']._serialized_end=87 26 | # @@protoc_insertion_point(module_scope) 27 | -------------------------------------------------------------------------------- /meshtastic/protobuf/rtttl_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import google.protobuf.descriptor 8 | import google.protobuf.message 9 | import typing 10 | 11 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 12 | 13 | @typing.final 14 | class RTTTLConfig(google.protobuf.message.Message): 15 | """ 16 | Canned message module configuration. 17 | """ 18 | 19 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 20 | 21 | RINGTONE_FIELD_NUMBER: builtins.int 22 | ringtone: builtins.str 23 | """ 24 | Ringtone for PWM Buzzer in RTTTL Format. 25 | """ 26 | def __init__( 27 | self, 28 | *, 29 | ringtone: builtins.str = ..., 30 | ) -> None: ... 31 | def ClearField(self, field_name: typing.Literal["ringtone", b"ringtone"]) -> None: ... 32 | 33 | global___RTTTLConfig = RTTTLConfig 34 | -------------------------------------------------------------------------------- /meshtastic/protobuf/storeforward_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # source: meshtastic/protobuf/storeforward.proto 4 | """Generated protocol buffer code.""" 5 | from google.protobuf import descriptor as _descriptor 6 | from google.protobuf import descriptor_pool as _descriptor_pool 7 | from google.protobuf import symbol_database as _symbol_database 8 | from google.protobuf.internal import builder as _builder 9 | # @@protoc_insertion_point(imports) 10 | 11 | _sym_db = _symbol_database.Default() 12 | 13 | 14 | 15 | 16 | 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') 17 | 18 | _globals = globals() 19 | _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) 20 | _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.storeforward_pb2', _globals) 21 | if _descriptor._USE_C_DESCRIPTORS == False: 22 | DESCRIPTOR._options = None 23 | DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\025StoreAndForwardProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' 24 | _globals['_STOREANDFORWARD']._serialized_start=64 25 | _globals['_STOREANDFORWARD']._serialized_end=1024 26 | _globals['_STOREANDFORWARD_STATISTICS']._serialized_start=366 27 | _globals['_STOREANDFORWARD_STATISTICS']._serialized_end=571 28 | _globals['_STOREANDFORWARD_HISTORY']._serialized_start=573 29 | _globals['_STOREANDFORWARD_HISTORY']._serialized_end=646 30 | _globals['_STOREANDFORWARD_HEARTBEAT']._serialized_start=648 31 | _globals['_STOREANDFORWARD_HEARTBEAT']._serialized_end=694 32 | _globals['_STOREANDFORWARD_REQUESTRESPONSE']._serialized_start=697 33 | _globals['_STOREANDFORWARD_REQUESTRESPONSE']._serialized_end=1013 34 | # @@protoc_insertion_point(module_scope) 35 | -------------------------------------------------------------------------------- /meshtastic/protobuf/xmodem_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # source: meshtastic/protobuf/xmodem.proto 4 | """Generated protocol buffer code.""" 5 | from google.protobuf import descriptor as _descriptor 6 | from google.protobuf import descriptor_pool as _descriptor_pool 7 | from google.protobuf import symbol_database as _symbol_database 8 | from google.protobuf.internal import builder as _builder 9 | # @@protoc_insertion_point(imports) 10 | 11 | _sym_db = _symbol_database.Default() 12 | 13 | 14 | 15 | 16 | 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') 17 | 18 | _globals = globals() 19 | _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) 20 | _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.xmodem_pb2', _globals) 21 | if _descriptor._USE_C_DESCRIPTORS == False: 22 | DESCRIPTOR._options = None 23 | DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\014XmodemProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' 24 | _globals['_XMODEM']._serialized_start=58 25 | _globals['_XMODEM']._serialized_end=249 26 | _globals['_XMODEM_CONTROL']._serialized_start=166 27 | _globals['_XMODEM_CONTROL']._serialized_end=249 28 | # @@protoc_insertion_point(module_scope) 29 | -------------------------------------------------------------------------------- /meshtastic/protobuf/xmodem_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import google.protobuf.descriptor 8 | import google.protobuf.internal.enum_type_wrapper 9 | import google.protobuf.message 10 | import sys 11 | import typing 12 | 13 | if sys.version_info >= (3, 10): 14 | import typing as typing_extensions 15 | else: 16 | import typing_extensions 17 | 18 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 19 | 20 | @typing.final 21 | class XModem(google.protobuf.message.Message): 22 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 23 | 24 | class _Control: 25 | ValueType = typing.NewType("ValueType", builtins.int) 26 | V: typing_extensions.TypeAlias = ValueType 27 | 28 | class _ControlEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[XModem._Control.ValueType], builtins.type): 29 | DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor 30 | NUL: XModem._Control.ValueType # 0 31 | SOH: XModem._Control.ValueType # 1 32 | STX: XModem._Control.ValueType # 2 33 | EOT: XModem._Control.ValueType # 4 34 | ACK: XModem._Control.ValueType # 6 35 | NAK: XModem._Control.ValueType # 21 36 | CAN: XModem._Control.ValueType # 24 37 | CTRLZ: XModem._Control.ValueType # 26 38 | 39 | class Control(_Control, metaclass=_ControlEnumTypeWrapper): ... 40 | NUL: XModem.Control.ValueType # 0 41 | SOH: XModem.Control.ValueType # 1 42 | STX: XModem.Control.ValueType # 2 43 | EOT: XModem.Control.ValueType # 4 44 | ACK: XModem.Control.ValueType # 6 45 | NAK: XModem.Control.ValueType # 21 46 | CAN: XModem.Control.ValueType # 24 47 | CTRLZ: XModem.Control.ValueType # 26 48 | 49 | CONTROL_FIELD_NUMBER: builtins.int 50 | SEQ_FIELD_NUMBER: builtins.int 51 | CRC16_FIELD_NUMBER: builtins.int 52 | BUFFER_FIELD_NUMBER: builtins.int 53 | control: global___XModem.Control.ValueType 54 | seq: builtins.int 55 | crc16: builtins.int 56 | buffer: builtins.bytes 57 | def __init__( 58 | self, 59 | *, 60 | control: global___XModem.Control.ValueType = ..., 61 | seq: builtins.int = ..., 62 | crc16: builtins.int = ..., 63 | buffer: builtins.bytes = ..., 64 | ) -> None: ... 65 | def ClearField(self, field_name: typing.Literal["buffer", b"buffer", "control", b"control", "crc16", b"crc16", "seq", b"seq"]) -> None: ... 66 | 67 | global___XModem = XModem 68 | -------------------------------------------------------------------------------- /meshtastic/remote_hardware.py: -------------------------------------------------------------------------------- 1 | """Remote hardware 2 | """ 3 | import logging 4 | 5 | from pubsub import pub # type: ignore[import-untyped] 6 | 7 | from meshtastic.protobuf import portnums_pb2, remote_hardware_pb2 8 | from meshtastic.util import our_exit 9 | 10 | 11 | def onGPIOreceive(packet, interface) -> None: 12 | """Callback for received GPIO responses""" 13 | logging.debug(f"packet:{packet} interface:{interface}") 14 | gpioValue = 0 15 | hw = packet["decoded"]["remotehw"] 16 | if "gpioValue" in hw: 17 | gpioValue = hw["gpioValue"] 18 | else: 19 | if not "gpioMask" in hw: 20 | # we did get a reply, but due to protobufs, 0 for numeric value is not sent 21 | # see https://developers.google.com/protocol-buffers/docs/proto3#default 22 | # so, we set it here 23 | gpioValue = 0 24 | 25 | # print(f'mask:{interface.mask}') 26 | value = int(gpioValue) & int(interface.mask) 27 | print( 28 | f'Received RemoteHardware type={hw["type"]}, gpio_value={gpioValue} value={value}' 29 | ) 30 | interface.gotResponse = True 31 | 32 | 33 | class RemoteHardwareClient: 34 | """ 35 | This is the client code to control/monitor simple hardware built into the 36 | meshtastic devices. It is intended to be both a useful API/service and example 37 | code for how you can connect to your own custom meshtastic services 38 | """ 39 | 40 | def __init__(self, iface) -> None: 41 | """ 42 | Constructor 43 | 44 | iface is the already open MeshInterface instance 45 | """ 46 | self.iface = iface 47 | ch = iface.localNode.getChannelByName("gpio") 48 | if not ch: 49 | our_exit( 50 | "Warning: No channel named 'gpio' was found.\n" 51 | "On the sending and receive nodes create a channel named 'gpio'.\n" 52 | "For example, run '--ch-add gpio' on one device, then '--seturl' on\n" 53 | "the other devices using the url from the device where the channel was added." 54 | ) 55 | self.channelIndex = ch.index 56 | 57 | pub.subscribe(onGPIOreceive, "meshtastic.receive.remotehw") 58 | 59 | def _sendHardware(self, nodeid, r, wantResponse=False, onResponse=None): 60 | if not nodeid: 61 | our_exit( 62 | r"Warning: Must use a destination node ID for this operation (use --dest \!xxxxxxxxx)" 63 | ) 64 | return self.iface.sendData( 65 | r, 66 | nodeid, 67 | portnums_pb2.REMOTE_HARDWARE_APP, 68 | wantAck=True, 69 | channelIndex=self.channelIndex, 70 | wantResponse=wantResponse, 71 | onResponse=onResponse, 72 | ) 73 | 74 | def writeGPIOs(self, nodeid, mask, vals): 75 | """ 76 | Write the specified vals bits to the device GPIOs. Only bits in mask that 77 | are 1 will be changed 78 | """ 79 | logging.debug(f"writeGPIOs nodeid:{nodeid} mask:{mask} vals:{vals}") 80 | r = remote_hardware_pb2.HardwareMessage() 81 | r.type = remote_hardware_pb2.HardwareMessage.Type.WRITE_GPIOS 82 | r.gpio_mask = mask 83 | r.gpio_value = vals 84 | return self._sendHardware(nodeid, r) 85 | 86 | def readGPIOs(self, nodeid, mask, onResponse=None): 87 | """Read the specified bits from GPIO inputs on the device""" 88 | logging.debug(f"readGPIOs nodeid:{nodeid} mask:{mask}") 89 | r = remote_hardware_pb2.HardwareMessage() 90 | r.type = remote_hardware_pb2.HardwareMessage.Type.READ_GPIOS 91 | r.gpio_mask = mask 92 | return self._sendHardware(nodeid, r, wantResponse=True, onResponse=onResponse) 93 | 94 | def watchGPIOs(self, nodeid, mask): 95 | """Watch the specified bits from GPIO inputs on the device for changes""" 96 | logging.debug(f"watchGPIOs nodeid:{nodeid} mask:{mask}") 97 | r = remote_hardware_pb2.HardwareMessage() 98 | r.type = remote_hardware_pb2.HardwareMessage.Type.WATCH_GPIOS 99 | r.gpio_mask = mask 100 | self.iface.mask = mask 101 | return self._sendHardware(nodeid, r) 102 | -------------------------------------------------------------------------------- /meshtastic/serial_interface.py: -------------------------------------------------------------------------------- 1 | """ Serial interface class 2 | """ 3 | # pylint: disable=R0917 4 | import logging 5 | import platform 6 | import time 7 | 8 | from typing import List, Optional 9 | 10 | import serial # type: ignore[import-untyped] 11 | 12 | import meshtastic.util 13 | from meshtastic.stream_interface import StreamInterface 14 | 15 | if platform.system() != "Windows": 16 | import termios 17 | 18 | 19 | class SerialInterface(StreamInterface): 20 | """Interface class for meshtastic devices over a serial link""" 21 | 22 | def __init__(self, devPath: Optional[str]=None, debugOut=None, noProto: bool=False, connectNow: bool=True, noNodes: bool=False) -> None: 23 | """Constructor, opens a connection to a specified serial port, or if unspecified try to 24 | find one Meshtastic device by probing 25 | 26 | Keyword Arguments: 27 | devPath {string} -- A filepath to a device, i.e. /dev/ttyUSB0 (default: {None}) 28 | debugOut {stream} -- If a stream is provided, any debug serial output from the device will be emitted to that stream. (default: {None}) 29 | """ 30 | self.noProto = noProto 31 | 32 | self.devPath: Optional[str] = devPath 33 | 34 | if self.devPath is None: 35 | ports: List[str] = meshtastic.util.findPorts(True) 36 | logging.debug(f"ports:{ports}") 37 | if len(ports) == 0: 38 | print("No Serial Meshtastic device detected, attempting TCP connection on localhost.") 39 | return 40 | elif len(ports) > 1: 41 | message: str = "Warning: Multiple serial ports were detected so one serial port must be specified with the '--port'.\n" 42 | message += f" Ports detected:{ports}" 43 | meshtastic.util.our_exit(message) 44 | else: 45 | self.devPath = ports[0] 46 | 47 | logging.debug(f"Connecting to {self.devPath}") 48 | 49 | # first we need to set the HUPCL so the device will not reboot based on RTS and/or DTR 50 | # see https://github.com/pyserial/pyserial/issues/124 51 | if platform.system() != "Windows": 52 | with open(self.devPath, encoding="utf8") as f: 53 | attrs = termios.tcgetattr(f) 54 | attrs[2] = attrs[2] & ~termios.HUPCL 55 | termios.tcsetattr(f, termios.TCSAFLUSH, attrs) 56 | f.close() 57 | time.sleep(0.1) 58 | 59 | self.stream = serial.Serial( 60 | self.devPath, 115200, exclusive=True, timeout=0.5, write_timeout=0 61 | ) 62 | self.stream.flush() # type: ignore[attr-defined] 63 | time.sleep(0.1) 64 | 65 | StreamInterface.__init__( 66 | self, debugOut=debugOut, noProto=noProto, connectNow=connectNow, noNodes=noNodes 67 | ) 68 | 69 | def __repr__(self): 70 | rep = f"SerialInterface(devPath={self.devPath!r}" 71 | if hasattr(self, 'debugOut') and self.debugOut is not None: 72 | rep += f", debugOut={self.debugOut!r}" 73 | if self.noProto: 74 | rep += ", noProto=True" 75 | if hasattr(self, 'noNodes') and self.noNodes: 76 | rep += ", noNodes=True" 77 | rep += ")" 78 | return rep 79 | 80 | def close(self) -> None: 81 | """Close a connection to the device""" 82 | if self.stream: # Stream can be null if we were already closed 83 | self.stream.flush() # FIXME: why are there these two flushes with 100ms sleeps? This shouldn't be necessary 84 | time.sleep(0.1) 85 | self.stream.flush() 86 | time.sleep(0.1) 87 | logging.debug("Closing Serial stream") 88 | StreamInterface.close(self) 89 | -------------------------------------------------------------------------------- /meshtastic/slog/__init__.py: -------------------------------------------------------------------------------- 1 | """Structured logging framework (see dev docs for more info).""" 2 | 3 | from .slog import LogSet, root_dir 4 | -------------------------------------------------------------------------------- /meshtastic/slog/arrow.py: -------------------------------------------------------------------------------- 1 | """Utilities for Apache Arrow serialization.""" 2 | 3 | import logging 4 | import threading 5 | import os 6 | from typing import Optional, List 7 | 8 | import pyarrow as pa 9 | from pyarrow import feather 10 | 11 | chunk_size = 1000 # disk writes are batched based on this number of rows 12 | 13 | 14 | class ArrowWriter: 15 | """Writes an arrow file in a streaming fashion""" 16 | 17 | def __init__(self, file_name: str): 18 | """Create a new ArrowWriter object. 19 | 20 | file_name (str): The name of the file to write to. 21 | """ 22 | self.sink = pa.OSFile(file_name, "wb") # type: ignore 23 | self.new_rows: List[dict] = [] 24 | self.schema: Optional[pa.Schema] = None # haven't yet learned the schema 25 | self.writer: Optional[pa.RecordBatchStreamWriter] = None 26 | self._lock = threading.Condition() # Ensure only one thread writes at a time 27 | 28 | def close(self): 29 | """Close the stream and writes the file as needed.""" 30 | with self._lock: 31 | self._write() 32 | if self.writer: 33 | self.writer.close() 34 | self.sink.close() 35 | 36 | def set_schema(self, schema: pa.Schema): 37 | """Set the schema for the file. 38 | Only needed for datasets where we can't learn it from the first record written. 39 | 40 | schema (pa.Schema): The schema to use. 41 | """ 42 | with self._lock: 43 | assert self.schema is None 44 | self.schema = schema 45 | self.writer = pa.ipc.new_stream(self.sink, schema) 46 | 47 | def _write(self): 48 | """Write the new rows to the file.""" 49 | if len(self.new_rows) > 0: 50 | if self.schema is None: 51 | # only need to look at the first row to learn the schema 52 | self.set_schema(pa.Table.from_pylist([self.new_rows[0]]).schema) 53 | 54 | self.writer.write_batch( 55 | pa.RecordBatch.from_pylist(self.new_rows, schema=self.schema) 56 | ) 57 | self.new_rows = [] 58 | 59 | def add_row(self, row_dict: dict): 60 | """Add a row to the arrow file. 61 | We will automatically learn the schema from the first row. But all rows must use that schema. 62 | """ 63 | with self._lock: 64 | self.new_rows.append(row_dict) 65 | if len(self.new_rows) >= chunk_size: 66 | self._write() 67 | 68 | 69 | class FeatherWriter(ArrowWriter): 70 | """A smaller more interoperable version of arrow files. 71 | Uses a temporary .arrow file (which could be huge) but converts to a much smaller (but still fast) 72 | feather file. 73 | """ 74 | 75 | def __init__(self, file_name: str): 76 | super().__init__(file_name + ".arrow") 77 | self.base_file_name = file_name 78 | 79 | def close(self): 80 | super().close() 81 | src_name = self.base_file_name + ".arrow" 82 | dest_name = self.base_file_name + ".feather" 83 | if os.path.getsize(src_name) == 0: 84 | logging.warning(f"Discarding empty file: {src_name}") 85 | os.remove(src_name) 86 | else: 87 | logging.info(f"Compressing log data into {dest_name}") 88 | 89 | # note: must use open_stream, not open_file/read_table because the streaming layout is different 90 | # data = feather.read_table(src_name) 91 | with pa.memory_map(src_name) as source: 92 | array = pa.ipc.open_stream(source).read_all() 93 | 94 | # See https://stackoverflow.com/a/72406099 for more info and performance testing measurements 95 | feather.write_feather(array, dest_name, compression="zstd") 96 | os.remove(src_name) 97 | -------------------------------------------------------------------------------- /meshtastic/tcp_interface.py: -------------------------------------------------------------------------------- 1 | """TCPInterface class for interfacing with http endpoint 2 | """ 3 | # pylint: disable=R0917 4 | import contextlib 5 | import logging 6 | import socket 7 | import time 8 | from typing import Optional 9 | 10 | from meshtastic.stream_interface import StreamInterface 11 | 12 | DEFAULT_TCP_PORT = 4403 13 | 14 | class TCPInterface(StreamInterface): 15 | """Interface class for meshtastic devices over a TCP link""" 16 | 17 | def __init__( 18 | self, 19 | hostname: str, 20 | debugOut=None, 21 | noProto: bool=False, 22 | connectNow: bool=True, 23 | portNumber: int=DEFAULT_TCP_PORT, 24 | noNodes:bool=False, 25 | ): 26 | """Constructor, opens a connection to a specified IP address/hostname 27 | 28 | Keyword Arguments: 29 | hostname {string} -- Hostname/IP address of the device to connect to 30 | """ 31 | 32 | self.stream = None 33 | 34 | self.hostname: str = hostname 35 | self.portNumber: int = portNumber 36 | 37 | self.socket: Optional[socket.socket] = None 38 | 39 | if connectNow: 40 | self.myConnect() 41 | else: 42 | self.socket = None 43 | 44 | super().__init__(debugOut=debugOut, noProto=noProto, connectNow=connectNow, noNodes=noNodes) 45 | 46 | def __repr__(self): 47 | rep = f"TCPInterface({self.hostname!r}" 48 | if self.debugOut is not None: 49 | rep += f", debugOut={self.debugOut!r}" 50 | if self.noProto: 51 | rep += ", noProto=True" 52 | if self.socket is None: 53 | rep += ", connectNow=False" 54 | if self.portNumber != DEFAULT_TCP_PORT: 55 | rep += f", portNumber={self.portNumber!r}" 56 | if self.noNodes: 57 | rep += ", noNodes=True" 58 | rep += ")" 59 | return rep 60 | 61 | def _socket_shutdown(self) -> None: 62 | """Shutdown the socket. 63 | Note: Broke out this line so the exception could be unit tested. 64 | """ 65 | if self.socket is not None: 66 | self.socket.shutdown(socket.SHUT_RDWR) 67 | 68 | def myConnect(self) -> None: 69 | """Connect to socket""" 70 | logging.debug(f"Connecting to {self.hostname}") # type: ignore[str-bytes-safe] 71 | server_address = (self.hostname, self.portNumber) 72 | self.socket = socket.create_connection(server_address) 73 | 74 | def close(self) -> None: 75 | """Close a connection to the device""" 76 | logging.debug("Closing TCP stream") 77 | super().close() 78 | # Sometimes the socket read might be blocked in the reader thread. 79 | # Therefore we force the shutdown by closing the socket here 80 | self._wantExit = True 81 | if self.socket is not None: 82 | with contextlib.suppress(Exception): # Ignore errors in shutdown, because we might have a race with the server 83 | self._socket_shutdown() 84 | self.socket.close() 85 | 86 | self.socket = None 87 | 88 | def _writeBytes(self, b: bytes) -> None: 89 | """Write an array of bytes to our stream and flush""" 90 | if self.socket is not None: 91 | self.socket.send(b) 92 | 93 | def _readBytes(self, length) -> Optional[bytes]: 94 | """Read an array of bytes from our stream""" 95 | if self.socket is not None: 96 | data = self.socket.recv(length) 97 | # empty byte indicates a disconnected socket, 98 | # we need to handle it to avoid an infinite loop reading from null socket 99 | if data == b'': 100 | logging.debug("dead socket, re-connecting") 101 | # cleanup and reconnect socket without breaking reader thread 102 | with contextlib.suppress(Exception): 103 | self._socket_shutdown() 104 | self.socket.close() 105 | self.socket = None 106 | time.sleep(1) 107 | self.myConnect() 108 | self._startConfig() 109 | return None 110 | return data 111 | 112 | # no socket, break reader thread 113 | self._wantExit = True 114 | return None 115 | -------------------------------------------------------------------------------- /meshtastic/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meshtastic/python/622a435465872fd316054f1a4a7dd6d4d44bb3ec/meshtastic/tests/__init__.py -------------------------------------------------------------------------------- /meshtastic/tests/conftest.py: -------------------------------------------------------------------------------- 1 | """Common pytest code (place for fixtures).""" 2 | 3 | import argparse 4 | from unittest.mock import MagicMock 5 | 6 | import pytest 7 | 8 | from meshtastic import mt_config 9 | 10 | from ..mesh_interface import MeshInterface 11 | 12 | 13 | @pytest.fixture 14 | def reset_mt_config(): 15 | """Fixture to reset mt_config.""" 16 | parser = None 17 | parser = argparse.ArgumentParser(add_help=False) 18 | mt_config.reset() 19 | mt_config.parser = parser 20 | 21 | 22 | @pytest.fixture 23 | def iface_with_nodes(): 24 | """Fixture to setup some nodes.""" 25 | nodesById = { 26 | "!9388f81c": { 27 | "num": 2475227164, 28 | "user": { 29 | "id": "!9388f81c", 30 | "longName": "Unknown f81c", 31 | "shortName": "?1C", 32 | "macaddr": "RBeTiPgc", 33 | "hwModel": "TBEAM", 34 | }, 35 | "position": {}, 36 | "lastHeard": 1640204888, 37 | } 38 | } 39 | 40 | nodesByNum = { 41 | 2475227164: { 42 | "num": 2475227164, 43 | "user": { 44 | "id": "!9388f81c", 45 | "longName": "Unknown f81c", 46 | "shortName": "?1C", 47 | "macaddr": "RBeTiPgc", 48 | "hwModel": "TBEAM", 49 | }, 50 | "position": {"time": 1640206266}, 51 | "lastHeard": 1640206266, 52 | } 53 | } 54 | iface = MeshInterface(noProto=True) 55 | iface.nodes = nodesById 56 | iface.nodesByNum = nodesByNum 57 | myInfo = MagicMock() 58 | iface.myInfo = myInfo 59 | iface.myInfo.my_node_num = 2475227164 60 | return iface 61 | -------------------------------------------------------------------------------- /meshtastic/tests/slog-test-input/power.feather: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meshtastic/python/622a435465872fd316054f1a4a7dd6d4d44bb3ec/meshtastic/tests/slog-test-input/power.feather -------------------------------------------------------------------------------- /meshtastic/tests/slog-test-input/slog.feather: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meshtastic/python/622a435465872fd316054f1a4a7dd6d4d44bb3ec/meshtastic/tests/slog-test-input/slog.feather -------------------------------------------------------------------------------- /meshtastic/tests/test_analysis.py: -------------------------------------------------------------------------------- 1 | """Test analysis processing.""" 2 | 3 | import logging 4 | import os 5 | import sys 6 | 7 | import pytest 8 | 9 | try: 10 | # Depends upon matplotlib & other packages in poetry's analysis group, not installed by default 11 | from meshtastic.analysis.__main__ import main 12 | except ImportError: 13 | pytest.skip("Can't import meshtastic.analysis", allow_module_level=True) 14 | 15 | 16 | @pytest.mark.unit 17 | def test_analysis(caplog): 18 | """Test analysis processing""" 19 | 20 | cur_dir = os.path.dirname(os.path.abspath(__file__)) 21 | slog_input_dir = os.path.join(cur_dir, "slog-test-input") 22 | 23 | sys.argv = ["fakescriptname", "--no-server", "--slog", slog_input_dir] 24 | 25 | with caplog.at_level(logging.DEBUG): 26 | logging.getLogger().propagate = True # Let our testing framework see our logs 27 | main() 28 | 29 | assert "Exiting without running visualization server" in caplog.text 30 | -------------------------------------------------------------------------------- /meshtastic/tests/test_examples.py: -------------------------------------------------------------------------------- 1 | """Meshtastic test that the examples run as expected. 2 | We assume you have a python virtual environment in current directory. 3 | If not, you need to run: "python3 -m venv venv", "source venv/bin/activate", "pip install ." 4 | """ 5 | import subprocess 6 | 7 | import pytest 8 | 9 | 10 | @pytest.mark.examples 11 | def test_examples_hello_world_serial_no_arg(): 12 | """Test hello_world_serial without any args""" 13 | return_value, _ = subprocess.getstatusoutput( 14 | "source venv/bin/activate; python3 examples/hello_world_serial.py" 15 | ) 16 | assert return_value == 3 17 | 18 | 19 | @pytest.mark.examples 20 | def test_examples_hello_world_serial_with_arg(capsys): 21 | """Test hello_world_serial with arg""" 22 | return_value, _ = subprocess.getstatusoutput( 23 | "source venv/bin/activate; python3 examples/hello_world_serial.py hello" 24 | ) 25 | assert return_value == 1 26 | _, err = capsys.readouterr() 27 | assert err == "" 28 | # TODO: Why does this not work? 29 | # assert out == 'Warning: No Meshtastic devices detected.' 30 | -------------------------------------------------------------------------------- /meshtastic/tests/test_init.py: -------------------------------------------------------------------------------- 1 | """Meshtastic unit tests for __init__.py""" 2 | 3 | import logging 4 | import re 5 | from unittest.mock import MagicMock 6 | 7 | import pytest 8 | 9 | from meshtastic import _onNodeInfoReceive, _onPositionReceive, _onTextReceive, mt_config 10 | 11 | from ..serial_interface import SerialInterface 12 | 13 | 14 | @pytest.mark.unit 15 | def test_init_onTextReceive_with_exception(caplog): 16 | """Test _onTextReceive""" 17 | args = MagicMock() 18 | mt_config.args = args 19 | iface = MagicMock(autospec=SerialInterface) 20 | packet = {} 21 | with caplog.at_level(logging.DEBUG): 22 | _onTextReceive(iface, packet) 23 | assert re.search(r"in _onTextReceive", caplog.text, re.MULTILINE) 24 | assert re.search(r"Malformatted", caplog.text, re.MULTILINE) 25 | 26 | 27 | @pytest.mark.unit 28 | def test_init_onPositionReceive(caplog): 29 | """Test _onPositionReceive""" 30 | args = MagicMock() 31 | mt_config.args = args 32 | iface = MagicMock(autospec=SerialInterface) 33 | packet = {"from": "foo", "decoded": {"position": {}}} 34 | with caplog.at_level(logging.DEBUG): 35 | _onPositionReceive(iface, packet) 36 | assert re.search(r"in _onPositionReceive", caplog.text, re.MULTILINE) 37 | 38 | 39 | @pytest.mark.unit 40 | def test_init_onNodeInfoReceive(caplog, iface_with_nodes): 41 | """Test _onNodeInfoReceive""" 42 | args = MagicMock() 43 | mt_config.args = args 44 | iface = iface_with_nodes 45 | iface.myInfo.my_node_num = 2475227164 46 | packet = { 47 | "from": 4808675309, 48 | "decoded": { 49 | "user": { 50 | "id": "bar", 51 | }, 52 | }, 53 | } 54 | with caplog.at_level(logging.DEBUG): 55 | _onNodeInfoReceive(iface, packet) 56 | assert re.search(r"in _onNodeInfoReceive", caplog.text, re.MULTILINE) 57 | -------------------------------------------------------------------------------- /meshtastic/tests/test_int.py: -------------------------------------------------------------------------------- 1 | """Meshtastic integration tests""" 2 | import re 3 | import subprocess 4 | 5 | import pytest 6 | 7 | 8 | @pytest.mark.int 9 | def test_int_meshtastic_no_args(): 10 | """Test meshtastic without any args""" 11 | return_value, out = subprocess.getstatusoutput("meshtastic") 12 | assert re.match(r"usage: meshtastic", out) 13 | assert return_value == 1 14 | 15 | 16 | @pytest.mark.int 17 | def test_int_mesh_tunnel_no_args(): 18 | """Test mesh-tunnel without any args""" 19 | return_value, out = subprocess.getstatusoutput("mesh-tunnel") 20 | assert re.match(r"usage: mesh-tunnel", out) 21 | assert return_value == 1 22 | 23 | 24 | @pytest.mark.int 25 | def test_int_version(): 26 | """Test '--version'.""" 27 | return_value, out = subprocess.getstatusoutput("meshtastic --version") 28 | assert re.match(r"[0-9]+\.[0-9]+\.[0-9]", out) 29 | assert return_value == 0 30 | 31 | 32 | @pytest.mark.int 33 | def test_int_help(): 34 | """Test '--help'.""" 35 | return_value, out = subprocess.getstatusoutput("meshtastic --help") 36 | assert re.match(r"usage: meshtastic ", out) 37 | assert return_value == 0 38 | 39 | 40 | @pytest.mark.int 41 | def test_int_support(): 42 | """Test '--support'.""" 43 | return_value, out = subprocess.getstatusoutput("meshtastic --support") 44 | assert re.search(r"System", out) 45 | assert re.search(r"Python", out) 46 | assert return_value == 0 47 | -------------------------------------------------------------------------------- /meshtastic/tests/test_remote_hardware.py: -------------------------------------------------------------------------------- 1 | """Meshtastic unit tests for remote_hardware.py""" 2 | 3 | import logging 4 | import re 5 | from unittest.mock import MagicMock, patch 6 | 7 | import pytest 8 | 9 | from ..remote_hardware import RemoteHardwareClient, onGPIOreceive 10 | from ..serial_interface import SerialInterface 11 | 12 | 13 | @pytest.mark.unit 14 | def test_RemoteHardwareClient(): 15 | """Test that we can instantiate a RemoteHardwareClient instance""" 16 | iface = MagicMock(autospec=SerialInterface) 17 | rhw = RemoteHardwareClient(iface) 18 | assert rhw.iface == iface 19 | iface.close() 20 | 21 | 22 | @pytest.mark.unit 23 | def test_onGPIOreceive(capsys): 24 | """Test onGPIOreceive""" 25 | iface = MagicMock(autospec=SerialInterface) 26 | packet = {"decoded": {"remotehw": {"type": "foo", "gpioValue": "4096"}}} 27 | onGPIOreceive(packet, iface) 28 | out, err = capsys.readouterr() 29 | assert re.search(r"Received RemoteHardware", out) 30 | assert err == "" 31 | 32 | 33 | @pytest.mark.unit 34 | def test_RemoteHardwareClient_no_gpio_channel(capsys): 35 | """Test that we can instantiate a RemoteHardwareClient instance but there is no channel named channel 'gpio'""" 36 | iface = MagicMock(autospec=SerialInterface) 37 | with patch("meshtastic.serial_interface.SerialInterface", return_value=iface) as mo: 38 | mo.localNode.getChannelByName.return_value = None 39 | with pytest.raises(SystemExit) as pytest_wrapped_e: 40 | RemoteHardwareClient(mo) 41 | assert pytest_wrapped_e.type == SystemExit 42 | assert pytest_wrapped_e.value.code == 1 43 | out, err = capsys.readouterr() 44 | assert re.search(r"Warning: No channel named", out) 45 | assert err == "" 46 | 47 | 48 | @pytest.mark.unit 49 | def test_readGPIOs(caplog): 50 | """Test readGPIOs""" 51 | iface = MagicMock(autospec=SerialInterface) 52 | rhw = RemoteHardwareClient(iface) 53 | with caplog.at_level(logging.DEBUG): 54 | rhw.readGPIOs("0x10", 123) 55 | assert re.search(r"readGPIOs", caplog.text, re.MULTILINE) 56 | iface.close() 57 | 58 | 59 | @pytest.mark.unit 60 | def test_writeGPIOs(caplog): 61 | """Test writeGPIOs""" 62 | iface = MagicMock(autospec=SerialInterface) 63 | rhw = RemoteHardwareClient(iface) 64 | with caplog.at_level(logging.DEBUG): 65 | rhw.writeGPIOs("0x10", 123, 1) 66 | assert re.search(r"writeGPIOs", caplog.text, re.MULTILINE) 67 | iface.close() 68 | 69 | 70 | @pytest.mark.unit 71 | def test_watchGPIOs(caplog): 72 | """Test watchGPIOs""" 73 | iface = MagicMock(autospec=SerialInterface) 74 | rhw = RemoteHardwareClient(iface) 75 | with caplog.at_level(logging.DEBUG): 76 | rhw.watchGPIOs("0x10", 123) 77 | assert re.search(r"watchGPIOs", caplog.text, re.MULTILINE) 78 | iface.close() 79 | 80 | 81 | @pytest.mark.unit 82 | def test_sendHardware_no_nodeid(capsys): 83 | """Test sending no nodeid to _sendHardware()""" 84 | iface = MagicMock(autospec=SerialInterface) 85 | with patch("meshtastic.serial_interface.SerialInterface", return_value=iface) as mo: 86 | with pytest.raises(SystemExit) as pytest_wrapped_e: 87 | rhw = RemoteHardwareClient(mo) 88 | rhw._sendHardware(None, None) 89 | assert pytest_wrapped_e.type == SystemExit 90 | out, err = capsys.readouterr() 91 | assert re.search(r"Warning: Must use a destination node ID", out) 92 | assert err == "" 93 | -------------------------------------------------------------------------------- /meshtastic/tests/test_serial_interface.py: -------------------------------------------------------------------------------- 1 | """Meshtastic unit tests for serial_interface.py""" 2 | # pylint: disable=R0917 3 | 4 | import re 5 | from unittest.mock import mock_open, patch 6 | 7 | import pytest 8 | 9 | from ..serial_interface import SerialInterface 10 | from ..protobuf import config_pb2 11 | 12 | 13 | @pytest.mark.unit 14 | @patch("time.sleep") 15 | @patch("termios.tcsetattr") 16 | @patch("termios.tcgetattr") 17 | @patch("builtins.open", new_callable=mock_open, read_data="data") 18 | @patch("serial.Serial") 19 | @patch("meshtastic.util.findPorts", return_value=["/dev/ttyUSBfake"]) 20 | def test_SerialInterface_single_port( 21 | mocked_findPorts, mocked_serial, mocked_open, mock_get, mock_set, mock_sleep, capsys 22 | ): 23 | """Test that we can instantiate a SerialInterface with a single port""" 24 | iface = SerialInterface(noProto=True) 25 | iface.localNode.localConfig.lora.CopyFrom(config_pb2.Config.LoRaConfig()) 26 | iface.showInfo() 27 | iface.localNode.showInfo() 28 | iface.close() 29 | mocked_findPorts.assert_called() 30 | mocked_serial.assert_called() 31 | mocked_open.assert_called() 32 | mock_get.assert_called() 33 | mock_set.assert_called() 34 | mock_sleep.assert_called() 35 | out, err = capsys.readouterr() 36 | assert re.search(r"Nodes in mesh", out, re.MULTILINE) 37 | assert re.search(r"Preferences", out, re.MULTILINE) 38 | assert re.search(r"Channels", out, re.MULTILINE) 39 | assert re.search(r"Primary channel", out, re.MULTILINE) 40 | assert err == "" 41 | 42 | 43 | @pytest.mark.unit 44 | @patch("meshtastic.util.findPorts", return_value=[]) 45 | def test_SerialInterface_no_ports(mocked_findPorts, capsys): 46 | """Test that we can instantiate a SerialInterface with no ports""" 47 | serialInterface = SerialInterface(noProto=True) 48 | mocked_findPorts.assert_called() 49 | assert serialInterface.devPath is None 50 | out, err = capsys.readouterr() 51 | assert re.search(r"No.*Meshtastic.*device.*detected", out, re.MULTILINE) 52 | assert err == "" 53 | 54 | 55 | @pytest.mark.unit 56 | @patch( 57 | "meshtastic.util.findPorts", return_value=["/dev/ttyUSBfake1", "/dev/ttyUSBfake2"] 58 | ) 59 | def test_SerialInterface_multiple_ports(mocked_findPorts, capsys): 60 | """Test that we can instantiate a SerialInterface with two ports""" 61 | with pytest.raises(SystemExit) as pytest_wrapped_e: 62 | SerialInterface(noProto=True) 63 | mocked_findPorts.assert_called() 64 | assert pytest_wrapped_e.type == SystemExit 65 | assert pytest_wrapped_e.value.code == 1 66 | out, err = capsys.readouterr() 67 | assert re.search(r"Warning: Multiple serial ports were detected", out, re.MULTILINE) 68 | assert err == "" 69 | -------------------------------------------------------------------------------- /meshtastic/tests/test_smoke2.py: -------------------------------------------------------------------------------- 1 | """Meshtastic smoke tests with 2 devices connected via USB""" 2 | import re 3 | import subprocess 4 | 5 | import pytest 6 | 7 | 8 | @pytest.mark.smoke2 9 | def test_smoke2_info(): 10 | """Test --info with 2 devices connected serially""" 11 | return_value, out = subprocess.getstatusoutput("meshtastic --info") 12 | assert re.search(r"Warning: Multiple", out, re.MULTILINE) 13 | assert return_value == 1 14 | 15 | 16 | @pytest.mark.smoke2 17 | def test_smoke2_test(): 18 | """Test --test""" 19 | return_value, out = subprocess.getstatusoutput("meshtastic --test") 20 | assert re.search(r"Writing serial debugging", out, re.MULTILINE) 21 | assert re.search(r"Ports opened", out, re.MULTILINE) 22 | assert re.search(r"Running 5 tests", out, re.MULTILINE) 23 | assert return_value == 0 24 | -------------------------------------------------------------------------------- /meshtastic/tests/test_smoke_wifi.py: -------------------------------------------------------------------------------- 1 | """Meshtastic smoke tests a device setup with wifi. 2 | 3 | Need to have run the following on an esp32 device: 4 | meshtastic --set wifi_ssid 'foo' --set wifi_password 'sekret' 5 | """ 6 | import re 7 | import subprocess 8 | 9 | import pytest 10 | 11 | 12 | @pytest.mark.smokewifi 13 | def test_smokewifi_info(): 14 | """Test --info""" 15 | return_value, out = subprocess.getstatusoutput( 16 | "meshtastic --info --host meshtastic.local" 17 | ) 18 | assert re.search(r"^Owner", out, re.MULTILINE) 19 | assert re.search(r"^My info", out, re.MULTILINE) 20 | assert re.search(r"^Nodes in mesh", out, re.MULTILINE) 21 | assert re.search(r"^Preferences", out, re.MULTILINE) 22 | assert re.search(r"^Channels", out, re.MULTILINE) 23 | assert re.search(r"^ PRIMARY", out, re.MULTILINE) 24 | assert re.search(r"^Primary channel URL", out, re.MULTILINE) 25 | assert return_value == 0 26 | -------------------------------------------------------------------------------- /meshtastic/tests/test_stream_interface.py: -------------------------------------------------------------------------------- 1 | """Meshtastic unit tests for stream_interface.py""" 2 | 3 | import logging 4 | from unittest.mock import MagicMock 5 | 6 | import pytest 7 | 8 | from ..stream_interface import StreamInterface 9 | 10 | # import re 11 | 12 | 13 | @pytest.mark.unit 14 | def test_StreamInterface(): 15 | """Test that we cannot instantiate a StreamInterface based on noProto""" 16 | with pytest.raises(Exception) as pytest_wrapped_e: 17 | StreamInterface() 18 | assert pytest_wrapped_e.type == Exception 19 | 20 | 21 | # Note: This takes a bit, so moving from unit to slow 22 | @pytest.mark.unitslow 23 | @pytest.mark.usefixtures("reset_mt_config") 24 | def test_StreamInterface_with_noProto(caplog): 25 | """Test that we can instantiate a StreamInterface based on nonProto 26 | and we can read/write bytes from a mocked stream 27 | """ 28 | stream = MagicMock() 29 | test_data = b"hello" 30 | stream.read.return_value = test_data 31 | with caplog.at_level(logging.DEBUG): 32 | iface = StreamInterface(noProto=True, connectNow=False) 33 | iface.stream = stream 34 | iface._writeBytes(test_data) 35 | data = iface._readBytes(len(test_data)) 36 | assert data == test_data 37 | 38 | 39 | # TODO 40 | ### Note: This takes a bit, so moving from unit to slow 41 | ### Tip: If you want to see the print output, run with '-s' flag: 42 | ### pytest -s meshtastic/tests/test_stream_interface.py::test_sendToRadioImpl 43 | # @pytest.mark.unitslow 44 | # @pytest.mark.usefixtures("reset_mt_config") 45 | # def test_sendToRadioImpl(caplog): 46 | # """Test _sendToRadioImpl()""" 47 | # 48 | ## def add_header(b): 49 | ## """Add header stuffs for radio""" 50 | ## bufLen = len(b) 51 | ## header = bytes([START1, START2, (bufLen >> 8) & 0xff, bufLen & 0xff]) 52 | ## return header + b 53 | # 54 | # # captured raw bytes of a Heltec2.1 radio with 2 channels (primary and a secondary channel named "gpio") 55 | # 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' 56 | # 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' 57 | # # pylint: disable=C0301 58 | # 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@' 59 | # raw_4_complete = b'@\xcf\xe5\xd1\x8c\x0e' 60 | # # pylint: disable=C0301 61 | # 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' 62 | # # pylint: disable=C0301 63 | # 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' 64 | # # pylint: disable=C0301 65 | # 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' 66 | # 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' 67 | # raw_blank = b'' 68 | # 69 | # test_data = b'hello' 70 | # stream = MagicMock() 71 | # #stream.read.return_value = add_header(test_data) 72 | # stream.read.side_effect = [ raw_1_my_info, raw_2_node_info, raw_3_node_info, raw_4_complete, 73 | # raw_5_prefs, raw_6_channel0, raw_7_channel1, raw_8_channel2, 74 | # raw_blank, raw_blank] 75 | # toRadio = MagicMock() 76 | # toRadio.SerializeToString.return_value = test_data 77 | # with caplog.at_level(logging.DEBUG): 78 | # iface = StreamInterface(noProto=True, connectNow=False) 79 | # iface.stream = stream 80 | # iface.connect() 81 | # iface._sendToRadioImpl(toRadio) 82 | # assert re.search(r'Sending: ', caplog.text, re.MULTILINE) 83 | # assert re.search(r'reading character', caplog.text, re.MULTILINE) 84 | # assert re.search(r'In reader loop', caplog.text, re.MULTILINE) 85 | -------------------------------------------------------------------------------- /meshtastic/tests/test_tcp_interface.py: -------------------------------------------------------------------------------- 1 | """Meshtastic unit tests for tcp_interface.py""" 2 | 3 | import re 4 | from unittest.mock import patch 5 | 6 | import pytest 7 | 8 | from ..protobuf import config_pb2 9 | from ..tcp_interface import TCPInterface 10 | 11 | 12 | @pytest.mark.unit 13 | def test_TCPInterface(capsys): 14 | """Test that we can instantiate a TCPInterface""" 15 | with patch("socket.socket") as mock_socket: 16 | iface = TCPInterface(hostname="localhost", noProto=True) 17 | iface.localNode.localConfig.lora.CopyFrom(config_pb2.Config.LoRaConfig()) 18 | iface.myConnect() 19 | iface.showInfo() 20 | iface.localNode.showInfo() 21 | out, err = capsys.readouterr() 22 | assert re.search(r"Owner: None \(None\)", out, re.MULTILINE) 23 | assert re.search(r"Nodes", out, re.MULTILINE) 24 | assert re.search(r"Preferences", out, re.MULTILINE) 25 | assert re.search(r"Channels", out, re.MULTILINE) 26 | assert re.search(r"Primary channel URL", out, re.MULTILINE) 27 | assert err == "" 28 | assert mock_socket.called 29 | iface.close() 30 | 31 | 32 | @pytest.mark.unit 33 | def test_TCPInterface_exception(): 34 | """Test that we can instantiate a TCPInterface""" 35 | 36 | def throw_an_exception(): 37 | raise ValueError("Fake exception.") 38 | 39 | with patch( 40 | "meshtastic.tcp_interface.TCPInterface._socket_shutdown" 41 | ) as mock_shutdown: 42 | mock_shutdown.side_effect = throw_an_exception 43 | with patch("socket.socket") as mock_socket: 44 | iface = TCPInterface(hostname="localhost", noProto=True) 45 | iface.myConnect() 46 | iface.close() 47 | assert mock_socket.called 48 | assert mock_shutdown.called 49 | 50 | 51 | @pytest.mark.unit 52 | def test_TCPInterface_without_connecting(): 53 | """Test that we can instantiate a TCPInterface with connectNow as false""" 54 | with patch("socket.socket"): 55 | iface = TCPInterface(hostname="localhost", noProto=True, connectNow=False) 56 | assert iface.socket is None 57 | -------------------------------------------------------------------------------- /meshtastic/version.py: -------------------------------------------------------------------------------- 1 | """Version lookup utilities, isolated for cleanliness""" 2 | import sys 3 | try: 4 | from importlib.metadata import version 5 | except: 6 | import pkg_resources 7 | 8 | def get_active_version(): 9 | """Get the currently active version using importlib, or pkg_resources if we must""" 10 | if "importlib.metadata" in sys.modules: 11 | return version("meshtastic") 12 | else: 13 | return pkg_resources.get_distribution("meshtastic").version # pylint: disable=E0601 14 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "meshtastic" 3 | version = "2.6.3" 4 | description = "Python API & client shell for talking to Meshtastic devices" 5 | authors = ["Meshtastic Developers "] 6 | license = "GPL-3.0-only" 7 | readme = "README.md" 8 | 9 | [tool.poetry.dependencies] 10 | python = "^3.9,<3.14" # 3.9 is needed for pandas, bleak requires <3.14 11 | pyserial = "^3.5" 12 | protobuf = ">=4.21.12" 13 | tabulate = "^0.9.0" 14 | requests = "^2.31.0" 15 | pyyaml = "^6.0.1" 16 | pypubsub = "^4.0.3" 17 | bleak = "^0.22.3" 18 | packaging = "^24.0" 19 | argcomplete = { version = "^3.5.2", optional = true } 20 | pyqrcode = { version = "^1.2.1", optional = true } 21 | dotmap = { version = "^1.3.30", optional = true } 22 | print-color = { version = "^0.4.6", optional = true } 23 | dash = { version = "^2.17.1", optional = true } 24 | pytap2 = { version = "^2.3.0", optional = true } 25 | dash-bootstrap-components = { version = "^1.6.0", optional = true } 26 | pandas = { version = "^2.2.2", optional = true } 27 | pandas-stubs = { version = "^2.2.2.240603", optional = true } 28 | wcwidth = {version = "^0.2.13", optional = true} 29 | 30 | [tool.poetry.group.dev.dependencies] 31 | hypothesis = "^6.103.2" 32 | pytest = "^8.2.2" 33 | pytest-cov = "^5.0.0" 34 | pdoc3 = "^0.10.0" 35 | autopep8 = "^2.1.0" 36 | pylint = "^3.2.3" 37 | pyinstaller = "^6.8.0" 38 | mypy = "^1.10.0" 39 | mypy-protobuf = "^3.3.0" 40 | types-protobuf = "^5.26.0.20240422" 41 | types-tabulate = "^0.9.0.20240106" 42 | types-requests = "^2.31.0.20240406" 43 | types-setuptools = "^69.5.0.20240423" 44 | types-pyyaml = "^6.0.12.20240311" 45 | pyarrow-stubs = "^10.0.1.7" 46 | 47 | [tool.poetry.group.powermon] 48 | optional = true 49 | 50 | [tool.poetry.group.powermon.dependencies] 51 | riden = { git = "https://github.com/geeksville/riden.git#1.2.1" } 52 | ppk2-api = "^0.9.2" 53 | parse = "^1.20.2" 54 | pyarrow = "^16.1.0" 55 | platformdirs = "^4.2.2" 56 | 57 | # If you are doing power analysis you might want these extra devtools 58 | [tool.poetry.group.analysis] 59 | optional = true 60 | 61 | [tool.poetry.group.analysis.dependencies] 62 | jupyterlab = "^4.2.2" 63 | matplotlib = "^3.9.0" 64 | ipympl = "^0.9.4" 65 | ipywidgets = "^8.1.3" 66 | jupyterlab-widgets = "^3.0.11" 67 | 68 | [tool.poetry.extras] 69 | cli = ["pyqrcode", "print-color", "dotmap", "argcomplete", "wcwidth"] 70 | tunnel = ["pytap2"] 71 | analysis = ["dash", "dash-bootstrap-components", "pandas", "pandas-stubs"] 72 | 73 | [tool.poetry.scripts] 74 | meshtastic = "meshtastic.__main__:main" 75 | mesh-tunnel = "meshtastic.__main__:tunnelMain [tunnel]" 76 | mesh-analysis = "meshtastic.analysis.__main__:main [analysis]" 77 | 78 | # "Poe the poet" (optional) provides an easy way of running non python tools inside the poetry virtualenv 79 | # if you would like to use it run "pipx install poe" 80 | # then you can do stuff like "poe code" to run vscode inside this environment 81 | [tool.poe.tasks] 82 | code = "code ." 83 | juypter = "poetry run jupyter lab" 84 | 85 | [build-system] 86 | requires = ["poetry-core"] 87 | build-backend = "poetry.core.masonry.api" 88 | -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | 3 | addopts = -m "not int and not smoke1 and not smoke2 and not smokewifi and not examples and not smokevirt" 4 | 5 | filterwarnings = 6 | ignore::DeprecationWarning 7 | 8 | markers = 9 | unit: marks tests as unit tests 10 | unitslow: marks slow unit tests 11 | int: marks tests as integration tests 12 | smokevirt: marks tests as smoke tests against virtual device 13 | smoke1: runs smoke tests on a single device connected via USB 14 | smoke2: runs smoke tests on a two devices connected via USB 15 | smokewifi: runs smoke test on an esp32 device setup with wifi 16 | examples: runs the examples tests which validates the library 17 | -------------------------------------------------------------------------------- /standalone_readme.txt: -------------------------------------------------------------------------------- 1 | readme.txt for single standalone executable zip files that can be 2 | downloaded from https://github.com/meshtastic/python/releases 3 | 4 | If you do not want to install python and/or the python libraries, you can download one of these 5 | files to run the Meshtastic command line interface (CLI) as a standalone executable. 6 | 7 | See https://meshtastic.org/docs/software/python/cli/installation/#standalone-installation-ubuntu-only for more info. 8 | -------------------------------------------------------------------------------- /tests/close-bug.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import logging 3 | import sys 4 | 5 | from pubsub import pub 6 | 7 | import meshtastic 8 | 9 | # logging.basicConfig(level=logging.DEBUG) 10 | print(str(datetime.datetime.now()) + ": start") 11 | interface = meshtastic.TCPInterface(sys.argv[1]) 12 | print(str(datetime.datetime.now()) + ": middle") 13 | interface.close() 14 | print(str(datetime.datetime.now()) + ": after close") 15 | -------------------------------------------------------------------------------- /tests/hello_world.py: -------------------------------------------------------------------------------- 1 | import meshtastic.serial_interface 2 | 3 | interface = ( 4 | meshtastic.serial_interface.SerialInterface() 5 | ) # By default will try to find a meshtastic device, otherwise provide a device path like /dev/ttyUSB0 6 | interface.sendText("hello mesh") 7 | interface.close() 8 | -------------------------------------------------------------------------------- /tests/tcp-test.py: -------------------------------------------------------------------------------- 1 | # reported by @ScriptBlock 2 | 3 | import sys 4 | 5 | from pubsub import pub 6 | 7 | import meshtastic 8 | 9 | 10 | def onConnection( 11 | interface, topic=pub.AUTO_TOPIC 12 | ): # called when we (re)connect to the radio 13 | print(interface.myInfo) 14 | interface.close() 15 | 16 | 17 | pub.subscribe(onConnection, "meshtastic.connection.established") 18 | interface = meshtastic.TCPInterface(sys.argv[1]) 19 | -------------------------------------------------------------------------------- /tests/tuntest.py: -------------------------------------------------------------------------------- 1 | # delete me eventually 2 | # Note python-pytuntap was too buggy 3 | # using pip3 install pytap2 4 | # make sure to "sudo setcap cap_net_admin+eip /usr/bin/python3.8" so python can access tun device without being root 5 | # sudo ip tuntap del mode tun tun0 6 | 7 | # FIXME: set MTU correctly 8 | # select local ip address based on nodeid 9 | # print known node ids as IP addresses 10 | 11 | import logging 12 | from _thread import start_new_thread 13 | 14 | from pytap2 import TapDevice 15 | 16 | """A list of chatty UDP services we should never accidentally 17 | forward to our slow network""" 18 | udpBlacklist = { 19 | 1900, # SSDP 20 | 5353, # multicast DNS 21 | } 22 | 23 | """A list of TCP services to block""" 24 | tcpBlacklist = {} 25 | 26 | """A list of protocols we ignore""" 27 | protocolBlacklist = { 28 | 0x02, # IGMP 29 | 0x80, # Service-Specific Connection-Oriented Protocol in a Multilink and Connectionless Environment 30 | } 31 | 32 | 33 | def hexstr(barray): 34 | """Print a string of hex digits""" 35 | return ":".join("{:02x}".format(x) for x in barray) 36 | 37 | 38 | def ipstr(barray): 39 | """Print a string of ip digits""" 40 | return ".".join("{}".format(x) for x in barray) 41 | 42 | 43 | def readnet_u16(p, offset): 44 | """Read big endian u16 (network byte order)""" 45 | return p[offset] * 256 + p[offset + 1] 46 | 47 | 48 | def readtest(tap): 49 | while True: 50 | p = tap.read() 51 | 52 | protocol = p[8 + 1] 53 | srcaddr = p[12:16] 54 | destaddr = p[16:20] 55 | subheader = 20 56 | ignore = False # Assume we will be forwarding the packet 57 | if protocol in protocolBlacklist: 58 | ignore = True 59 | logging.debug(f"Ignoring blacklisted protocol 0x{protocol:02x}") 60 | elif protocol == 0x01: # ICMP 61 | logging.warn("Generating fake ping reply") 62 | # reply to pings (swap src and dest but keep rest of packet unchanged) 63 | pingback = p[:12] + p[16:20] + p[12:16] + p[20:] 64 | tap.write(pingback) 65 | elif protocol == 0x11: # UDP 66 | srcport = readnet_u16(p, subheader) 67 | destport = readnet_u16(p, subheader + 2) 68 | logging.debug(f"udp srcport={srcport}, destport={destport}") 69 | if destport in udpBlacklist: 70 | ignore = True 71 | logging.debug(f"ignoring blacklisted UDP port {destport}") 72 | elif protocol == 0x06: # TCP 73 | srcport = readnet_u16(p, subheader) 74 | destport = readnet_u16(p, subheader + 2) 75 | logging.debug(f"tcp srcport={srcport}, destport={destport}") 76 | if destport in tcpBlacklist: 77 | ignore = True 78 | logging.debug(f"ignoring blacklisted TCP port {destport}") 79 | else: 80 | logging.warning( 81 | f"unexpected protocol 0x{protocol:02x}, src={ipstr(srcaddr)}, dest={ipstr(destaddr)}" 82 | ) 83 | 84 | if not ignore: 85 | logging.debug( 86 | f"Forwarding packet bytelen={len(p)} src={ipstr(srcaddr)}, dest={ipstr(destaddr)}" 87 | ) 88 | 89 | 90 | logging.basicConfig(level=logging.DEBUG) 91 | 92 | tun = TapDevice(mtu=200) 93 | # tun.create() 94 | tun.up() 95 | tun.ifconfig(address="10.115.1.2", netmask="255.255.0.0") 96 | 97 | start_new_thread(readtest, (tun,)) 98 | input("press return key to quit!") 99 | tun.close() 100 | -------------------------------------------------------------------------------- /vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "github": { 3 | "silent": true 4 | } 5 | } 6 | --------------------------------------------------------------------------------