mirror of
https://github.com/meshtastic/python.git
synced 2026-08-01 17:47:24 -04:00
Compare commits
8 Commits
master
...
backup-com
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7422d9d314 | ||
|
|
5ee0264737 | ||
|
|
ec13bd308a | ||
|
|
2d88d8e918 | ||
|
|
fb48fc20b3 | ||
|
|
336e5154a3 | ||
|
|
3008e039fb | ||
|
|
4bf61d4c98 |
@@ -1,16 +0,0 @@
|
|||||||
language: en-US
|
|
||||||
reviews:
|
|
||||||
profile: chill
|
|
||||||
high_level_summary: true
|
|
||||||
poem: false
|
|
||||||
auto_review:
|
|
||||||
enabled: true
|
|
||||||
drafts: false
|
|
||||||
ignore_usernames:
|
|
||||||
- renovate
|
|
||||||
- renovate[bot]
|
|
||||||
abort_on_close: true
|
|
||||||
|
|
||||||
path_filters:
|
|
||||||
- "!/protobufs/**"
|
|
||||||
- "!/meshtastic/protobuf/**"
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
*
|
|
||||||
!Containerfile*
|
|
||||||
!Dockerfile
|
|
||||||
!README.md
|
|
||||||
!LICENSE.md
|
|
||||||
!bin/container-entrypoint.sh
|
|
||||||
!examples/
|
|
||||||
!extra/
|
|
||||||
!meshtastic/
|
|
||||||
!poetry.lock
|
|
||||||
!pyproject.toml
|
|
||||||
22
.github/CONTRIBUTING.md
vendored
22
.github/CONTRIBUTING.md
vendored
@@ -1,22 +0,0 @@
|
|||||||
# Contributing to Meshtastic Python
|
|
||||||
|
|
||||||
## Development resources
|
|
||||||
- [API Documentation](https://python.meshtastic.org/)
|
|
||||||
- [Meshtastic Python Development](https://meshtastic.org/docs/development/python/)
|
|
||||||
- [Building Meshtastic Python](https://meshtastic.org/docs/development/python/building/)
|
|
||||||
- [Using the Meshtastic Python Library](https://meshtastic.org/docs/development/python/library/)
|
|
||||||
|
|
||||||
## How to check your code (pytest/pylint) before a PR
|
|
||||||
- [Pre-requisites](https://meshtastic.org/docs/development/python/building/#pre-requisites)
|
|
||||||
- also execute `poetry install --all-extras --with dev,powermon` for all optional dependencies
|
|
||||||
- check your code with github ci actions locally
|
|
||||||
- You need to have act installed. You can get it at https://nektosact.com/
|
|
||||||
- on linux: `act -P ubuntu-latest=-self-hosted --matrix "python-version:3.12"`
|
|
||||||
- on windows:
|
|
||||||
- linux checks (linux docker): `act --matrix "python-version:3.12"`
|
|
||||||
- windows checks (windows host): `act -P ubuntu-latest=-self-hosted --matrix "python-version:3.12"`
|
|
||||||
- or run all locally:
|
|
||||||
- run `poetry run pylint meshtastic examples/ --ignore-patterns ".*_pb2.pyi?$"`
|
|
||||||
- run `poetry run mypy meshtastic/`
|
|
||||||
- run `poetry run pytest`
|
|
||||||
- more commands see [CI workflow](https://github.com/meshtastic/python/blob/master/.github/workflows/ci.yml)
|
|
||||||
206
.github/copilot-instructions.md
vendored
206
.github/copilot-instructions.md
vendored
@@ -1,206 +0,0 @@
|
|||||||
# Copilot Instructions for Meshtastic Python
|
|
||||||
|
|
||||||
## Project Overview
|
|
||||||
|
|
||||||
This is the Meshtastic Python library and CLI - a Python API for interacting with Meshtastic mesh radio devices. It supports communication via Serial, TCP, and BLE interfaces.
|
|
||||||
|
|
||||||
## Technology Stack
|
|
||||||
|
|
||||||
- **Language**: Python 3.9 - 3.14
|
|
||||||
- **Package Manager**: Poetry
|
|
||||||
- **Testing**: pytest with hypothesis for property-based testing
|
|
||||||
- **Linting**: pylint
|
|
||||||
- **Type Checking**: mypy (working toward strict mode)
|
|
||||||
- **Documentation**: pdoc3
|
|
||||||
- **License**: GPL-3.0
|
|
||||||
|
|
||||||
## Project Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
meshtastic/ # Main library package
|
|
||||||
├── __init__.py # Core interface classes and pub/sub topics
|
|
||||||
├── __main__.py # CLI entry point
|
|
||||||
├── mesh_interface.py # Base interface class for all connection types
|
|
||||||
├── serial_interface.py
|
|
||||||
├── tcp_interface.py
|
|
||||||
├── ble_interface.py
|
|
||||||
├── node.py # Node representation and configuration
|
|
||||||
├── protobuf/ # Generated Protocol Buffer files (*_pb2.py, *_pb2.pyi)
|
|
||||||
├── tests/ # Unit and integration tests
|
|
||||||
├── powermon/ # Power monitoring tools
|
|
||||||
└── analysis/ # Data analysis tools
|
|
||||||
examples/ # Usage examples
|
|
||||||
protobufs/ # Protocol Buffer source definitions
|
|
||||||
```
|
|
||||||
|
|
||||||
## Coding Standards
|
|
||||||
|
|
||||||
### Style Guidelines
|
|
||||||
|
|
||||||
- Follow PEP 8 style conventions
|
|
||||||
- Use type hints for function parameters and return values
|
|
||||||
- Document public functions and classes with docstrings
|
|
||||||
- Prefer explicit imports over wildcard imports
|
|
||||||
- Use `logging` module instead of print statements for debug output
|
|
||||||
|
|
||||||
### Type Annotations
|
|
||||||
|
|
||||||
- Add type hints to all new code
|
|
||||||
- Use `Optional[T]` for nullable types
|
|
||||||
- Use `Dict`, `List`, `Tuple` from `typing` module for Python 3.9 compatibility
|
|
||||||
- Protobuf types are in `meshtastic.protobuf.*_pb2` modules
|
|
||||||
|
|
||||||
### Naming Conventions
|
|
||||||
|
|
||||||
- Classes: `PascalCase` (e.g., `MeshInterface`, `SerialInterface`)
|
|
||||||
- Functions/methods: `camelCase` for public API (e.g., `sendText`, `sendData`)
|
|
||||||
- Internal functions: `snake_case` with leading underscore (e.g., `_send_packet`)
|
|
||||||
- Constants: `UPPER_SNAKE_CASE` (e.g., `BROADCAST_ADDR`, `LOCAL_ADDR`)
|
|
||||||
|
|
||||||
### Error Handling
|
|
||||||
|
|
||||||
- Use custom exception classes when appropriate (e.g., `MeshInterface.MeshInterfaceError`)
|
|
||||||
- Provide meaningful error messages
|
|
||||||
- Use `our_exit()` from `meshtastic.util` for CLI exits with error codes
|
|
||||||
|
|
||||||
## Testing
|
|
||||||
|
|
||||||
### Test Organization
|
|
||||||
|
|
||||||
Tests are in `meshtastic/tests/` and use pytest markers:
|
|
||||||
|
|
||||||
- `@pytest.mark.unit` - Fast unit tests (default)
|
|
||||||
- `@pytest.mark.unitslow` - Slower unit tests
|
|
||||||
- `@pytest.mark.int` - Integration tests
|
|
||||||
- `@pytest.mark.smoke1` - Single device smoke tests
|
|
||||||
- `@pytest.mark.smoke2` - Two device smoke tests
|
|
||||||
- `@pytest.mark.smokevirt` - Virtual device smoke tests
|
|
||||||
- `@pytest.mark.examples` - Example validation tests
|
|
||||||
|
|
||||||
### Running Tests
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Run unit tests only (default)
|
|
||||||
make test
|
|
||||||
# or
|
|
||||||
pytest -m unit
|
|
||||||
|
|
||||||
# Run all tests
|
|
||||||
pytest
|
|
||||||
|
|
||||||
# Run with coverage
|
|
||||||
make cov
|
|
||||||
```
|
|
||||||
|
|
||||||
### Writing Tests
|
|
||||||
|
|
||||||
- Use `pytest` fixtures from `conftest.py`
|
|
||||||
- Use `hypothesis` for property-based testing where appropriate
|
|
||||||
- Mock external dependencies (serial ports, network connections)
|
|
||||||
- Test file naming: `test_<module_name>.py`
|
|
||||||
|
|
||||||
## Pub/Sub Events
|
|
||||||
|
|
||||||
The library uses pypubsub for event handling. Key topics:
|
|
||||||
|
|
||||||
- `meshtastic.connection.established` - Connection successful
|
|
||||||
- `meshtastic.connection.lost` - Connection lost
|
|
||||||
- `meshtastic.receive.text(packet)` - Text message received
|
|
||||||
- `meshtastic.receive.position(packet)` - Position update received
|
|
||||||
- `meshtastic.receive.data.portnum(packet)` - Data packet by port number
|
|
||||||
- `meshtastic.node.updated(node)` - Node database changed
|
|
||||||
- `meshtastic.log.line(line)` - Raw log line from device
|
|
||||||
|
|
||||||
## Protocol Buffers
|
|
||||||
|
|
||||||
- Protobuf definitions are in `protobufs/meshtastic/`
|
|
||||||
- Generated Python files are in `meshtastic/protobuf/`
|
|
||||||
- Never edit `*_pb2.py` or `*_pb2.pyi` files directly
|
|
||||||
- Regenerate with: `make protobufs` or `./bin/regen-protobufs.sh`
|
|
||||||
|
|
||||||
## Common Patterns
|
|
||||||
|
|
||||||
### Creating an Interface
|
|
||||||
|
|
||||||
```python
|
|
||||||
import meshtastic.serial_interface
|
|
||||||
|
|
||||||
# Auto-detect device
|
|
||||||
iface = meshtastic.serial_interface.SerialInterface()
|
|
||||||
|
|
||||||
# Specific device
|
|
||||||
iface = meshtastic.serial_interface.SerialInterface(devPath="/dev/ttyUSB0")
|
|
||||||
|
|
||||||
# Always close when done
|
|
||||||
iface.close()
|
|
||||||
|
|
||||||
# Or use context manager
|
|
||||||
with meshtastic.serial_interface.SerialInterface() as iface:
|
|
||||||
iface.sendText("Hello mesh")
|
|
||||||
```
|
|
||||||
|
|
||||||
### Sending Messages
|
|
||||||
|
|
||||||
```python
|
|
||||||
# Text message (broadcast)
|
|
||||||
iface.sendText("Hello")
|
|
||||||
|
|
||||||
# Text message to specific node
|
|
||||||
iface.sendText("Hello", destinationId="!abcd1234")
|
|
||||||
|
|
||||||
# Binary data
|
|
||||||
iface.sendData(data, portNum=portnums_pb2.PRIVATE_APP)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Subscribing to Events
|
|
||||||
|
|
||||||
```python
|
|
||||||
from pubsub import pub
|
|
||||||
|
|
||||||
def on_receive(packet, interface):
|
|
||||||
print(f"Received: {packet}")
|
|
||||||
|
|
||||||
pub.subscribe(on_receive, "meshtastic.receive")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Development Workflow
|
|
||||||
|
|
||||||
1. Install dependencies: `poetry install --all-extras --with dev`
|
|
||||||
2. Make changes
|
|
||||||
3. Run linting: `poetry run pylint meshtastic examples/`
|
|
||||||
4. Run type checking: `poetry run mypy meshtastic/`
|
|
||||||
5. Run tests: `poetry run pytest -m unit`
|
|
||||||
6. Update documentation if needed
|
|
||||||
|
|
||||||
## CLI Development
|
|
||||||
|
|
||||||
The CLI is in `meshtastic/__main__.py`. When adding new CLI commands:
|
|
||||||
|
|
||||||
- Use argparse for argument parsing
|
|
||||||
- Support `--dest` for specifying target node
|
|
||||||
- Provide `--help` documentation
|
|
||||||
- Handle errors gracefully with meaningful messages
|
|
||||||
|
|
||||||
## Dependencies
|
|
||||||
|
|
||||||
### Required
|
|
||||||
- `pyserial` - Serial port communication
|
|
||||||
- `protobuf` - Protocol Buffers
|
|
||||||
- `pypubsub` - Pub/sub messaging
|
|
||||||
- `bleak` - BLE communication
|
|
||||||
- `tabulate` - Table formatting
|
|
||||||
- `pyyaml` - YAML config support
|
|
||||||
- `requests` - HTTP requests
|
|
||||||
|
|
||||||
### Optional (extras)
|
|
||||||
- `cli` extra: `pyqrcode`, `print-color`, `dotmap`, `argcomplete`
|
|
||||||
- `tunnel` extra: `pytap2`
|
|
||||||
- `analysis` extra: `dash`, `pandas`
|
|
||||||
|
|
||||||
## Important Notes
|
|
||||||
|
|
||||||
- Always test with actual Meshtastic hardware when possible
|
|
||||||
- Be mindful of radio regulations in your region
|
|
||||||
- The nodedb (`interface.nodes`) is read-only
|
|
||||||
- Packet IDs are random 32-bit integers
|
|
||||||
- Default timeout is 300 seconds for operations
|
|
||||||
BIN
.github/meshtastic_logo.png
vendored
BIN
.github/meshtastic_logo.png
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 105 KiB |
71
.github/workflows/ci.yml
vendored
71
.github/workflows/ci.yml
vendored
@@ -1,7 +1,4 @@
|
|||||||
name: CI
|
name: CI
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
@@ -11,29 +8,7 @@ on:
|
|||||||
- master
|
- master
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
validate:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
python-version:
|
|
||||||
- "3.12"
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
- name: Install Python ${{ matrix.python-version }}
|
|
||||||
uses: actions/setup-python@v5
|
|
||||||
with:
|
|
||||||
python-version: ${{ matrix.python-version }}
|
|
||||||
- name: Install meshtastic from local
|
|
||||||
run: |
|
|
||||||
python -m pip install --upgrade pip
|
|
||||||
pip3 install poetry
|
|
||||||
poetry install --all-extras --with dev,powermon
|
|
||||||
poetry run meshtastic --version
|
|
||||||
|
|
||||||
build:
|
build:
|
||||||
needs: validate
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
@@ -42,14 +17,10 @@ jobs:
|
|||||||
- "3.10"
|
- "3.10"
|
||||||
- "3.11"
|
- "3.11"
|
||||||
- "3.12"
|
- "3.12"
|
||||||
- "3.13"
|
|
||||||
- "3.14"
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Install Python ${{ matrix.python-version }}
|
- name: Install Python 3
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v5
|
||||||
with:
|
|
||||||
python-version: ${{ matrix.python-version }}
|
|
||||||
- name: Uninstall meshtastic
|
- name: Uninstall meshtastic
|
||||||
run: |
|
run: |
|
||||||
pip3 uninstall -y meshtastic
|
pip3 uninstall -y meshtastic
|
||||||
@@ -61,8 +32,15 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
poetry install --all-extras --with dev,powermon
|
poetry install --all-extras --with dev,powermon
|
||||||
poetry run meshtastic --version
|
poetry run meshtastic --version
|
||||||
- name: Run lint, type check, and tests in parallel
|
- name: Run pylint
|
||||||
run: make -j3 --output-sync=target ci
|
run: poetry run pylint meshtastic examples/ --ignore-patterns ".*_pb2.pyi?$"
|
||||||
|
- name: Check types with mypy
|
||||||
|
run: poetry run mypy meshtastic/
|
||||||
|
- name: Run tests with pytest
|
||||||
|
run: poetry run pytest --cov=meshtastic
|
||||||
|
- name: Generate coverage report
|
||||||
|
run: |
|
||||||
|
poetry run pytest --cov=meshtastic --cov-report=xml
|
||||||
- name: Upload coverage to Codecov
|
- name: Upload coverage to Codecov
|
||||||
uses: codecov/codecov-action@v4
|
uses: codecov/codecov-action@v4
|
||||||
with:
|
with:
|
||||||
@@ -73,37 +51,22 @@ jobs:
|
|||||||
name: codecov-umbrella
|
name: codecov-umbrella
|
||||||
fail_ci_if_error: true
|
fail_ci_if_error: true
|
||||||
verbose: true
|
verbose: true
|
||||||
|
validate:
|
||||||
simradio_testing:
|
|
||||||
needs: [validate, build]
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
matrix:
|
||||||
channel:
|
python-version:
|
||||||
- beta
|
- "3.9"
|
||||||
- alpha
|
- "3.10"
|
||||||
- daily
|
- "3.11"
|
||||||
continue-on-error: ${{ matrix.channel == 'daily' }}
|
- "3.12"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
- name: Install Python 3
|
- name: Install Python 3
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v5
|
||||||
with:
|
|
||||||
python-version: "3.12"
|
|
||||||
- name: Install meshtastic from local
|
- name: Install meshtastic from local
|
||||||
run: |
|
run: |
|
||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip3 install poetry
|
pip3 install poetry
|
||||||
poetry install --all-extras --with dev
|
poetry install
|
||||||
poetry run meshtastic --version
|
poetry run meshtastic --version
|
||||||
- name: Install meshtasticd (${{ matrix.channel }}) from PPA
|
|
||||||
run: |
|
|
||||||
sudo add-apt-repository -y ppa:meshtastic/${{ matrix.channel }}
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y meshtasticd
|
|
||||||
- name: Run firmware smoke tests
|
|
||||||
run: poetry run pytest -m "smokevirt or smokemesh" -v
|
|
||||||
timeout-minutes: 15
|
|
||||||
|
|||||||
79
.github/workflows/container-build.yaml
vendored
79
.github/workflows/container-build.yaml
vendored
@@ -1,79 +0,0 @@
|
|||||||
name: Create and publish Container image
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
tags:
|
|
||||||
- '*.*.*'
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
|
|
||||||
env:
|
|
||||||
REGISTRY: ghcr.io
|
|
||||||
IMAGE_NAME: ${{ github.repository }}
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-and-push-image:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
packages: write
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- container: Containerfile.debian
|
|
||||||
autotag: false
|
|
||||||
suffix: -debian
|
|
||||||
- container: Containerfile.alpine
|
|
||||||
autotag: ${{ github.ref == 'refs/heads/master' && 'true' || 'auto' }}
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v3
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
|
|
||||||
- name: Login to Container registry
|
|
||||||
if: github.event_name != 'pull_request'
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
registry: ${{ env.REGISTRY }}
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Docker meta
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@v5
|
|
||||||
with:
|
|
||||||
images: |
|
|
||||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
|
||||||
tags: |
|
|
||||||
type=ref,event=branch
|
|
||||||
type=ref,event=pr
|
|
||||||
type=edge
|
|
||||||
type=semver,pattern={{version}}
|
|
||||||
type=semver,pattern={{major}}.{{minor}}
|
|
||||||
type=semver,pattern={{major}}
|
|
||||||
flavor: |
|
|
||||||
latest=${{ matrix.autotag }}
|
|
||||||
suffix=${{ matrix.suffix }}
|
|
||||||
|
|
||||||
- name: Build and push
|
|
||||||
uses: docker/build-push-action@v6
|
|
||||||
with:
|
|
||||||
platforms: linux/amd64,linux/arm64
|
|
||||||
context: .
|
|
||||||
file: ${{ matrix.container }}
|
|
||||||
push: ${{ github.event_name != 'pull_request' }}
|
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
|
||||||
cache-from: type=gha
|
|
||||||
cache-to: type=gha,mode=max
|
|
||||||
36
.github/workflows/daily_simradio.yml
vendored
36
.github/workflows/daily_simradio.yml
vendored
@@ -1,36 +0,0 @@
|
|||||||
name: Daily SimRadio Tests
|
|
||||||
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
- cron: 0 6 * * *
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
simradio_testing:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: github.repository == 'meshtastic/python'
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
- name: Install Python 3
|
|
||||||
uses: actions/setup-python@v5
|
|
||||||
with:
|
|
||||||
python-version: "3.12"
|
|
||||||
- name: Install meshtastic from local
|
|
||||||
run: |
|
|
||||||
python -m pip install --upgrade pip
|
|
||||||
pip3 install poetry
|
|
||||||
poetry install --all-extras --with dev
|
|
||||||
poetry run meshtastic --version
|
|
||||||
- name: Install meshtasticd (daily) from PPA
|
|
||||||
run: |
|
|
||||||
sudo add-apt-repository -y ppa:meshtastic/daily
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y meshtasticd
|
|
||||||
- name: Run firmware smoke tests
|
|
||||||
run: poetry run pytest -m "smokevirt or smokemesh" -v
|
|
||||||
timeout-minutes: 15
|
|
||||||
33
.github/workflows/update_protobufs.yml
vendored
33
.github/workflows/update_protobufs.yml
vendored
@@ -1,9 +1,6 @@
|
|||||||
name: "Update protobufs"
|
name: "Update protobufs"
|
||||||
on: workflow_dispatch
|
on: workflow_dispatch
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
update-protobufs:
|
update-protobufs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -12,34 +9,23 @@ jobs:
|
|||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
|
||||||
submodules: true
|
submodules: true
|
||||||
|
|
||||||
- name: Setup Python
|
- name: Update Submodule
|
||||||
uses: actions/setup-python@v5
|
|
||||||
with:
|
|
||||||
python-version: "3.12"
|
|
||||||
|
|
||||||
- name: Install Poetry
|
|
||||||
run: |
|
run: |
|
||||||
python -m pip install --upgrade pip
|
git pull --recurse-submodules
|
||||||
python -m pip install poetry
|
|
||||||
|
|
||||||
- name: Update protobuf submodule
|
|
||||||
run: |
|
|
||||||
git submodule sync --recursive
|
|
||||||
git submodule update --init --recursive
|
|
||||||
git submodule update --remote --recursive
|
git submodule update --remote --recursive
|
||||||
|
|
||||||
- name: Download nanopb
|
- name: Download nanopb
|
||||||
run: |
|
run: |
|
||||||
curl -L -o nanopb-0.4.8-linux-x86.tar.gz https://jpa.kapsi.fi/nanopb/download/nanopb-0.4.8-linux-x86.tar.gz
|
wget https://jpa.kapsi.fi/nanopb/download/nanopb-0.4.8-linux-x86.tar.gz
|
||||||
tar xvzf nanopb-0.4.8-linux-x86.tar.gz
|
tar xvzf nanopb-0.4.8-linux-x86.tar.gz
|
||||||
mv nanopb-0.4.8-linux-x86 nanopb-0.4.8
|
mv nanopb-0.4.8-linux-x86 nanopb-0.4.8
|
||||||
|
|
||||||
- name: Install Python dependencies
|
- name: Install poetry (needed by regen-protobufs.sh)
|
||||||
run: |
|
run: |
|
||||||
poetry install --with dev
|
python -m pip install --upgrade pip
|
||||||
|
pip3 install poetry
|
||||||
|
|
||||||
- name: Re-generate protocol buffers
|
- name: Re-generate protocol buffers
|
||||||
run: |
|
run: |
|
||||||
@@ -52,9 +38,4 @@ jobs:
|
|||||||
git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}
|
git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}
|
||||||
git add protobufs
|
git add protobufs
|
||||||
git add meshtastic/protobuf
|
git add meshtastic/protobuf
|
||||||
if [[ -n "$(git status --porcelain)" ]]; then
|
git commit -m "Update protobuf submodule" && git push || echo "No changes to commit"
|
||||||
git commit -m "Update protobufs"
|
|
||||||
git push
|
|
||||||
else
|
|
||||||
echo "No changes to commit"
|
|
||||||
fi
|
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -18,4 +18,3 @@ meshtastic.spec
|
|||||||
.hypothesis/
|
.hypothesis/
|
||||||
coverage.xml
|
coverage.xml
|
||||||
.ipynb_checkpoints
|
.ipynb_checkpoints
|
||||||
.cursor/
|
|
||||||
12
.vscode/launch.json
vendored
12
.vscode/launch.json
vendored
@@ -4,7 +4,6 @@
|
|||||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"configurations": [
|
"configurations": [
|
||||||
|
|
||||||
{
|
{
|
||||||
"name": "meshtastic BLE",
|
"name": "meshtastic BLE",
|
||||||
"type": "debugpy",
|
"type": "debugpy",
|
||||||
@@ -262,14 +261,7 @@
|
|||||||
"module": "meshtastic",
|
"module": "meshtastic",
|
||||||
"justMyCode": true,
|
"justMyCode": true,
|
||||||
"args": ["--nodes", "--show-fields", "AKA,Pubkey,Role,Role,Role,Latitude,Latitude,deviceMetrics.voltage"]
|
"args": ["--nodes", "--show-fields", "AKA,Pubkey,Role,Role,Role,Latitude,Latitude,deviceMetrics.voltage"]
|
||||||
},
|
}
|
||||||
{
|
|
||||||
"name": "meshtastic --export-config",
|
|
||||||
"type": "debugpy",
|
|
||||||
"request": "launch",
|
|
||||||
"module": "meshtastic",
|
|
||||||
"justMyCode": true,
|
|
||||||
"args": ["--export-config", "config.json"]
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
Containerfile.alpine
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
#
|
|
||||||
# Copyright (C) 2025 Olliver Schinagl <oliver@schinagl.nl>
|
|
||||||
|
|
||||||
ARG TARGET_VERSION="3.11-alpine"
|
|
||||||
ARG TARGET_ARCH="library"
|
|
||||||
|
|
||||||
FROM docker.io/${TARGET_ARCH}/python:${TARGET_VERSION} AS builder
|
|
||||||
|
|
||||||
WORKDIR /tmp/build
|
|
||||||
|
|
||||||
COPY pyproject.toml poetry.lock /tmp/build/
|
|
||||||
|
|
||||||
RUN apk add --no-cache --virtual .build-deps build-base libffi-dev && \
|
|
||||||
pip install --no-cache-dir 'poetry==2.4.1' && \
|
|
||||||
poetry config virtualenvs.create false && \
|
|
||||||
poetry install --without dev --extras cli --extras tunnel --no-interaction --no-ansi --no-root
|
|
||||||
|
|
||||||
COPY . /tmp/build/
|
|
||||||
|
|
||||||
RUN poetry build --format wheel --no-interaction
|
|
||||||
|
|
||||||
FROM docker.io/${TARGET_ARCH}/python:${TARGET_VERSION}
|
|
||||||
|
|
||||||
RUN apk add --no-cache libffi && \
|
|
||||||
addgroup -S meshtastic && \
|
|
||||||
adduser -S -G meshtastic -h /home/meshtastic meshtastic
|
|
||||||
|
|
||||||
COPY --from=builder /tmp/build/dist/*.whl /tmp/
|
|
||||||
|
|
||||||
RUN wheel=$(echo /tmp/meshtastic-*.whl) && pip install --no-cache-dir "${wheel}[cli,tunnel]" && \
|
|
||||||
rm -f /tmp/meshtastic-*.whl
|
|
||||||
|
|
||||||
COPY "./bin/container-entrypoint.sh" "/init"
|
|
||||||
RUN chmod 0755 /init
|
|
||||||
|
|
||||||
WORKDIR /home/meshtastic
|
|
||||||
USER meshtastic
|
|
||||||
|
|
||||||
ENTRYPOINT [ "/init" ]
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
#
|
|
||||||
# Copyright (C) 2025 Olliver Schinagl <oliver@schinagl.nl>
|
|
||||||
|
|
||||||
ARG TARGET_VERSION="3.11"
|
|
||||||
ARG TARGET_ARCH="library"
|
|
||||||
|
|
||||||
FROM docker.io/${TARGET_ARCH}/python:${TARGET_VERSION} AS builder
|
|
||||||
|
|
||||||
WORKDIR /tmp/build
|
|
||||||
|
|
||||||
COPY pyproject.toml poetry.lock /tmp/build/
|
|
||||||
|
|
||||||
RUN pip install --no-cache-dir 'poetry==2.4.1' && \
|
|
||||||
poetry config virtualenvs.create false && \
|
|
||||||
poetry install --without dev --extras cli --extras tunnel --no-interaction --no-ansi --no-root
|
|
||||||
|
|
||||||
COPY . /tmp/build/
|
|
||||||
|
|
||||||
RUN poetry build --format wheel --no-interaction
|
|
||||||
|
|
||||||
FROM docker.io/${TARGET_ARCH}/python:${TARGET_VERSION}
|
|
||||||
|
|
||||||
RUN useradd --system --create-home --home-dir /home/meshtastic meshtastic
|
|
||||||
|
|
||||||
COPY --from=builder /tmp/build/dist/*.whl /tmp/
|
|
||||||
|
|
||||||
RUN wheel=$(echo /tmp/meshtastic-*.whl) && pip install --no-cache-dir "${wheel}[cli,tunnel]" && \
|
|
||||||
rm -f /tmp/meshtastic-*.whl
|
|
||||||
|
|
||||||
COPY "./bin/container-entrypoint.sh" "/init"
|
|
||||||
RUN chmod 0755 /init
|
|
||||||
|
|
||||||
WORKDIR /home/meshtastic
|
|
||||||
USER meshtastic
|
|
||||||
|
|
||||||
ENTRYPOINT [ "/init" ]
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
Containerfile
|
|
||||||
14
Makefile
14
Makefile
@@ -42,19 +42,5 @@ cov:
|
|||||||
examples: FORCE
|
examples: FORCE
|
||||||
pytest -mexamples
|
pytest -mexamples
|
||||||
|
|
||||||
# CI targets (run via poetry, executed in parallel by the CI workflow)
|
|
||||||
.PHONY: ci-pylint ci-mypy ci-test ci
|
|
||||||
|
|
||||||
ci-pylint:
|
|
||||||
poetry run pylint meshtastic examples/ --ignore-patterns ".*_pb2.pyi?$$"
|
|
||||||
|
|
||||||
ci-mypy:
|
|
||||||
poetry run mypy meshtastic/
|
|
||||||
|
|
||||||
ci-test:
|
|
||||||
poetry run pytest --cov=meshtastic --cov-report=xml
|
|
||||||
|
|
||||||
ci: ci-pylint ci-mypy ci-test
|
|
||||||
|
|
||||||
# Makefile hack to get the examples to always run
|
# Makefile hack to get the examples to always run
|
||||||
FORCE: ;
|
FORCE: ;
|
||||||
|
|||||||
35
README.md
35
README.md
@@ -1,10 +1,4 @@
|
|||||||
<div align="center" markdown="1">
|
# Meshtastic Python
|
||||||
|
|
||||||
<img src=".github/meshtastic_logo.png" alt="Meshtastic Logo" width="80"/>
|
|
||||||
|
|
||||||
<h1 align="center"> Meshtastic Python
|
|
||||||
</h1>
|
|
||||||
<p style="font-size:15px;" align="center">A Python library and client for use with Meshtastic devices. </p>
|
|
||||||
|
|
||||||
[](https://codecov.io/gh/meshtastic/python)
|
[](https://codecov.io/gh/meshtastic/python)
|
||||||

|

|
||||||
@@ -13,37 +7,16 @@
|
|||||||
[](https://opencollective.com/meshtastic/)
|
[](https://opencollective.com/meshtastic/)
|
||||||

|

|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div align="center">
|
|
||||||
<a href="https://meshtastic.org/docs/software/python/cli/installation">Getting Started Guide</a>
|
|
||||||
-
|
|
||||||
<a href="https://python.meshtastic.org">API Documentation</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
|
A Python client for use with Meshtastic devices.
|
||||||
This small library (and example application) provides an easy API for sending and receiving messages over mesh radios.
|
This small library (and example application) provides an easy API for sending and receiving messages over mesh radios.
|
||||||
It also provides access to any of the operations/data available in the device user interface or the Android application.
|
It also provides access to any of the operations/data available in the device user interface or the Android application.
|
||||||
Events are delivered using a publish-subscribe model, and you can subscribe to only the message types you are interested in.
|
Events are delivered using a publish-subscribe model, and you can subscribe to only the message types you are interested in.
|
||||||
|
|
||||||
## Container usage
|
**[Getting Started Guide](https://meshtastic.org/docs/software/python/cli/installation)**
|
||||||
|
|
||||||
Container images are published to GHCR for this repository. The container entrypoint defaults to running `meshtastic`,
|
**[API Documentation](https://python.meshtastic.org)**
|
||||||
so CLI flags can be passed directly:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker run --rm ghcr.io/meshtastic/python --help
|
|
||||||
```
|
|
||||||
|
|
||||||
To run another command, pass it explicitly (for example, a shell):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker run --rm -it --entrypoint /bin/sh ghcr.io/meshtastic/python
|
|
||||||
```
|
|
||||||
|
|
||||||
The container runs as a non-root user by default. When talking to local hardware, pass the serial device through
|
|
||||||
explicitly (for example `--device /dev/ttyUSB0:/dev/ttyUSB0`) and ensure host device permissions allow access.
|
|
||||||
|
|
||||||
## Call for Contributors
|
## Call for Contributors
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
#
|
|
||||||
# Copyright (C) 2025 Olliver Schinagl <oliver@schinagl.nl>
|
|
||||||
#
|
|
||||||
# A beginning user should be able to docker run image bash (or sh) without
|
|
||||||
# needing to learn about --entrypoint
|
|
||||||
# https://github.com/docker-library/official-images#consistency
|
|
||||||
|
|
||||||
set -eu
|
|
||||||
|
|
||||||
bin='meshtastic'
|
|
||||||
|
|
||||||
# run command if it is not starting with a "-" and is an executable in PATH
|
|
||||||
if [ "${#}" -le 0 ] || \
|
|
||||||
[ "${1#-}" != "${1}" ] || \
|
|
||||||
[ -d "${1}" ] || \
|
|
||||||
! command -v "${1}" > '/dev/null' 2>&1; then
|
|
||||||
entrypoint='true'
|
|
||||||
fi
|
|
||||||
|
|
||||||
exec ${entrypoint:+${bin:?}} "${@}"
|
|
||||||
|
|
||||||
exit 0
|
|
||||||
@@ -1,332 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""Inject nanopb .options constraints as inline field options into a .proto file.
|
|
||||||
|
|
||||||
The nanopb .options file format is specific to the nanopb C generator and is
|
|
||||||
ignored by standard protoc (including --python_out). By injecting the options
|
|
||||||
directly into the proto file's field declarations, protoc will embed them in
|
|
||||||
the serialized descriptor, making them accessible in Python via:
|
|
||||||
|
|
||||||
from meshtastic.protobuf import mesh_pb2, nanopb_pb2
|
|
||||||
field = mesh_pb2.DESCRIPTOR.message_types_by_name['User'].fields_by_name['long_name']
|
|
||||||
opts = field.GetOptions().Extensions[nanopb_pb2.nanopb]
|
|
||||||
print(opts.max_size) # 40
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
inject_nanopb_options.py <options_file> <proto_file>
|
|
||||||
|
|
||||||
The proto_file is modified in-place. Intended to operate on temporary copies
|
|
||||||
generated by regen-protobufs.sh, not the source .proto files.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import re
|
|
||||||
import sys
|
|
||||||
from pathlib import Path
|
|
||||||
from typing import Any, Dict, List, Tuple
|
|
||||||
|
|
||||||
# IntSize enum values from nanopb.proto
|
|
||||||
INT_SIZE_ENUM = {8: "IS_8", 16: "IS_16", 32: "IS_32", 64: "IS_64"}
|
|
||||||
|
|
||||||
# Options that are valid proto FieldOptions and useful outside of C code generation.
|
|
||||||
# We skip C-only options (anonymous_oneof, no_unions, skip_message, packed_struct,
|
|
||||||
# packed_enum, mangle_names, callback_datatype, callback_function, descriptorsize,
|
|
||||||
# type_override) since they either don't apply to proto fields or are C-specific.
|
|
||||||
FIELD_OPTIONS = frozenset(
|
|
||||||
{
|
|
||||||
"max_size",
|
|
||||||
"max_length",
|
|
||||||
"max_count",
|
|
||||||
"int_size",
|
|
||||||
"fixed_length",
|
|
||||||
"fixed_count",
|
|
||||||
"long_names",
|
|
||||||
"proto3",
|
|
||||||
"default_has",
|
|
||||||
"sort_by_tag",
|
|
||||||
"msgid",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def parse_value(s: str) -> Any:
|
|
||||||
"""Convert an option value string to an appropriate Python type."""
|
|
||||||
if re.fullmatch(r"-?[0-9]+", s):
|
|
||||||
return int(s)
|
|
||||||
if s.lower() == "true":
|
|
||||||
return True
|
|
||||||
if s.lower() == "false":
|
|
||||||
return False
|
|
||||||
return s
|
|
||||||
|
|
||||||
|
|
||||||
def parse_options_file(
|
|
||||||
path: Path,
|
|
||||||
) -> Tuple[Dict[Tuple[str, ...], Dict[str, Any]], Dict[str, Dict[str, Any]]]:
|
|
||||||
"""Parse a nanopb .options file.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
specific: maps (message_path..., field_name) -> {option: value}
|
|
||||||
e.g. ('User', 'long_name') or ('Route', 'Link', 'uid')
|
|
||||||
wildcard: maps field_name -> {option: value}
|
|
||||||
applies to any field with this name in the same proto file
|
|
||||||
"""
|
|
||||||
specific: Dict[Tuple[str, ...], Dict[str, Any]] = {}
|
|
||||||
wildcard: Dict[str, Dict[str, Any]] = {}
|
|
||||||
|
|
||||||
with open(path, encoding="utf-8") as f:
|
|
||||||
for line in f:
|
|
||||||
# Strip inline comments
|
|
||||||
comment_pos = line.find("#")
|
|
||||||
if comment_pos >= 0:
|
|
||||||
line = line[:comment_pos]
|
|
||||||
line = line.strip().lstrip("*").strip()
|
|
||||||
if not line:
|
|
||||||
continue
|
|
||||||
|
|
||||||
tokens = line.split()
|
|
||||||
if len(tokens) < 2:
|
|
||||||
continue
|
|
||||||
|
|
||||||
pattern = tokens[0]
|
|
||||||
opts: Dict[str, Any] = {}
|
|
||||||
for tok in tokens[1:]:
|
|
||||||
if ":" in tok:
|
|
||||||
k, v = tok.split(":", 1)
|
|
||||||
if k in FIELD_OPTIONS:
|
|
||||||
opts[k] = parse_value(v)
|
|
||||||
|
|
||||||
if not opts:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if "." in pattern:
|
|
||||||
# e.g. "User.long_name" -> key=('User', 'long_name')
|
|
||||||
# or "Route.Link.uid" -> key=('Route', 'Link', 'uid')
|
|
||||||
parts = tuple(pattern.split("."))
|
|
||||||
if parts in specific:
|
|
||||||
specific[parts].update(opts)
|
|
||||||
else:
|
|
||||||
specific[parts] = opts
|
|
||||||
else:
|
|
||||||
# wildcard: applies to any field with this name
|
|
||||||
if pattern in wildcard:
|
|
||||||
wildcard[pattern].update(opts)
|
|
||||||
else:
|
|
||||||
wildcard[pattern] = opts
|
|
||||||
|
|
||||||
return specific, wildcard
|
|
||||||
|
|
||||||
|
|
||||||
def format_nanopb_opts(opts: Dict[str, Any]) -> str:
|
|
||||||
"""Format a dict of nanopb options as a proto field options annotation string."""
|
|
||||||
parts = []
|
|
||||||
for k, v in opts.items():
|
|
||||||
if k == "int_size":
|
|
||||||
enum_val = INT_SIZE_ENUM.get(v, f"IS_{v}")
|
|
||||||
parts.append(f"(nanopb).int_size = {enum_val}")
|
|
||||||
elif isinstance(v, bool):
|
|
||||||
parts.append(f"(nanopb).{k} = {'true' if v else 'false'}")
|
|
||||||
else:
|
|
||||||
parts.append(f"(nanopb).{k} = {v}")
|
|
||||||
return ", ".join(parts)
|
|
||||||
|
|
||||||
|
|
||||||
def message_path_matches(
|
|
||||||
context_stack: List[Tuple[str, str]], msg_path: Tuple[str, ...]
|
|
||||||
) -> bool:
|
|
||||||
"""Check if the current message context ends with msg_path.
|
|
||||||
|
|
||||||
context_stack entries are ('message'|'oneof'|'enum', name).
|
|
||||||
msg_path is the tuple of message names from the options pattern,
|
|
||||||
e.g. ('User',) or ('Route', 'Link').
|
|
||||||
"""
|
|
||||||
msg_names = [name for kind, name in context_stack if kind == "message"]
|
|
||||||
n = len(msg_path)
|
|
||||||
return len(msg_names) >= n and tuple(msg_names[-n:]) == msg_path
|
|
||||||
|
|
||||||
|
|
||||||
def inject_into_proto(
|
|
||||||
content: str,
|
|
||||||
specific: Dict[Tuple[str, ...], Dict[str, Any]],
|
|
||||||
wildcard: Dict[str, Dict[str, Any]],
|
|
||||||
nanopb_import_path: str,
|
|
||||||
) -> str:
|
|
||||||
"""Inject nanopb field options into a proto file's text content.
|
|
||||||
|
|
||||||
Adds an import for nanopb.proto if not already present.
|
|
||||||
Returns the modified content.
|
|
||||||
"""
|
|
||||||
if not specific and not wildcard:
|
|
||||||
return content
|
|
||||||
|
|
||||||
lines = content.split("\n")
|
|
||||||
|
|
||||||
# Check if nanopb is already imported (after sed fixup, it will be
|
|
||||||
# 'meshtastic/protobuf/nanopb.proto')
|
|
||||||
nanopb_already_imported = any(
|
|
||||||
"nanopb.proto" in line
|
|
||||||
for line in lines
|
|
||||||
if line.strip().startswith("import")
|
|
||||||
)
|
|
||||||
|
|
||||||
# Track the index of the last import line so we can insert after it
|
|
||||||
last_import_idx = max(
|
|
||||||
(
|
|
||||||
i
|
|
||||||
for i, line in enumerate(lines)
|
|
||||||
if line.strip().startswith("import ") and line.strip().endswith(";")
|
|
||||||
),
|
|
||||||
default=-1,
|
|
||||||
)
|
|
||||||
|
|
||||||
# State
|
|
||||||
context_stack: List[Tuple[str, str]] = [] # ('message'|'oneof'|'enum', name)
|
|
||||||
result: List[str] = []
|
|
||||||
import_added = nanopb_already_imported
|
|
||||||
|
|
||||||
# Patterns for proto structural elements
|
|
||||||
message_re = re.compile(r"^(\s*)message\s+(\w+)\s*\{")
|
|
||||||
oneof_re = re.compile(r"^(\s*)oneof\s+(\w+)\s*\{")
|
|
||||||
enum_re = re.compile(r"^(\s*)enum\s+(\w+)\s*\{")
|
|
||||||
close_re = re.compile(r"^\s*\}")
|
|
||||||
|
|
||||||
# Pattern for field declarations:
|
|
||||||
# indent [optional|repeated] type name = number [options] ;
|
|
||||||
# We exclude map<> fields (different syntax, nanopb handles them differently)
|
|
||||||
# and enum value lines (no type keyword before the name).
|
|
||||||
field_re = re.compile(
|
|
||||||
r"^(\s*)" # (1) indent
|
|
||||||
r"(optional\s+|repeated\s+)?" # (2) optional qualifier
|
|
||||||
r"([\w.]+)\s+" # (3) field type (possibly qualified like google.protobuf.Any)
|
|
||||||
r"(\w+)\s*" # (4) field name
|
|
||||||
r"=\s*(\d+)" # (5) field number
|
|
||||||
r"(?:\s*\[([^\]]*)\])?" # (6) existing options, without brackets
|
|
||||||
r"\s*;" # trailing semicolon
|
|
||||||
)
|
|
||||||
|
|
||||||
for i, line in enumerate(lines):
|
|
||||||
# Insert nanopb import right after the last existing import line.
|
|
||||||
# Only do this when there IS an existing import (last_import_idx >= 0);
|
|
||||||
# if there are no imports we fall through to the syntax-line fallback below.
|
|
||||||
if not import_added and last_import_idx >= 0 and i == last_import_idx + 1:
|
|
||||||
result.append(f'import "{nanopb_import_path}";')
|
|
||||||
import_added = True
|
|
||||||
|
|
||||||
# --- Track message/oneof/enum nesting ---
|
|
||||||
m = message_re.match(line)
|
|
||||||
if m:
|
|
||||||
context_stack.append(("message", m.group(2)))
|
|
||||||
result.append(line)
|
|
||||||
continue
|
|
||||||
|
|
||||||
m = oneof_re.match(line)
|
|
||||||
if m:
|
|
||||||
context_stack.append(("oneof", m.group(2)))
|
|
||||||
result.append(line)
|
|
||||||
continue
|
|
||||||
|
|
||||||
m = enum_re.match(line)
|
|
||||||
if m:
|
|
||||||
context_stack.append(("enum", m.group(2)))
|
|
||||||
result.append(line)
|
|
||||||
continue
|
|
||||||
|
|
||||||
if close_re.match(line) and context_stack:
|
|
||||||
context_stack.pop()
|
|
||||||
result.append(line)
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Skip field injection inside enum bodies (enum values look like fields
|
|
||||||
# but should not have nanopb options added)
|
|
||||||
in_enum = bool(context_stack) and context_stack[-1][0] == "enum"
|
|
||||||
|
|
||||||
# --- Try to match and modify a field declaration ---
|
|
||||||
m = field_re.match(line)
|
|
||||||
if m and not in_enum:
|
|
||||||
indent = m.group(1)
|
|
||||||
qualifier = m.group(2) or ""
|
|
||||||
ftype = m.group(3)
|
|
||||||
fname = m.group(4)
|
|
||||||
fnum = m.group(5)
|
|
||||||
existing_opts = m.group(6) or ""
|
|
||||||
|
|
||||||
# Collect applicable nanopb options (wildcard < specific)
|
|
||||||
extra: Dict[str, Any] = {}
|
|
||||||
|
|
||||||
# 1. Wildcard: any field with this name in this proto file
|
|
||||||
if fname in wildcard:
|
|
||||||
extra.update(wildcard[fname])
|
|
||||||
|
|
||||||
# 2. Specific: check all keys whose last element is fname and whose
|
|
||||||
# preceding path matches the current message context
|
|
||||||
for key, opts in specific.items():
|
|
||||||
if key[-1] == fname:
|
|
||||||
msg_path = key[:-1]
|
|
||||||
if message_path_matches(context_stack, msg_path):
|
|
||||||
extra.update(opts)
|
|
||||||
break
|
|
||||||
|
|
||||||
if extra:
|
|
||||||
nanopb_str = format_nanopb_opts(extra)
|
|
||||||
if existing_opts.strip():
|
|
||||||
opts_block = f"[{existing_opts}, {nanopb_str}]"
|
|
||||||
else:
|
|
||||||
opts_block = f"[{nanopb_str}]"
|
|
||||||
qual = qualifier.rstrip()
|
|
||||||
sep = " " if qual else ""
|
|
||||||
line = f"{indent}{qual}{sep}{ftype} {fname} = {fnum} {opts_block};"
|
|
||||||
|
|
||||||
result.append(line)
|
|
||||||
|
|
||||||
# Edge case: if there were no import lines, add nanopb import after syntax line
|
|
||||||
if not import_added:
|
|
||||||
for i, line in enumerate(result):
|
|
||||||
if line.strip().startswith("syntax") and line.strip().endswith(";"):
|
|
||||||
result.insert(i + 1, f'import "{nanopb_import_path}";')
|
|
||||||
break
|
|
||||||
|
|
||||||
return "\n".join(result)
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
|
||||||
"""Parse an .options file and inject its constraints into a .proto file in-place."""
|
|
||||||
if len(sys.argv) != 3:
|
|
||||||
print(
|
|
||||||
f"Usage: {sys.argv[0]} <options_file> <proto_file>",
|
|
||||||
file=sys.stderr,
|
|
||||||
)
|
|
||||||
return 1
|
|
||||||
|
|
||||||
opts_path = Path(sys.argv[1])
|
|
||||||
proto_path = Path(sys.argv[2])
|
|
||||||
|
|
||||||
if not opts_path.exists():
|
|
||||||
print(f"Options file not found: {opts_path}", file=sys.stderr)
|
|
||||||
return 1
|
|
||||||
|
|
||||||
if not proto_path.exists():
|
|
||||||
print(f"Proto file not found: {proto_path}", file=sys.stderr)
|
|
||||||
return 1
|
|
||||||
|
|
||||||
specific, wildcard = parse_options_file(opts_path)
|
|
||||||
total = len(specific) + len(wildcard)
|
|
||||||
|
|
||||||
if total == 0:
|
|
||||||
print(f" [{opts_path.name}] No injectable options found, skipping.")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
content = proto_path.read_text(encoding="utf-8")
|
|
||||||
|
|
||||||
# After regen-protobufs.sh's sed fixup, the nanopb import path is:
|
|
||||||
nanopb_import_path = "meshtastic/protobuf/nanopb.proto"
|
|
||||||
|
|
||||||
modified = inject_into_proto(content, specific, wildcard, nanopb_import_path)
|
|
||||||
proto_path.write_text(modified, encoding="utf-8")
|
|
||||||
|
|
||||||
print(
|
|
||||||
f" [{opts_path.name}] Injected {len(specific)} specific + "
|
|
||||||
f"{len(wildcard)} wildcard option(s) into {proto_path.name}"
|
|
||||||
)
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
sys.exit(main())
|
|
||||||
@@ -6,12 +6,6 @@ set -e
|
|||||||
#gsed -i 's/import "\//import ".\//g' ./protobufs/meshtastic/*
|
#gsed -i 's/import "\//import ".\//g' ./protobufs/meshtastic/*
|
||||||
#gsed -i 's/package meshtastic;//g' ./protobufs/meshtastic/*
|
#gsed -i 's/package meshtastic;//g' ./protobufs/meshtastic/*
|
||||||
|
|
||||||
POETRYDIR=$(poetry env info --path)
|
|
||||||
|
|
||||||
if [[ -z "${POETRYDIR}" ]]; then
|
|
||||||
poetry install
|
|
||||||
fi
|
|
||||||
|
|
||||||
# protoc looks for mypy plugin in the python path
|
# protoc looks for mypy plugin in the python path
|
||||||
source $(poetry env info --path)/bin/activate
|
source $(poetry env info --path)/bin/activate
|
||||||
|
|
||||||
@@ -28,8 +22,6 @@ OUTDIR=${TMPDIR}/out
|
|||||||
PYIDIR=${TMPDIR}/out
|
PYIDIR=${TMPDIR}/out
|
||||||
mkdir -p "${OUTDIR}" "${INDIR}" "${PYIDIR}"
|
mkdir -p "${OUTDIR}" "${INDIR}" "${PYIDIR}"
|
||||||
cp ./protobufs/meshtastic/*.proto "${INDIR}"
|
cp ./protobufs/meshtastic/*.proto "${INDIR}"
|
||||||
cp ./protobufs/meshtastic/*.options "${INDIR}"
|
|
||||||
cp ./protobufs/nanopb.proto "${INDIR}"
|
|
||||||
|
|
||||||
# OS-X sed is apparently a little different and expects an arg for -i
|
# OS-X sed is apparently a little different and expects an arg for -i
|
||||||
if [[ $OSTYPE == 'darwin'* ]]; then
|
if [[ $OSTYPE == 'darwin'* ]]; then
|
||||||
@@ -44,21 +36,6 @@ $SEDCMD 's/^package meshtastic;/package meshtastic.protobuf;/' "${INDIR}/"*.prot
|
|||||||
# fix the imports to match
|
# fix the imports to match
|
||||||
$SEDCMD 's/^import "meshtastic\//import "meshtastic\/protobuf\//' "${INDIR}/"*.proto
|
$SEDCMD 's/^import "meshtastic\//import "meshtastic\/protobuf\//' "${INDIR}/"*.proto
|
||||||
|
|
||||||
$SEDCMD 's/^import "nanopb.proto"/import "meshtastic\/protobuf\/nanopb.proto"/' "${INDIR}/"*.proto
|
|
||||||
|
|
||||||
# Inject nanopb .options constraints as inline proto field options so that
|
|
||||||
# protoc --python_out embeds them in the generated descriptors. Python code
|
|
||||||
# can then read them via:
|
|
||||||
# field.GetOptions().Extensions[nanopb_pb2.nanopb].max_size
|
|
||||||
echo "Injecting nanopb options into proto files..."
|
|
||||||
for OPTS_FILE in "${INDIR}"/*.options; do
|
|
||||||
BASENAME=$(basename "${OPTS_FILE}" .options)
|
|
||||||
PROTO_FILE="${INDIR}/${BASENAME}.proto"
|
|
||||||
if [ -f "${PROTO_FILE}" ]; then
|
|
||||||
python3 ./bin/inject_nanopb_options.py "${OPTS_FILE}" "${PROTO_FILE}"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# Generate the python files
|
# Generate the python files
|
||||||
./nanopb-0.4.8/generator-bin/protoc -I=$TMPDIR/in --python_out "${OUTDIR}" "--mypy_out=${PYIDIR}" $INDIR/*.proto
|
./nanopb-0.4.8/generator-bin/protoc -I=$TMPDIR/in --python_out "${OUTDIR}" "--mypy_out=${PYIDIR}" $INDIR/*.proto
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,6 @@ owner_short: BOB
|
|||||||
|
|
||||||
channel_url: https://www.meshtastic.org/e/#CgMSAQESCDgBQANIAVAe
|
channel_url: https://www.meshtastic.org/e/#CgMSAQESCDgBQANIAVAe
|
||||||
|
|
||||||
canned_messages: Hi|Bye|Yes|No|Ok
|
|
||||||
ringtone: 24:d=32,o=5,b=565:f6,p,f6,4p,p,f6,p,f6,2p,p,b6,p,b6,p,b6,p,b6,p,b,p,b,p,b,p,b,p,b,p,b,p,b,p,b,1p.,2p.,p
|
|
||||||
|
|
||||||
location:
|
location:
|
||||||
lat: 35.88888
|
lat: 35.88888
|
||||||
lon: -93.88888
|
lon: -93.88888
|
||||||
|
|||||||
@@ -1,70 +0,0 @@
|
|||||||
# Contributing Example Scripts
|
|
||||||
|
|
||||||
Use this guide when adding or updating scripts in `examples/`.
|
|
||||||
|
|
||||||
## Must-have checklist before opening a PR
|
|
||||||
|
|
||||||
1. Script teaches one clear thing (its primary learning goal).
|
|
||||||
2. File name matches that goal.
|
|
||||||
3. Top docstring states purpose, transport scope, behavior, and expected output.
|
|
||||||
4. Script has safe shutdown (`with ...` or `finally`) and graceful `KeyboardInterrupt` handling.
|
|
||||||
5. Argument handling is clear (`argparse` preferred).
|
|
||||||
6. Errors are explicit (no bare `except:`).
|
|
||||||
7. The script is not a near-duplicate of an existing example.
|
|
||||||
|
|
||||||
## Choose the right teaching goal
|
|
||||||
|
|
||||||
Each example should have one primary lesson. Keep it focused.
|
|
||||||
|
|
||||||
- Good: "Send one text message over serial."
|
|
||||||
- Good: "Print inbound text messages."
|
|
||||||
- Avoid: discovery + chat + config mutation all in one script unless that combined flow is the lesson.
|
|
||||||
|
|
||||||
## Transport scope must be explicit
|
|
||||||
|
|
||||||
State exactly what transports are supported and why.
|
|
||||||
|
|
||||||
- Serial-only when that keeps the example simplest.
|
|
||||||
- Multi-transport (Serial/TCP/BLE) only when transport selection is part of the lesson.
|
|
||||||
- If TCP/BLE are supported, expose explicit flags (`--host`, `--ble`) and document defaults.
|
|
||||||
|
|
||||||
## Behavior and output should be predictable
|
|
||||||
|
|
||||||
Readers should know if the script sends, receives, mutates config, or combines those.
|
|
||||||
|
|
||||||
- Receive examples: subscribe to the narrowest pubsub topic that matches the lesson.
|
|
||||||
- Send examples: clarify destination behavior (broadcast default vs explicit destination).
|
|
||||||
- Mutation examples: clearly document side effects.
|
|
||||||
|
|
||||||
Output should make success easy to confirm:
|
|
||||||
|
|
||||||
- Print concise, stable status/event lines.
|
|
||||||
- Avoid noisy debug output unless the script is specifically diagnostic-focused.
|
|
||||||
|
|
||||||
## Cleanup and error handling
|
|
||||||
|
|
||||||
- Use context managers where practical; otherwise close interfaces in `finally`.
|
|
||||||
- Handle `KeyboardInterrupt` cleanly.
|
|
||||||
- Exit non-zero for invalid args, connection/setup failures, or command failures.
|
|
||||||
|
|
||||||
## Naming guidance
|
|
||||||
|
|
||||||
Use descriptive names tied to the teaching goal.
|
|
||||||
|
|
||||||
- Prefer names like `tcp_connection_info_once.py` over `pub_sub_example.py`.
|
|
||||||
- Prefer names like `tcp_pubsub_send_and_receive.py` over `pub_sub_example2.py`.
|
|
||||||
- Avoid generic names such as `example2.py`.
|
|
||||||
|
|
||||||
Keep existing filenames only when compatibility or discoverability outweighs clarity.
|
|
||||||
|
|
||||||
## New script vs extending an existing one
|
|
||||||
|
|
||||||
Create a new script when:
|
|
||||||
|
|
||||||
- The learning goal is genuinely distinct.
|
|
||||||
- Combining behaviors would make either example harder to understand.
|
|
||||||
|
|
||||||
Extend an existing script when:
|
|
||||||
|
|
||||||
- The change deepens the same lesson.
|
|
||||||
- The resulting script remains focused and readable.
|
|
||||||
@@ -1,42 +1,21 @@
|
|||||||
"""Print the local node hardware model.
|
"""Simple program to demo how to use meshtastic library.
|
||||||
|
To run: python examples/get_hw.py
|
||||||
Purpose: show the narrowest read-only local hardware lookup.
|
|
||||||
Transport scope: Serial only.
|
|
||||||
Behavior: reads local node metadata and prints hwModel.
|
|
||||||
Expected output: one hardware model line, if available.
|
|
||||||
Cleanup/error handling: exits with code 3 for bad args and closes interface on exit.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import argparse
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
import meshtastic
|
||||||
import meshtastic.serial_interface
|
import meshtastic.serial_interface
|
||||||
|
|
||||||
|
# simple arg check
|
||||||
|
if len(sys.argv) != 1:
|
||||||
|
print(f"usage: {sys.argv[0]}")
|
||||||
|
print("Print the hardware model for the local node.")
|
||||||
|
sys.exit(3)
|
||||||
|
|
||||||
def main() -> int:
|
iface = meshtastic.serial_interface.SerialInterface()
|
||||||
"""Print the hardware model for the local node."""
|
if iface.nodes:
|
||||||
if len(sys.argv) != 1:
|
for n in iface.nodes.values():
|
||||||
print(f"usage: {sys.argv[0]}")
|
if n["num"] == iface.myInfo.my_node_num:
|
||||||
print("Print the hardware model for the local node.")
|
print(n["user"]["hwModel"])
|
||||||
return 3
|
iface.close()
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(description="Print local Meshtastic hardware model")
|
|
||||||
parser.parse_args()
|
|
||||||
|
|
||||||
try:
|
|
||||||
with meshtastic.serial_interface.SerialInterface() as iface:
|
|
||||||
if iface.nodes:
|
|
||||||
for node in iface.nodes.values():
|
|
||||||
if node["num"] == iface.myInfo.my_node_num:
|
|
||||||
print(node["user"]["hwModel"])
|
|
||||||
break
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
return 0
|
|
||||||
except Exception as exc:
|
|
||||||
print(f"Error: Could not read hardware model: {exc}")
|
|
||||||
return 1
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
raise SystemExit(main())
|
|
||||||
|
|||||||
@@ -1,38 +1,19 @@
|
|||||||
"""Send one text message over serial.
|
"""Simple program to demo how to use meshtastic library.
|
||||||
|
To run: python examples/hello_world_serial.py
|
||||||
Purpose: minimal send-only example.
|
|
||||||
Transport scope: Serial only.
|
|
||||||
Behavior: sends one message and exits.
|
|
||||||
Expected output: no output on success.
|
|
||||||
Cleanup/error handling: exits with code 3 for bad args, closes interface on exit.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import argparse
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
import meshtastic
|
||||||
import meshtastic.serial_interface
|
import meshtastic.serial_interface
|
||||||
|
|
||||||
|
# simple arg check
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
print(f"usage: {sys.argv[0]} message")
|
||||||
|
sys.exit(3)
|
||||||
|
|
||||||
def main() -> int:
|
# By default will try to find a meshtastic device,
|
||||||
"""Parse arguments and send one text message."""
|
# otherwise provide a device path like /dev/ttyUSB0
|
||||||
if len(sys.argv) < 2:
|
iface = meshtastic.serial_interface.SerialInterface()
|
||||||
print(f"usage: {sys.argv[0]} message")
|
iface.sendText(sys.argv[1])
|
||||||
return 3
|
iface.close()
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(description="Send one Meshtastic text message over serial")
|
|
||||||
parser.add_argument("message", help="Message text to broadcast")
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
try:
|
|
||||||
with meshtastic.serial_interface.SerialInterface() as iface:
|
|
||||||
iface.sendText(args.message)
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
return 0
|
|
||||||
except Exception as exc:
|
|
||||||
print(f"Error: Could not send message: {exc}")
|
|
||||||
return 1
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
raise SystemExit(main())
|
|
||||||
|
|||||||
@@ -1,46 +1,20 @@
|
|||||||
"""Show a concise local node summary over serial.
|
"""Simple program to demo how to use meshtastic library.
|
||||||
|
To run: python examples/info.py
|
||||||
Purpose: read local node identity and metadata in one place.
|
|
||||||
Transport scope: Serial only.
|
|
||||||
Behavior: reads node database, prints local node ID/name/hardware model.
|
|
||||||
Expected output: 1-3 summary lines describing the local node.
|
|
||||||
Cleanup/error handling: closes interface on exit and prints clear errors on failure.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import meshtastic
|
||||||
import meshtastic.serial_interface
|
import meshtastic.serial_interface
|
||||||
|
|
||||||
|
iface = meshtastic.serial_interface.SerialInterface()
|
||||||
|
|
||||||
def main() -> int:
|
# call showInfo() just to ensure values are populated
|
||||||
"""Print local node summary fields."""
|
# info = iface.showInfo()
|
||||||
try:
|
|
||||||
with meshtastic.serial_interface.SerialInterface() as iface:
|
|
||||||
local_num = iface.myInfo.my_node_num
|
|
||||||
local_node = None
|
|
||||||
if iface.nodes:
|
|
||||||
for node in iface.nodes.values():
|
|
||||||
if node["num"] == local_num:
|
|
||||||
local_node = node
|
|
||||||
break
|
|
||||||
|
|
||||||
if not local_node:
|
|
||||||
print(f"Local node not found in node database (node num: {local_num}).")
|
|
||||||
return 1
|
|
||||||
|
|
||||||
user = local_node.get("user", {})
|
|
||||||
print(f"Node number: {local_num}")
|
|
||||||
print(f"Node ID: {local_node.get('id', 'unknown')}")
|
|
||||||
print(
|
|
||||||
"Name: "
|
|
||||||
f"{user.get('longName', 'unknown')} ({user.get('shortName', 'unknown')})"
|
|
||||||
)
|
|
||||||
print(f"Hardware model: {user.get('hwModel', 'unknown')}")
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
return 0
|
|
||||||
except Exception as exc:
|
|
||||||
print(f"Error: Could not read local node summary: {exc}")
|
|
||||||
return 1
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if iface.nodes:
|
||||||
raise SystemExit(main())
|
for n in iface.nodes.values():
|
||||||
|
if n["num"] == iface.myInfo.my_node_num:
|
||||||
|
print(n["user"]["hwModel"])
|
||||||
|
break
|
||||||
|
|
||||||
|
iface.close()
|
||||||
|
|||||||
@@ -1,64 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""Passively monitor incoming text messages over serial.
|
|
||||||
|
|
||||||
Purpose: receive-only monitor for text messages.
|
|
||||||
Transport scope: Serial only.
|
|
||||||
Behavior: subscribes to text receive events and prints timestamp/channel/sender/text.
|
|
||||||
Expected output: one line per received text message.
|
|
||||||
Cleanup/error handling: graceful Ctrl+C exit and explicit connection errors.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import time
|
|
||||||
from datetime import datetime
|
|
||||||
from typing import Any, Optional
|
|
||||||
|
|
||||||
from pubsub import pub
|
|
||||||
import meshtastic.serial_interface
|
|
||||||
|
|
||||||
_TZ_NAME = time.tzname[time.localtime().tm_isdst > 0]
|
|
||||||
|
|
||||||
|
|
||||||
def on_receive(packet: dict[str, Any], interface: Any) -> None: # pylint: disable=unused-argument
|
|
||||||
"""Print a compact line for each received text packet."""
|
|
||||||
decoded = packet.get("decoded", {})
|
|
||||||
if decoded.get("portnum") != "TEXT_MESSAGE_APP":
|
|
||||||
return
|
|
||||||
|
|
||||||
message = decoded.get("text")
|
|
||||||
if not message:
|
|
||||||
return
|
|
||||||
|
|
||||||
channel_num = packet.get("channel", 0)
|
|
||||||
sender_id = packet.get("fromId", "unknown")
|
|
||||||
message_time = datetime.now().strftime(f"%a %b %d %Y %H:%M:%S {_TZ_NAME}")
|
|
||||||
print(f"{message_time} : {channel_num} : {sender_id} : {message}")
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
|
||||||
"""Connect over serial and print inbound text messages."""
|
|
||||||
parser = argparse.ArgumentParser(description="Read incoming Meshtastic text over serial")
|
|
||||||
parser.add_argument("--port", default=None, help="Serial port path (default: auto-detect)")
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
pub.subscribe(on_receive, "meshtastic.receive")
|
|
||||||
|
|
||||||
iface: Optional[meshtastic.serial_interface.SerialInterface] = None
|
|
||||||
try:
|
|
||||||
iface = meshtastic.serial_interface.SerialInterface(devPath=args.port)
|
|
||||||
print("Connected. Listening for text messages. Press Ctrl+C to exit.")
|
|
||||||
while True:
|
|
||||||
time.sleep(1)
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
return 0
|
|
||||||
except Exception as exc:
|
|
||||||
print(f"Error: Could not monitor serial messages: {exc}")
|
|
||||||
return 1
|
|
||||||
finally:
|
|
||||||
if iface:
|
|
||||||
iface.close()
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
raise SystemExit(main())
|
|
||||||
30
examples/pub_sub_example.py
Normal file
30
examples/pub_sub_example.py
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
"""Simple program to demo how to use meshtastic library.
|
||||||
|
To run: python examples/pub_sub_example.py
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from pubsub import pub
|
||||||
|
|
||||||
|
import meshtastic
|
||||||
|
import meshtastic.tcp_interface
|
||||||
|
|
||||||
|
# simple arg check
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
print(f"usage: {sys.argv[0]} host")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
def onConnection(interface, topic=pub.AUTO_TOPIC): # pylint: disable=unused-argument
|
||||||
|
"""This is called when we (re)connect to the radio."""
|
||||||
|
print(interface.myInfo)
|
||||||
|
interface.close()
|
||||||
|
|
||||||
|
|
||||||
|
pub.subscribe(onConnection, "meshtastic.connection.established")
|
||||||
|
|
||||||
|
try:
|
||||||
|
iface = meshtastic.tcp_interface.TCPInterface(sys.argv[1])
|
||||||
|
except:
|
||||||
|
print(f"Error: Could not connect to {sys.argv[1]}")
|
||||||
|
sys.exit(1)
|
||||||
39
examples/pub_sub_example2.py
Normal file
39
examples/pub_sub_example2.py
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
"""Simple program to demo how to use meshtastic library.
|
||||||
|
To run: python examples/pub_sub_example2.py
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
from pubsub import pub
|
||||||
|
|
||||||
|
import meshtastic
|
||||||
|
import meshtastic.tcp_interface
|
||||||
|
|
||||||
|
# simple arg check
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
print(f"usage: {sys.argv[0]} host")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
def onReceive(packet, interface): # pylint: disable=unused-argument
|
||||||
|
"""called when a packet arrives"""
|
||||||
|
print(f"Received: {packet}")
|
||||||
|
|
||||||
|
|
||||||
|
def onConnection(interface, topic=pub.AUTO_TOPIC): # pylint: disable=unused-argument
|
||||||
|
"""called when we (re)connect to the radio"""
|
||||||
|
# defaults to broadcast, specify a destination ID if you wish
|
||||||
|
interface.sendText("hello mesh")
|
||||||
|
|
||||||
|
|
||||||
|
pub.subscribe(onReceive, "meshtastic.receive")
|
||||||
|
pub.subscribe(onConnection, "meshtastic.connection.established")
|
||||||
|
try:
|
||||||
|
iface = meshtastic.tcp_interface.TCPInterface(hostname=sys.argv[1])
|
||||||
|
while True:
|
||||||
|
time.sleep(1000)
|
||||||
|
iface.close()
|
||||||
|
except Exception as ex:
|
||||||
|
print(f"Error: Could not connect to {sys.argv[1]} {ex}")
|
||||||
|
sys.exit(1)
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
"""Auto-reply to received text messages.
|
|
||||||
|
|
||||||
Purpose: demonstrate receive callback + generated reply flow.
|
|
||||||
Transport scope: Serial default, optional TCP/BLE.
|
|
||||||
Behavior: listens for text, prints message metadata, sends one reply per text message.
|
|
||||||
Expected output: "Connected..." plus message/reply lines while running.
|
|
||||||
Cleanup/error handling: clear connect failures and graceful Ctrl+C close.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import time
|
|
||||||
from typing import Any, Optional, Union
|
|
||||||
from pubsub import pub
|
|
||||||
import meshtastic.serial_interface
|
|
||||||
import meshtastic.tcp_interface
|
|
||||||
import meshtastic.ble_interface
|
|
||||||
from meshtastic.mesh_interface import MeshInterface
|
|
||||||
|
|
||||||
def onReceive(packet: dict, interface: MeshInterface) -> None:
|
|
||||||
"""Reply to every received packet with some info"""
|
|
||||||
text: Optional[str] = packet.get("decoded", {}).get("text")
|
|
||||||
if text:
|
|
||||||
rx_snr: Any = packet.get("rxSnr", "unknown")
|
|
||||||
hop_limit: Any = packet.get("hopLimit", "unknown")
|
|
||||||
print(f"message: {text}")
|
|
||||||
reply: str = f"got msg '{text}' with rxSnr: {rx_snr} and hopLimit: {hop_limit}"
|
|
||||||
print("Sending reply: ", reply)
|
|
||||||
interface.sendText(reply)
|
|
||||||
|
|
||||||
def onConnection(interface: MeshInterface, topic: Any = pub.AUTO_TOPIC) -> None: # pylint: disable=unused-argument
|
|
||||||
"""called when we (re)connect to the radio"""
|
|
||||||
print("Connected. Will auto-reply to all messages while running.")
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(description="Meshtastic Auto-Reply Feature Demo")
|
|
||||||
group = parser.add_mutually_exclusive_group()
|
|
||||||
group.add_argument("--host", help="Connect via TCP to this hostname or IP")
|
|
||||||
group.add_argument("--ble", help="Connect via BLE to this MAC address")
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
pub.subscribe(onReceive, "meshtastic.receive")
|
|
||||||
pub.subscribe(onConnection, "meshtastic.connection.established")
|
|
||||||
|
|
||||||
iface: Optional[Union[
|
|
||||||
meshtastic.tcp_interface.TCPInterface,
|
|
||||||
meshtastic.ble_interface.BLEInterface,
|
|
||||||
meshtastic.serial_interface.SerialInterface
|
|
||||||
]] = None
|
|
||||||
|
|
||||||
# defaults to serial, use --host for TCP or --ble for Bluetooth
|
|
||||||
try:
|
|
||||||
if args.host:
|
|
||||||
# note: timeout only applies after connection, not during the initial connect attempt
|
|
||||||
# TCPInterface.myConnect() calls socket.create_connection() without a timeout
|
|
||||||
iface = meshtastic.tcp_interface.TCPInterface(hostname=args.host, timeout=10)
|
|
||||||
elif args.ble:
|
|
||||||
iface = meshtastic.ble_interface.BLEInterface(address=args.ble, timeout=10)
|
|
||||||
else:
|
|
||||||
iface = meshtastic.serial_interface.SerialInterface(timeout=10)
|
|
||||||
except KeyboardInterrupt as exc:
|
|
||||||
raise SystemExit(0) from exc
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error: Could not connect. {e}")
|
|
||||||
raise SystemExit(1) from e
|
|
||||||
|
|
||||||
try:
|
|
||||||
while True:
|
|
||||||
time.sleep(1)
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
pass
|
|
||||||
finally:
|
|
||||||
if iface:
|
|
||||||
iface.close()
|
|
||||||
@@ -1,13 +1,7 @@
|
|||||||
"""Scan host serial hardware for supported Meshtastic devices.
|
"""Program to scan for hardware
|
||||||
|
To run: python examples/scan_for_devices.py
|
||||||
Purpose: host-side discovery without opening a radio session.
|
|
||||||
Transport scope: none (OS/device scanning only).
|
|
||||||
Behavior: scans vendor IDs, lists matched devices, and candidate active ports.
|
|
||||||
Expected output: vendor ID list, zero-or-more detected devices, and port list.
|
|
||||||
Cleanup/error handling: exits with code 3 for bad args and code 1 on scan errors.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import argparse
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from meshtastic.util import (
|
from meshtastic.util import (
|
||||||
@@ -16,38 +10,20 @@ from meshtastic.util import (
|
|||||||
get_unique_vendor_ids,
|
get_unique_vendor_ids,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# simple arg check
|
||||||
|
if len(sys.argv) != 1:
|
||||||
|
print(f"usage: {sys.argv[0]}")
|
||||||
|
print("Detect which device we might have.")
|
||||||
|
sys.exit(3)
|
||||||
|
|
||||||
def main() -> int:
|
vids = get_unique_vendor_ids()
|
||||||
"""Run device detection and print candidate ports."""
|
print(f"Searching for all devices with these vendor ids {vids}")
|
||||||
if len(sys.argv) != 1:
|
|
||||||
print(f"usage: {sys.argv[0]}")
|
|
||||||
print("Detect which device we might have.")
|
|
||||||
return 3
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(description="Scan host for supported Meshtastic devices")
|
sds = detect_supported_devices()
|
||||||
parser.parse_args()
|
if len(sds) > 0:
|
||||||
|
print("Detected possible devices:")
|
||||||
|
for d in sds:
|
||||||
|
print(f" name:{d.name}{d.version} firmware:{d.for_firmware}")
|
||||||
|
|
||||||
try:
|
ports = active_ports_on_supported_devices(sds)
|
||||||
vids = get_unique_vendor_ids()
|
print(f"ports:{ports}")
|
||||||
print(f"Searching for all devices with these vendor ids {vids}")
|
|
||||||
|
|
||||||
supported_devices = detect_supported_devices()
|
|
||||||
if supported_devices:
|
|
||||||
print("Detected possible devices:")
|
|
||||||
for device in supported_devices:
|
|
||||||
print(
|
|
||||||
f" name:{device.name}{device.version} firmware:{device.for_firmware}"
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
print("Detected possible devices: none")
|
|
||||||
|
|
||||||
ports = active_ports_on_supported_devices(supported_devices)
|
|
||||||
print(f"ports:{ports}")
|
|
||||||
except Exception as exc:
|
|
||||||
print(f"Error: device scan failed: {exc}")
|
|
||||||
return 1
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
raise SystemExit(main())
|
|
||||||
|
|||||||
@@ -1,40 +1,21 @@
|
|||||||
"""Set local owner long/short name over serial.
|
"""Simple program to demo how to use meshtastic library.
|
||||||
|
To run: python examples/set_owner.py Bobby 333
|
||||||
Purpose: demonstrate a local config mutation workflow.
|
|
||||||
Transport scope: Serial only.
|
|
||||||
Behavior: updates owner long name and optional short name.
|
|
||||||
Expected output: prints the owner values being applied.
|
|
||||||
Cleanup/error handling: exits with code 3 for bad args and closes interface on exit.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import argparse
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
import meshtastic
|
||||||
import meshtastic.serial_interface
|
import meshtastic.serial_interface
|
||||||
|
|
||||||
|
# simple arg check
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
print(f"usage: {sys.argv[0]} long_name [short_name]")
|
||||||
|
sys.exit(3)
|
||||||
|
|
||||||
def main() -> int:
|
iface = meshtastic.serial_interface.SerialInterface()
|
||||||
"""Parse args and set owner fields."""
|
long_name = sys.argv[1]
|
||||||
if len(sys.argv) < 2:
|
short_name = None
|
||||||
print(f"usage: {sys.argv[0]} long_name [short_name]")
|
if len(sys.argv) > 2:
|
||||||
return 3
|
short_name = sys.argv[2]
|
||||||
|
iface.localNode.setOwner(long_name, short_name)
|
||||||
parser = argparse.ArgumentParser(description="Set Meshtastic local owner information")
|
iface.close()
|
||||||
parser.add_argument("long_name", help="Owner long name")
|
|
||||||
parser.add_argument("short_name", nargs="?", default=None, help="Owner short name")
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
print(f"Setting owner long_name={args.long_name}, short_name={args.short_name}")
|
|
||||||
try:
|
|
||||||
with meshtastic.serial_interface.SerialInterface() as iface:
|
|
||||||
iface.localNode.setOwner(args.long_name, args.short_name)
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
return 0
|
|
||||||
except Exception as exc:
|
|
||||||
print(f"Error: Could not set owner: {exc}")
|
|
||||||
return 1
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
raise SystemExit(main())
|
|
||||||
|
|||||||
@@ -1,24 +1,6 @@
|
|||||||
"""List serial ports currently visible to Meshtastic helpers.
|
"""Simple program to show serial ports.
|
||||||
|
|
||||||
Purpose: fastest host-side serial port enumeration.
|
|
||||||
Transport scope: none (host serial listing only).
|
|
||||||
Behavior: prints result of `findPorts()`.
|
|
||||||
Expected output: list-like representation of available candidate ports.
|
|
||||||
Cleanup/error handling: exits with code 1 on unexpected scan error.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from meshtastic.util import findPorts
|
from meshtastic.util import findPorts
|
||||||
|
|
||||||
|
print(findPorts())
|
||||||
def main() -> int:
|
|
||||||
"""Print discovered serial ports."""
|
|
||||||
try:
|
|
||||||
print(findPorts())
|
|
||||||
except Exception as exc:
|
|
||||||
print(f"Error: Could not list ports: {exc}")
|
|
||||||
return 1
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
raise SystemExit(main())
|
|
||||||
|
|||||||
@@ -1,41 +0,0 @@
|
|||||||
"""Connect over TCP, print connection info once, then exit.
|
|
||||||
|
|
||||||
Purpose: demonstrate pubsub connection lifecycle callback.
|
|
||||||
Transport scope: TCP only.
|
|
||||||
Behavior: subscribe to `meshtastic.connection.established`, print `myInfo`, then close.
|
|
||||||
Expected output: one object/line showing local radio info after connect.
|
|
||||||
Cleanup/error handling: explicit connect failure message and clean close on callback.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
|
|
||||||
from pubsub import pub
|
|
||||||
import meshtastic.tcp_interface
|
|
||||||
|
|
||||||
|
|
||||||
def on_connection(interface, topic=pub.AUTO_TOPIC): # pylint: disable=unused-argument
|
|
||||||
"""Print local radio info when connected, then close."""
|
|
||||||
print(interface.myInfo)
|
|
||||||
interface.close()
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
|
||||||
"""Parse args, connect, and wait for established callback."""
|
|
||||||
parser = argparse.ArgumentParser(description="Print radio info on TCP connect and exit")
|
|
||||||
parser.add_argument("host", help="TCP hostname or IP of the Meshtastic node")
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
pub.subscribe(on_connection, "meshtastic.connection.established")
|
|
||||||
|
|
||||||
try:
|
|
||||||
meshtastic.tcp_interface.TCPInterface(args.host)
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
return 0
|
|
||||||
except Exception as exc:
|
|
||||||
print(f"Error: Could not connect to {args.host}: {exc}")
|
|
||||||
return 1
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
raise SystemExit(main())
|
|
||||||
@@ -1,47 +1,14 @@
|
|||||||
"""Look up local node position over TCP.
|
"""Demonstration of how to look up a radio's location via its LAN connection.
|
||||||
|
Before running, connect your machine to the same WiFi network as the radio.
|
||||||
Purpose: demonstrate read-only position lookup via LAN/TCP.
|
|
||||||
Transport scope: TCP only.
|
|
||||||
Behavior: connects, reads local node position, prints it, then exits.
|
|
||||||
Expected output: position dict for local node.
|
|
||||||
Cleanup/error handling: explicit connect/read failures and clean close.
|
|
||||||
"""
|
"""
|
||||||
# pylint: disable=duplicate-code
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
|
|
||||||
|
import meshtastic
|
||||||
import meshtastic.tcp_interface
|
import meshtastic.tcp_interface
|
||||||
|
|
||||||
|
radio_hostname = "meshtastic.local" # Can also be an IP
|
||||||
|
iface = meshtastic.tcp_interface.TCPInterface(radio_hostname)
|
||||||
|
my_node_num = iface.myInfo.my_node_num
|
||||||
|
pos = iface.nodesByNum[my_node_num]["position"]
|
||||||
|
print(pos)
|
||||||
|
|
||||||
def main() -> int:
|
iface.close()
|
||||||
"""Connect over TCP and print local node position."""
|
|
||||||
parser = argparse.ArgumentParser(description="Print local node position over TCP")
|
|
||||||
parser.add_argument(
|
|
||||||
"--host",
|
|
||||||
default="meshtastic.local",
|
|
||||||
help="TCP hostname or IP (default: meshtastic.local)",
|
|
||||||
)
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
iface = None
|
|
||||||
try:
|
|
||||||
iface = meshtastic.tcp_interface.TCPInterface(args.host)
|
|
||||||
my_node_num = iface.myInfo.my_node_num
|
|
||||||
pos = iface.nodesByNum[my_node_num].get("position")
|
|
||||||
if pos is None:
|
|
||||||
print(f"No position available for local node {my_node_num}")
|
|
||||||
return 1
|
|
||||||
print(pos)
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
return 0
|
|
||||||
except Exception as exc:
|
|
||||||
print(f"Error: Could not read position from {args.host}: {exc}")
|
|
||||||
return 1
|
|
||||||
finally:
|
|
||||||
if iface:
|
|
||||||
iface.close()
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
raise SystemExit(main())
|
|
||||||
|
|||||||
@@ -1,54 +0,0 @@
|
|||||||
"""Send once on connect and print received packets over TCP.
|
|
||||||
|
|
||||||
Purpose: demonstrate pubsub send-on-connect plus receive callback flow.
|
|
||||||
Transport scope: TCP only.
|
|
||||||
Behavior: sends "hello mesh" at connect, prints packets while running.
|
|
||||||
Expected output: "Connected..." plus "Received: ..." lines for inbound packets.
|
|
||||||
Cleanup/error handling: graceful Ctrl+C exit and clean interface close.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import time
|
|
||||||
|
|
||||||
from pubsub import pub
|
|
||||||
from meshtastic.tcp_interface import TCPInterface
|
|
||||||
|
|
||||||
|
|
||||||
def on_receive(packet, interface): # pylint: disable=unused-argument
|
|
||||||
"""Print each inbound packet."""
|
|
||||||
print(f"Received: {packet}")
|
|
||||||
|
|
||||||
|
|
||||||
def on_connection(interface, topic=pub.AUTO_TOPIC): # pylint: disable=unused-argument
|
|
||||||
"""Send a broadcast text when connected."""
|
|
||||||
print("Connected. Sending one broadcast message.")
|
|
||||||
interface.sendText("hello mesh")
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
|
||||||
"""Parse args, connect via TCP, and run callbacks."""
|
|
||||||
parser = argparse.ArgumentParser(description="TCP pubsub send-and-receive example")
|
|
||||||
parser.add_argument("host", help="TCP hostname or IP of the Meshtastic node")
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
pub.subscribe(on_receive, "meshtastic.receive")
|
|
||||||
pub.subscribe(on_connection, "meshtastic.connection.established")
|
|
||||||
|
|
||||||
iface = None
|
|
||||||
try:
|
|
||||||
iface = TCPInterface(hostname=args.host)
|
|
||||||
while True:
|
|
||||||
time.sleep(1)
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
return 0
|
|
||||||
except Exception as exc:
|
|
||||||
print(f"Error: Could not connect to {args.host}: {exc}")
|
|
||||||
return 1
|
|
||||||
finally:
|
|
||||||
if iface:
|
|
||||||
iface.close()
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
raise SystemExit(main())
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
"""Interactive text chat demo.
|
|
||||||
|
|
||||||
Purpose: demonstrate bidirectional text chat loop.
|
|
||||||
Transport scope: Serial default, optional TCP/BLE.
|
|
||||||
Behavior: prints incoming messages and sends each typed line as text.
|
|
||||||
Expected output: incoming sender/text lines and sent messages reaching peers.
|
|
||||||
Cleanup/error handling: explicit connect errors and graceful Ctrl+C / EOF close.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
from typing import Any, Optional, Union
|
|
||||||
from pubsub import pub
|
|
||||||
import meshtastic.serial_interface
|
|
||||||
import meshtastic.tcp_interface
|
|
||||||
import meshtastic.ble_interface
|
|
||||||
from meshtastic.mesh_interface import MeshInterface
|
|
||||||
|
|
||||||
def onReceive(packet: dict, interface: MeshInterface) -> None: # pylint: disable=unused-argument
|
|
||||||
"""called when a packet arrives"""
|
|
||||||
text: Optional[str] = packet.get("decoded", {}).get("text")
|
|
||||||
if text:
|
|
||||||
sender: str = packet.get("fromId", "unknown")
|
|
||||||
print(f"{sender}: {text}")
|
|
||||||
|
|
||||||
def onConnection(interface: MeshInterface, topic: Any = pub.AUTO_TOPIC) -> None: # pylint: disable=unused-argument
|
|
||||||
"""called when we (re)connect to the radio"""
|
|
||||||
print("Connected. Type a message and press Enter to send. Ctrl+C to exit.")
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(description="Meshtastic text chat demo")
|
|
||||||
group = parser.add_mutually_exclusive_group()
|
|
||||||
group.add_argument("--host", help="Connect via TCP to this hostname or IP")
|
|
||||||
group.add_argument("--ble", help="Connect via BLE to this MAC address or device name")
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
pub.subscribe(onReceive, "meshtastic.receive")
|
|
||||||
pub.subscribe(onConnection, "meshtastic.connection.established")
|
|
||||||
|
|
||||||
iface: Optional[Union[
|
|
||||||
meshtastic.tcp_interface.TCPInterface,
|
|
||||||
meshtastic.ble_interface.BLEInterface,
|
|
||||||
meshtastic.serial_interface.SerialInterface
|
|
||||||
]] = None
|
|
||||||
|
|
||||||
# defaults to serial, use --host for TCP or --ble for Bluetooth
|
|
||||||
try:
|
|
||||||
if args.host:
|
|
||||||
# note: timeout only applies after connection, not during the initial connect attempt
|
|
||||||
# TCPInterface.myConnect() calls socket.create_connection() without a timeout
|
|
||||||
iface = meshtastic.tcp_interface.TCPInterface(hostname=args.host, timeout=10)
|
|
||||||
elif args.ble:
|
|
||||||
iface = meshtastic.ble_interface.BLEInterface(address=args.ble, timeout=10)
|
|
||||||
else:
|
|
||||||
iface = meshtastic.serial_interface.SerialInterface(timeout=10)
|
|
||||||
except KeyboardInterrupt as exc:
|
|
||||||
raise SystemExit(0) from exc
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error: Could not connect. {e}")
|
|
||||||
raise SystemExit(1) from e
|
|
||||||
|
|
||||||
assert iface is not None
|
|
||||||
try:
|
|
||||||
while True:
|
|
||||||
line = input()
|
|
||||||
if line:
|
|
||||||
iface.sendText(line)
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
pass
|
|
||||||
except EOFError:
|
|
||||||
pass
|
|
||||||
finally:
|
|
||||||
if iface:
|
|
||||||
iface.close()
|
|
||||||
@@ -1,10 +1,7 @@
|
|||||||
"""Create or delete a waypoint.
|
"""Program to create and delete waypoint
|
||||||
|
To run:
|
||||||
Purpose: demonstrate waypoint mutation API (create/delete).
|
python3 examples/waypoint.py --port /dev/ttyUSB0 create 45 test the_desc_2 '2024-12-18T23:05:23' 48.74 7.35
|
||||||
Transport scope: Serial only.
|
python3 examples/waypoint.py delete 45
|
||||||
Behavior: sends waypoint create/delete request and prints API response.
|
|
||||||
Expected output: request response object printed to stdout.
|
|
||||||
Cleanup/error handling: explicit argument parsing and clean interface close.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
@@ -15,28 +12,25 @@ import meshtastic
|
|||||||
import meshtastic.serial_interface
|
import meshtastic.serial_interface
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
prog="waypoint", description="Create and delete Meshtastic waypoint"
|
prog='waypoint',
|
||||||
)
|
description='Create and delete Meshtastic waypoint')
|
||||||
parser.add_argument("--port", default=None)
|
parser.add_argument('--port', default=None)
|
||||||
parser.add_argument("--debug", default=False, action="store_true")
|
parser.add_argument('--debug', default=False, action='store_true')
|
||||||
|
|
||||||
subparsers = parser.add_subparsers(dest="cmd", required=True)
|
subparsers = parser.add_subparsers(dest='cmd')
|
||||||
parser_delete = subparsers.add_parser("delete", help="Delete a waypoint")
|
parser_delete = subparsers.add_parser('delete', help='Delete a waypoint')
|
||||||
parser_delete.add_argument("id", type=int, help="ID of the waypoint")
|
parser_delete.add_argument('id', help="id of the waypoint")
|
||||||
|
|
||||||
parser_create = subparsers.add_parser("create", help="Create a new waypoint")
|
parser_create = subparsers.add_parser('create', help='Create a new waypoint')
|
||||||
parser_create.add_argument("id", type=int, help="ID of the waypoint")
|
parser_create.add_argument('id', help="id of the waypoint")
|
||||||
parser_create.add_argument("name", help="Name of the waypoint")
|
parser_create.add_argument('name', help="name of the waypoint")
|
||||||
parser_create.add_argument("description", help="Description of the waypoint")
|
parser_create.add_argument('description', help="description of the waypoint")
|
||||||
parser_create.add_argument("icon", help="Icon of the waypoint")
|
parser_create.add_argument('expire', help="expiration date of the waypoint as interpreted by datetime.fromisoformat")
|
||||||
parser_create.add_argument(
|
parser_create.add_argument('latitude', help="latitude of the waypoint")
|
||||||
"expire",
|
parser_create.add_argument('longitude', help="longitude of the waypoint")
|
||||||
help="Expiration time as ISO timestamp accepted by datetime.fromisoformat",
|
|
||||||
)
|
|
||||||
parser_create.add_argument("latitude", type=float, help="Latitude of the waypoint")
|
|
||||||
parser_create.add_argument("longitude", type=float, help="Longitude of the waypoint")
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
print(args)
|
||||||
|
|
||||||
# By default will try to find a meshtastic device,
|
# By default will try to find a meshtastic device,
|
||||||
# otherwise provide a device path like /dev/ttyUSB0
|
# otherwise provide a device path like /dev/ttyUSB0
|
||||||
@@ -45,16 +39,17 @@ if args.debug:
|
|||||||
else:
|
else:
|
||||||
d = None
|
d = None
|
||||||
with meshtastic.serial_interface.SerialInterface(args.port, debugOut=d) as iface:
|
with meshtastic.serial_interface.SerialInterface(args.port, debugOut=d) as iface:
|
||||||
if args.cmd == "create":
|
if args.cmd == 'create':
|
||||||
p = iface.sendWaypoint(
|
p = iface.sendWaypoint(
|
||||||
waypoint_id=args.id,
|
waypoint_id=int(args.id),
|
||||||
name=args.name,
|
name=args.name,
|
||||||
description=args.description,
|
description=args.description,
|
||||||
icon=args.icon,
|
|
||||||
expire=int(datetime.datetime.fromisoformat(args.expire).timestamp()),
|
expire=int(datetime.datetime.fromisoformat(args.expire).timestamp()),
|
||||||
latitude=args.latitude,
|
latitude=float(args.latitude),
|
||||||
longitude=args.longitude,
|
longitude=float(args.longitude),
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
p = iface.deleteWaypoint(args.id)
|
p = iface.deleteWaypoint(int(args.id))
|
||||||
print(p)
|
print(p)
|
||||||
|
|
||||||
|
# iface.close()
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ type of packet, you should subscribe to the full topic name. If you want to see
|
|||||||
- `meshtastic.receive.data.portnum(packet)` (where portnum is an integer or well known PortNum enum)
|
- `meshtastic.receive.data.portnum(packet)` (where portnum is an integer or well known PortNum enum)
|
||||||
- `meshtastic.node.updated(node = NodeInfo)` - published when a node in the DB changes (appears, location changed, username changed, etc...)
|
- `meshtastic.node.updated(node = NodeInfo)` - published when a node in the DB changes (appears, location changed, username changed, etc...)
|
||||||
- `meshtastic.log.line(line)` - a raw unparsed log line from the radio
|
- `meshtastic.log.line(line)` - a raw unparsed log line from the radio
|
||||||
- `meshtastic.clientNotification(notification, interface) - a ClientNotification sent from the radio
|
|
||||||
|
|
||||||
We receive position, user, or data packets from the mesh. You probably only care about `meshtastic.receive.data`. The first argument for
|
We receive position, user, or data packets from the mesh. You probably only care about `meshtastic.receive.data`. The first argument for
|
||||||
that publish will be the packet. Text or binary data packets (from `sendData` or `sendText`) will both arrive this way. If you print packet
|
that publish will be the packet. Text or binary data packets (from `sendData` or `sendText`) will both arrive this way. If you print packet
|
||||||
@@ -129,7 +128,6 @@ NODELESS_WANT_CONFIG_ID = 69420
|
|||||||
|
|
||||||
publishingThread = DeferredExecution("publishing")
|
publishingThread = DeferredExecution("publishing")
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
class ResponseHandler(NamedTuple):
|
class ResponseHandler(NamedTuple):
|
||||||
"""A pending response callback, waiting for a response to one of our messages"""
|
"""A pending response callback, waiting for a response to one of our messages"""
|
||||||
@@ -161,53 +159,31 @@ def _onTextReceive(iface, asDict):
|
|||||||
#
|
#
|
||||||
# Usually btw this problem is caused by apps sending binary data but setting the payload type to
|
# Usually btw this problem is caused by apps sending binary data but setting the payload type to
|
||||||
# text.
|
# text.
|
||||||
logger.debug(f"in _onTextReceive() asDict:{asDict}")
|
logging.debug(f"in _onTextReceive() asDict:{asDict}")
|
||||||
try:
|
try:
|
||||||
asBytes = asDict["decoded"]["payload"]
|
asBytes = asDict["decoded"]["payload"]
|
||||||
asDict["decoded"]["text"] = asBytes.decode("utf-8")
|
asDict["decoded"]["text"] = asBytes.decode("utf-8")
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
logger.error(f"Malformatted utf8 in text message: {ex}")
|
logging.error(f"Malformatted utf8 in text message: {ex}")
|
||||||
_receiveInfoUpdate(iface, asDict)
|
_receiveInfoUpdate(iface, asDict)
|
||||||
|
|
||||||
|
|
||||||
def _onPositionReceive(iface, asDict):
|
def _onPositionReceive(iface, asDict):
|
||||||
"""Special auto parsing for received messages"""
|
"""Special auto parsing for received messages"""
|
||||||
logger.debug(f"in _onPositionReceive() asDict:{asDict}")
|
logging.debug(f"in _onPositionReceive() asDict:{asDict}")
|
||||||
if "decoded" in asDict:
|
if "decoded" in asDict:
|
||||||
if "position" in asDict["decoded"] and "from" in asDict:
|
if "position" in asDict["decoded"] and "from" in asDict:
|
||||||
p = asDict["decoded"]["position"]
|
p = asDict["decoded"]["position"]
|
||||||
logger.debug(f"p:{p}")
|
logging.debug(f"p:{p}")
|
||||||
p = iface._fixupPosition(p)
|
p = iface._fixupPosition(p)
|
||||||
logger.debug(f"after fixup p:{p}")
|
logging.debug(f"after fixup p:{p}")
|
||||||
# For the local node, only accept position updates with equal
|
# update node DB as needed
|
||||||
# or better precision. The local GPS is authoritative, and
|
iface._getOrCreateByNum(asDict["from"])["position"] = p
|
||||||
# low-precision echoes from the mesh (e.g., map reports relayed
|
|
||||||
# by other nodes) should not overwrite it.
|
|
||||||
# For remote nodes, always accept the latest position since
|
|
||||||
# any update from them reflects their current state.
|
|
||||||
node = iface._getOrCreateByNum(asDict["from"])
|
|
||||||
is_local_node = (
|
|
||||||
iface.myInfo is not None
|
|
||||||
and asDict["from"] == iface.myInfo.my_node_num
|
|
||||||
)
|
|
||||||
if is_local_node:
|
|
||||||
existing = node.get("position", {})
|
|
||||||
existing_precision = existing.get("precisionBits", 0) or 0
|
|
||||||
new_precision = p.get("precisionBits", 0) or 0
|
|
||||||
if existing_precision == 0 or new_precision >= existing_precision:
|
|
||||||
node["position"] = p
|
|
||||||
else:
|
|
||||||
logger.debug(
|
|
||||||
f"Ignoring low-precision position echo for local node "
|
|
||||||
f"({new_precision} < {existing_precision})"
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
node["position"] = p
|
|
||||||
|
|
||||||
|
|
||||||
def _onNodeInfoReceive(iface, asDict):
|
def _onNodeInfoReceive(iface, asDict):
|
||||||
"""Special auto parsing for received messages"""
|
"""Special auto parsing for received messages"""
|
||||||
logger.debug(f"in _onNodeInfoReceive() asDict:{asDict}")
|
logging.debug(f"in _onNodeInfoReceive() asDict:{asDict}")
|
||||||
if "decoded" in asDict:
|
if "decoded" in asDict:
|
||||||
if "user" in asDict["decoded"] and "from" in asDict:
|
if "user" in asDict["decoded"] and "from" in asDict:
|
||||||
p = asDict["decoded"]["user"]
|
p = asDict["decoded"]["user"]
|
||||||
@@ -221,7 +197,7 @@ def _onNodeInfoReceive(iface, asDict):
|
|||||||
|
|
||||||
def _onTelemetryReceive(iface, asDict):
|
def _onTelemetryReceive(iface, asDict):
|
||||||
"""Automatically update device metrics on received packets"""
|
"""Automatically update device metrics on received packets"""
|
||||||
logger.debug(f"in _onTelemetryReceive() asDict:{asDict}")
|
logging.debug(f"in _onTelemetryReceive() asDict:{asDict}")
|
||||||
if "from" not in asDict:
|
if "from" not in asDict:
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -245,7 +221,7 @@ def _onTelemetryReceive(iface, asDict):
|
|||||||
updateObj = telemetry.get(toUpdate)
|
updateObj = telemetry.get(toUpdate)
|
||||||
newMetrics = node.get(toUpdate, {})
|
newMetrics = node.get(toUpdate, {})
|
||||||
newMetrics.update(updateObj)
|
newMetrics.update(updateObj)
|
||||||
logger.debug(f"updating {toUpdate} metrics for {asDict['from']} to {newMetrics}")
|
logging.debug(f"updating {toUpdate} metrics for {asDict['from']} to {newMetrics}")
|
||||||
node[toUpdate] = newMetrics
|
node[toUpdate] = newMetrics
|
||||||
|
|
||||||
def _receiveInfoUpdate(iface, asDict):
|
def _receiveInfoUpdate(iface, asDict):
|
||||||
@@ -257,7 +233,7 @@ def _receiveInfoUpdate(iface, asDict):
|
|||||||
|
|
||||||
def _onAdminReceive(iface, asDict):
|
def _onAdminReceive(iface, asDict):
|
||||||
"""Special auto parsing for received messages"""
|
"""Special auto parsing for received messages"""
|
||||||
logger.debug(f"in _onAdminReceive() asDict:{asDict}")
|
logging.debug(f"in _onAdminReceive() asDict:{asDict}")
|
||||||
if "decoded" in asDict and "from" in asDict and "admin" in asDict["decoded"]:
|
if "decoded" in asDict and "from" in asDict and "admin" in asDict["decoded"]:
|
||||||
adminMessage = asDict["decoded"]["admin"]["raw"]
|
adminMessage = asDict["decoded"]["admin"]["raw"]
|
||||||
iface._getOrCreateByNum(asDict["from"])["adminSessionPassKey"] = adminMessage.session_passkey
|
iface._getOrCreateByNum(asDict["from"])["adminSessionPassKey"] = adminMessage.session_passkey
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import logging
|
import logging
|
||||||
import os
|
|
||||||
from typing import cast, List
|
from typing import cast, List
|
||||||
|
|
||||||
import dash_bootstrap_components as dbc # type: ignore[import-untyped]
|
import dash_bootstrap_components as dbc # type: ignore[import-untyped]
|
||||||
@@ -144,8 +143,8 @@ def create_dash(slog_path: str) -> Dash:
|
|||||||
"""
|
"""
|
||||||
app = Dash(external_stylesheets=[dbc.themes.BOOTSTRAP])
|
app = Dash(external_stylesheets=[dbc.themes.BOOTSTRAP])
|
||||||
|
|
||||||
dpwr = read_pandas(os.path.join(slog_path, "power.feather"))
|
dpwr = read_pandas(f"{slog_path}/power.feather")
|
||||||
dslog = read_pandas(os.path.join(slog_path, "slog.feather"))
|
dslog = read_pandas(f"{slog_path}/slog.feather")
|
||||||
|
|
||||||
pmon_raises = get_pmon_raises(dslog)
|
pmon_raises = get_pmon_raises(dslog)
|
||||||
|
|
||||||
@@ -191,7 +190,7 @@ def main():
|
|||||||
parser = create_argparser()
|
parser = create_argparser()
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
if not args.slog:
|
if not args.slog:
|
||||||
args.slog = os.path.join(root_dir(), "latest")
|
args.slog = f"{root_dir()}/latest"
|
||||||
|
|
||||||
app = create_dash(slog_path=args.slog)
|
app = create_dash(slog_path=args.slog)
|
||||||
port = 8051
|
port = 8051
|
||||||
|
|||||||
@@ -4,10 +4,9 @@ import asyncio
|
|||||||
import atexit
|
import atexit
|
||||||
import logging
|
import logging
|
||||||
import struct
|
import struct
|
||||||
import sys
|
|
||||||
import time
|
import time
|
||||||
import io
|
import io
|
||||||
from threading import Thread, Event
|
from threading import Thread
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
|
|
||||||
import google.protobuf
|
import google.protobuf
|
||||||
@@ -24,7 +23,6 @@ FROMRADIO_UUID = "2c55e69e-4993-11ed-b878-0242ac120002"
|
|||||||
FROMNUM_UUID = "ed9da18c-a800-4f66-a670-aa7547e34453"
|
FROMNUM_UUID = "ed9da18c-a800-4f66-a670-aa7547e34453"
|
||||||
LEGACY_LOGRADIO_UUID = "6c6fd238-78fa-436b-aacf-15c5be1ef2e2"
|
LEGACY_LOGRADIO_UUID = "6c6fd238-78fa-436b-aacf-15c5be1ef2e2"
|
||||||
LOGRADIO_UUID = "5a3d6e49-06e6-4423-9944-e9de8cdf9547"
|
LOGRADIO_UUID = "5a3d6e49-06e6-4423-9944-e9de8cdf9547"
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class BLEInterface(MeshInterface):
|
class BLEInterface(MeshInterface):
|
||||||
@@ -33,43 +31,32 @@ class BLEInterface(MeshInterface):
|
|||||||
class BLEError(Exception):
|
class BLEError(Exception):
|
||||||
"""An exception class for BLE errors."""
|
"""An exception class for BLE errors."""
|
||||||
|
|
||||||
DEVICE_NOT_FOUND = "device_not_found"
|
def __init__(
|
||||||
MULTIPLE_DEVICES = "multiple_devices"
|
|
||||||
READ_ERROR = "read_error"
|
|
||||||
WRITE_ERROR = "write_error"
|
|
||||||
UNKNOWN = "unknown"
|
|
||||||
|
|
||||||
def __init__(self, message: str, kind: str = UNKNOWN):
|
|
||||||
super().__init__(message)
|
|
||||||
self.kind = kind
|
|
||||||
|
|
||||||
def __init__( # pylint: disable=R0917
|
|
||||||
self,
|
self,
|
||||||
address: Optional[str],
|
address: Optional[str],
|
||||||
noProto: bool = False,
|
noProto: bool = False,
|
||||||
debugOut: Optional[io.TextIOWrapper]=None,
|
debugOut: Optional[io.TextIOWrapper]=None,
|
||||||
noNodes: bool = False,
|
noNodes: bool = False,
|
||||||
timeout: int = 300,
|
|
||||||
) -> None:
|
) -> None:
|
||||||
MeshInterface.__init__(
|
MeshInterface.__init__(
|
||||||
self, debugOut=debugOut, noProto=noProto, noNodes=noNodes, timeout=timeout
|
self, debugOut=debugOut, noProto=noProto, noNodes=noNodes
|
||||||
)
|
)
|
||||||
|
|
||||||
self.should_read = False
|
self.should_read = False
|
||||||
|
|
||||||
logger.debug("Threads starting")
|
logging.debug("Threads starting")
|
||||||
self._want_receive = True
|
self._want_receive = True
|
||||||
self._receiveThread: Optional[Thread] = Thread(
|
self._receiveThread: Optional[Thread] = Thread(
|
||||||
target=self._receiveFromRadioImpl, name="BLEReceive", daemon=True
|
target=self._receiveFromRadioImpl, name="BLEReceive", daemon=True
|
||||||
)
|
)
|
||||||
self._receiveThread.start()
|
self._receiveThread.start()
|
||||||
logger.debug("Threads running")
|
logging.debug("Threads running")
|
||||||
|
|
||||||
self.client: Optional[BLEClient] = None
|
self.client: Optional[BLEClient] = None
|
||||||
try:
|
try:
|
||||||
logger.debug(f"BLE connecting to: {address if address else 'any'}")
|
logging.debug(f"BLE connecting to: {address if address else 'any'}")
|
||||||
self.client = self.connect(address)
|
self.client = self.connect(address)
|
||||||
logger.debug("BLE connected")
|
logging.debug("BLE connected")
|
||||||
except BLEInterface.BLEError as e:
|
except BLEInterface.BLEError as e:
|
||||||
self.close()
|
self.close()
|
||||||
raise e
|
raise e
|
||||||
@@ -82,13 +69,13 @@ class BLEInterface(MeshInterface):
|
|||||||
if self.client.has_characteristic(LOGRADIO_UUID):
|
if self.client.has_characteristic(LOGRADIO_UUID):
|
||||||
self.client.start_notify(LOGRADIO_UUID, self.log_radio_handler)
|
self.client.start_notify(LOGRADIO_UUID, self.log_radio_handler)
|
||||||
|
|
||||||
logger.debug("Mesh configure starting")
|
logging.debug("Mesh configure starting")
|
||||||
self._startConfig()
|
self._startConfig()
|
||||||
if not self.noProto:
|
if not self.noProto:
|
||||||
self._waitConnected(timeout=60.0)
|
self._waitConnected(timeout=60.0)
|
||||||
self.waitForConfig()
|
self.waitForConfig()
|
||||||
|
|
||||||
logger.debug("Register FROMNUM notify callback")
|
logging.debug("Register FROMNUM notify callback")
|
||||||
self.client.start_notify(FROMNUM_UUID, self.from_num_handler)
|
self.client.start_notify(FROMNUM_UUID, self.from_num_handler)
|
||||||
|
|
||||||
# We MUST run atexit (if we can) because otherwise (at least on linux) the BLE device is not disconnected
|
# We MUST run atexit (if we can) because otherwise (at least on linux) the BLE device is not disconnected
|
||||||
@@ -96,23 +83,12 @@ class BLEInterface(MeshInterface):
|
|||||||
# Note: the on disconnected callback will call our self.close which will make us nicely wait for threads to exit
|
# Note: the on disconnected callback will call our self.close which will make us nicely wait for threads to exit
|
||||||
self._exit_handler = atexit.register(self.client.disconnect)
|
self._exit_handler = atexit.register(self.client.disconnect)
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
rep = f"BLEInterface(address={self.client.address if self.client else None!r}"
|
|
||||||
if self.debugOut is not None:
|
|
||||||
rep += f", debugOut={self.debugOut!r}"
|
|
||||||
if self.noProto:
|
|
||||||
rep += ", noProto=True"
|
|
||||||
if self.noNodes:
|
|
||||||
rep += ", noNodes=True"
|
|
||||||
rep += ")"
|
|
||||||
return rep
|
|
||||||
|
|
||||||
def from_num_handler(self, _, b: bytes) -> None: # pylint: disable=C0116
|
def from_num_handler(self, _, b: bytes) -> None: # pylint: disable=C0116
|
||||||
"""Handle callbacks for fromnum notify.
|
"""Handle callbacks for fromnum notify.
|
||||||
Note: this method does not need to be async because it is just setting a bool.
|
Note: this method does not need to be async because it is just setting a bool.
|
||||||
"""
|
"""
|
||||||
from_num = struct.unpack("<I", bytes(b))[0]
|
from_num = struct.unpack("<I", bytes(b))[0]
|
||||||
logger.debug(f"FROMNUM notify: {from_num}")
|
logging.debug(f"FROMNUM notify: {from_num}")
|
||||||
self.should_read = True
|
self.should_read = True
|
||||||
|
|
||||||
async def log_radio_handler(self, _, b): # pylint: disable=C0116
|
async def log_radio_handler(self, _, b): # pylint: disable=C0116
|
||||||
@@ -127,7 +103,7 @@ class BLEInterface(MeshInterface):
|
|||||||
)
|
)
|
||||||
self._handleLogLine(message)
|
self._handleLogLine(message)
|
||||||
except google.protobuf.message.DecodeError:
|
except google.protobuf.message.DecodeError:
|
||||||
logger.warning("Malformed LogRecord received. Skipping.")
|
logging.warning("Malformed LogRecord received. Skipping.")
|
||||||
|
|
||||||
async def legacy_log_radio_handler(self, _, b): # pylint: disable=C0116
|
async def legacy_log_radio_handler(self, _, b): # pylint: disable=C0116
|
||||||
log_radio = b.decode("utf-8").replace("\n", "")
|
log_radio = b.decode("utf-8").replace("\n", "")
|
||||||
@@ -137,7 +113,7 @@ class BLEInterface(MeshInterface):
|
|||||||
def scan() -> List[BLEDevice]:
|
def scan() -> List[BLEDevice]:
|
||||||
"""Scan for available BLE devices."""
|
"""Scan for available BLE devices."""
|
||||||
with BLEClient() as client:
|
with BLEClient() as client:
|
||||||
logger.info("Scanning for BLE devices (takes 10 seconds)...")
|
logging.info("Scanning for BLE devices (takes 10 seconds)...")
|
||||||
response = client.discover(
|
response = client.discover(
|
||||||
timeout=10, return_adv=True, service_uuids=[SERVICE_UUID]
|
timeout=10, return_adv=True, service_uuids=[SERVICE_UUID]
|
||||||
)
|
)
|
||||||
@@ -167,13 +143,11 @@ class BLEInterface(MeshInterface):
|
|||||||
|
|
||||||
if len(addressed_devices) == 0:
|
if len(addressed_devices) == 0:
|
||||||
raise BLEInterface.BLEError(
|
raise BLEInterface.BLEError(
|
||||||
f"No Meshtastic BLE peripheral with identifier or address '{address}' found. Try --ble-scan to find it.",
|
f"No Meshtastic BLE peripheral with identifier or address '{address}' found. Try --ble-scan to find it."
|
||||||
BLEInterface.BLEError.DEVICE_NOT_FOUND,
|
|
||||||
)
|
)
|
||||||
if len(addressed_devices) > 1:
|
if len(addressed_devices) > 1:
|
||||||
raise BLEInterface.BLEError(
|
raise BLEInterface.BLEError(
|
||||||
f"More than one Meshtastic BLE peripheral with identifier or address '{address}' found.",
|
f"More than one Meshtastic BLE peripheral with identifier or address '{address}' found."
|
||||||
BLEInterface.BLEError.MULTIPLE_DEVICES,
|
|
||||||
)
|
)
|
||||||
return addressed_devices[0]
|
return addressed_devices[0]
|
||||||
|
|
||||||
@@ -189,7 +163,7 @@ class BLEInterface(MeshInterface):
|
|||||||
|
|
||||||
# Bleak docs recommend always doing a scan before connecting (even if we know addr)
|
# Bleak docs recommend always doing a scan before connecting (even if we know addr)
|
||||||
device = self.find_device(address)
|
device = self.find_device(address)
|
||||||
client = BLEClient(device.address, disconnected_callback=lambda _: self.close())
|
client = BLEClient(device.address, disconnected_callback=lambda _: self.close)
|
||||||
client.connect()
|
client.connect()
|
||||||
client.discover()
|
client.discover()
|
||||||
return client
|
return client
|
||||||
@@ -201,32 +175,29 @@ class BLEInterface(MeshInterface):
|
|||||||
retries: int = 0
|
retries: int = 0
|
||||||
while self._want_receive:
|
while self._want_receive:
|
||||||
if self.client is None:
|
if self.client is None:
|
||||||
logger.debug(f"BLE client is None, shutting down")
|
logging.debug(f"BLE client is None, shutting down")
|
||||||
self._want_receive = False
|
self._want_receive = False
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
b = bytes(self.client.read_gatt_char(FROMRADIO_UUID))
|
b = bytes(self.client.read_gatt_char(FROMRADIO_UUID))
|
||||||
except BleakDBusError as e:
|
except BleakDBusError as e:
|
||||||
# Device disconnected probably, so end our read loop immediately
|
# Device disconnected probably, so end our read loop immediately
|
||||||
logger.debug(f"Device disconnected, shutting down {e}")
|
logging.debug(f"Device disconnected, shutting down {e}")
|
||||||
self._want_receive = False
|
self._want_receive = False
|
||||||
except BleakError as e:
|
except BleakError as e:
|
||||||
# We were definitely disconnected
|
# We were definitely disconnected
|
||||||
if "Not connected" in str(e):
|
if "Not connected" in str(e):
|
||||||
logger.debug(f"Device disconnected, shutting down {e}")
|
logging.debug(f"Device disconnected, shutting down {e}")
|
||||||
self._want_receive = False
|
self._want_receive = False
|
||||||
else:
|
else:
|
||||||
raise BLEInterface.BLEError(
|
raise BLEInterface.BLEError("Error reading BLE") from e
|
||||||
"Error reading BLE",
|
|
||||||
BLEInterface.BLEError.READ_ERROR,
|
|
||||||
) from e
|
|
||||||
if not b:
|
if not b:
|
||||||
if retries < 5:
|
if retries < 5:
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
retries += 1
|
retries += 1
|
||||||
continue
|
continue
|
||||||
break
|
break
|
||||||
logger.debug(f"FROMRADIO read: {b.hex()}")
|
logging.debug(f"FROMRADIO read: {b.hex()}")
|
||||||
self._handleFromRadio(b)
|
self._handleFromRadio(b)
|
||||||
else:
|
else:
|
||||||
time.sleep(0.01)
|
time.sleep(0.01)
|
||||||
@@ -234,7 +205,7 @@ class BLEInterface(MeshInterface):
|
|||||||
def _sendToRadioImpl(self, toRadio) -> None:
|
def _sendToRadioImpl(self, toRadio) -> None:
|
||||||
b: bytes = toRadio.SerializeToString()
|
b: bytes = toRadio.SerializeToString()
|
||||||
if b and self.client: # we silently ignore writes while we are shutting down
|
if b and self.client: # we silently ignore writes while we are shutting down
|
||||||
logger.debug(f"TORADIO write: {b.hex()}")
|
logging.debug(f"TORADIO write: {b.hex()}")
|
||||||
try:
|
try:
|
||||||
self.client.write_gatt_char(
|
self.client.write_gatt_char(
|
||||||
TORADIO_UUID, b, response=True
|
TORADIO_UUID, b, response=True
|
||||||
@@ -242,8 +213,7 @@ class BLEInterface(MeshInterface):
|
|||||||
# search Bleak src for org.bluez.Error.InProgress
|
# search Bleak src for org.bluez.Error.InProgress
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise BLEInterface.BLEError(
|
raise BLEInterface.BLEError(
|
||||||
"Error writing BLE (are you in the 'bluetooth' user group? did you enter the pairing PIN on your computer?)",
|
"Error writing BLE (are you in the 'bluetooth' user group? did you enter the pairing PIN on your computer?)"
|
||||||
BLEInterface.BLEError.WRITE_ERROR,
|
|
||||||
) from e
|
) from e
|
||||||
# Allow to propagate and then make sure we read
|
# Allow to propagate and then make sure we read
|
||||||
time.sleep(0.01)
|
time.sleep(0.01)
|
||||||
@@ -253,7 +223,7 @@ class BLEInterface(MeshInterface):
|
|||||||
try:
|
try:
|
||||||
MeshInterface.close(self)
|
MeshInterface.close(self)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error closing mesh interface: {e}")
|
logging.error(f"Error closing mesh interface: {e}")
|
||||||
|
|
||||||
if self._want_receive:
|
if self._want_receive:
|
||||||
self._want_receive = False # Tell the thread we want it to stop
|
self._want_receive = False # Tell the thread we want it to stop
|
||||||
@@ -275,16 +245,14 @@ class BLEClient:
|
|||||||
"""Client for managing connection to a BLE device"""
|
"""Client for managing connection to a BLE device"""
|
||||||
|
|
||||||
def __init__(self, address=None, **kwargs) -> None:
|
def __init__(self, address=None, **kwargs) -> None:
|
||||||
self._loop_ready = Event()
|
|
||||||
self._eventLoop = asyncio.new_event_loop()
|
self._eventLoop = asyncio.new_event_loop()
|
||||||
self._eventThread = Thread(
|
self._eventThread = Thread(
|
||||||
target=self._run_event_loop, name="BLEClient", daemon=True
|
target=self._run_event_loop, name="BLEClient", daemon=True
|
||||||
)
|
)
|
||||||
self._eventThread.start()
|
self._eventThread.start()
|
||||||
self._loop_ready.wait() # Wait for event loop to be running
|
|
||||||
|
|
||||||
if not address:
|
if not address:
|
||||||
logger.debug("No address provided - only discover method will work.")
|
logging.debug("No address provided - only discover method will work.")
|
||||||
return
|
return
|
||||||
|
|
||||||
self.bleak_client = BleakClient(address, **kwargs)
|
self.bleak_client = BleakClient(address, **kwargs)
|
||||||
@@ -325,28 +293,13 @@ class BLEClient:
|
|||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
def async_await(self, coro, timeout=None): # pylint: disable=C0116
|
def async_await(self, coro, timeout=None): # pylint: disable=C0116
|
||||||
"""Wait for async operation to complete.
|
return self.async_run(coro).result(timeout)
|
||||||
|
|
||||||
On macOS, CoreBluetooth requires occasional I/O operations for
|
|
||||||
callbacks to be properly delivered. The debug logging provides this
|
|
||||||
I/O when enabled, allowing the system to process pending callbacks.
|
|
||||||
"""
|
|
||||||
logger.debug(f"async_await: waiting for {coro}")
|
|
||||||
future = self.async_run(coro)
|
|
||||||
# On macOS without debug logging, callbacks may not be delivered
|
|
||||||
# unless we trigger some I/O. This is a known quirk of CoreBluetooth.
|
|
||||||
sys.stdout.flush()
|
|
||||||
result = future.result(timeout)
|
|
||||||
logger.debug("async_await: complete")
|
|
||||||
return result
|
|
||||||
|
|
||||||
def async_run(self, coro): # pylint: disable=C0116
|
def async_run(self, coro): # pylint: disable=C0116
|
||||||
return asyncio.run_coroutine_threadsafe(coro, self._eventLoop)
|
return asyncio.run_coroutine_threadsafe(coro, self._eventLoop)
|
||||||
|
|
||||||
def _run_event_loop(self):
|
def _run_event_loop(self):
|
||||||
try:
|
try:
|
||||||
# Signal ready from WITHIN the loop to guarantee it's actually running
|
|
||||||
self._eventLoop.call_soon(self._loop_ready.set)
|
|
||||||
self._eventLoop.run_forever()
|
self._eventLoop.run_forever()
|
||||||
finally:
|
finally:
|
||||||
self._eventLoop.close()
|
self._eventLoop.close()
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"""Mesh Interface class
|
"""Mesh Interface class
|
||||||
"""
|
"""
|
||||||
# pylint: disable=R0917,C0302
|
# pylint: disable=R0917
|
||||||
|
|
||||||
import collections
|
import collections
|
||||||
import json
|
import json
|
||||||
@@ -17,7 +17,6 @@ from decimal import Decimal
|
|||||||
from typing import Any, Callable, Dict, List, Optional, Union
|
from typing import Any, Callable, Dict, List, Optional, Union
|
||||||
|
|
||||||
import google.protobuf.json_format
|
import google.protobuf.json_format
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import print_color # type: ignore[import-untyped]
|
import print_color # type: ignore[import-untyped]
|
||||||
except ImportError as e:
|
except ImportError as e:
|
||||||
@@ -47,7 +46,6 @@ from meshtastic.util import (
|
|||||||
stripnl,
|
stripnl,
|
||||||
)
|
)
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
def _timeago(delta_secs: int) -> str:
|
def _timeago(delta_secs: int) -> str:
|
||||||
"""Convert a number of seconds in the past into a short, friendly string
|
"""Convert a number of seconds in the past into a short, friendly string
|
||||||
@@ -90,7 +88,7 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
super().__init__(self.message)
|
super().__init__(self.message)
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, debugOut=None, noProto: bool = False, noNodes: bool = False, timeout: int = 300
|
self, debugOut=None, noProto: bool = False, noNodes: bool = False
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Constructor
|
"""Constructor
|
||||||
|
|
||||||
@@ -99,14 +97,13 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
link - just be a dumb serial client.
|
link - just be a dumb serial client.
|
||||||
noNodes -- If True, instruct the node to not send its nodedb
|
noNodes -- If True, instruct the node to not send its nodedb
|
||||||
on startup, just other configuration information.
|
on startup, just other configuration information.
|
||||||
timeout -- How long to wait for replies (default: 300 seconds)
|
|
||||||
"""
|
"""
|
||||||
self.debugOut = debugOut
|
self.debugOut = debugOut
|
||||||
self.nodes: Optional[Dict[str, Dict]] = None # FIXME
|
self.nodes: Optional[Dict[str, Dict]] = None # FIXME
|
||||||
self.isConnected: threading.Event = threading.Event()
|
self.isConnected: threading.Event = threading.Event()
|
||||||
self.noProto: bool = noProto
|
self.noProto: bool = noProto
|
||||||
self.localNode: meshtastic.node.Node = meshtastic.node.Node(
|
self.localNode: meshtastic.node.Node = meshtastic.node.Node(
|
||||||
self, -1, timeout=timeout
|
self, -1
|
||||||
) # We fixup nodenum later
|
) # We fixup nodenum later
|
||||||
self.myInfo: Optional[
|
self.myInfo: Optional[
|
||||||
mesh_pb2.MyNodeInfo
|
mesh_pb2.MyNodeInfo
|
||||||
@@ -120,7 +117,7 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
self.failure = (
|
self.failure = (
|
||||||
None # If we've encountered a fatal exception it will be kept here
|
None # If we've encountered a fatal exception it will be kept here
|
||||||
)
|
)
|
||||||
self._timeout: Timeout = Timeout(maxSecs=timeout)
|
self._timeout: Timeout = Timeout()
|
||||||
self._acknowledgment: Acknowledgment = Acknowledgment()
|
self._acknowledgment: Acknowledgment = Acknowledgment()
|
||||||
self.heartbeatTimer: Optional[threading.Timer] = None
|
self.heartbeatTimer: Optional[threading.Timer] = None
|
||||||
random.seed() # FIXME, we should not clobber the random seedval here, instead tell user they must call it
|
random.seed() # FIXME, we should not clobber the random seedval here, instead tell user they must call it
|
||||||
@@ -152,11 +149,11 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
|
|
||||||
def __exit__(self, exc_type, exc_value, trace):
|
def __exit__(self, exc_type, exc_value, trace):
|
||||||
if exc_type is not None and exc_value is not None:
|
if exc_type is not None and exc_value is not None:
|
||||||
logger.error(
|
logging.error(
|
||||||
f"An exception of type {exc_type} with value {exc_value} has occurred"
|
f"An exception of type {exc_type} with value {exc_value} has occurred"
|
||||||
)
|
)
|
||||||
if trace is not None:
|
if trace is not None:
|
||||||
logger.error(f"Traceback:\n{''.join(traceback.format_tb(trace))}")
|
logging.error(f"Traceback: {trace}")
|
||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -253,7 +250,6 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
"channel": "Channel",
|
"channel": "Channel",
|
||||||
"lastHeard": "LastHeard",
|
"lastHeard": "LastHeard",
|
||||||
"since": "Since",
|
"since": "Since",
|
||||||
"isFavorite": "Fav",
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -285,7 +281,7 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
|
|
||||||
def getNestedValue(node_dict: Dict[str, Any], key_path: str) -> Any:
|
def getNestedValue(node_dict: Dict[str, Any], key_path: str) -> Any:
|
||||||
if key_path.index(".") < 0:
|
if key_path.index(".") < 0:
|
||||||
logger.debug("getNestedValue was called without a nested path.")
|
logging.debug("getNestedValue was called without a nested path.")
|
||||||
return None
|
return None
|
||||||
keys = key_path.split(".")
|
keys = key_path.split(".")
|
||||||
value: Optional[Union[str, dict]] = node_dict
|
value: Optional[Union[str, dict]] = node_dict
|
||||||
@@ -301,14 +297,14 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
showFields = ["N", "user.longName", "user.id", "user.shortName", "user.hwModel", "user.publicKey",
|
showFields = ["N", "user.longName", "user.id", "user.shortName", "user.hwModel", "user.publicKey",
|
||||||
"user.role", "position.latitude", "position.longitude", "position.altitude",
|
"user.role", "position.latitude", "position.longitude", "position.altitude",
|
||||||
"deviceMetrics.batteryLevel", "deviceMetrics.channelUtilization",
|
"deviceMetrics.batteryLevel", "deviceMetrics.channelUtilization",
|
||||||
"deviceMetrics.airUtilTx", "snr", "hopsAway", "channel", "isFavorite", "lastHeard", "since"]
|
"deviceMetrics.airUtilTx", "snr", "hopsAway", "channel", "lastHeard", "since"]
|
||||||
else:
|
else:
|
||||||
# Always at least include the row number.
|
# Always at least include the row number.
|
||||||
showFields.insert(0, "N")
|
showFields.insert(0, "N")
|
||||||
|
|
||||||
rows: List[Dict[str, Any]] = []
|
rows: List[Dict[str, Any]] = []
|
||||||
if self.nodesByNum:
|
if self.nodesByNum:
|
||||||
logger.debug(f"self.nodes:{self.nodes}")
|
logging.debug(f"self.nodes:{self.nodes}")
|
||||||
for node in self.nodesByNum.values():
|
for node in self.nodesByNum.values():
|
||||||
if not includeSelf and node["num"] == self.localNode.nodeNum:
|
if not includeSelf and node["num"] == self.localNode.nodeNum:
|
||||||
continue
|
continue
|
||||||
@@ -343,8 +339,6 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
formatted_value = "Powered"
|
formatted_value = "Powered"
|
||||||
else:
|
else:
|
||||||
formatted_value = formatFloat(raw_value, 0, "%")
|
formatted_value = formatFloat(raw_value, 0, "%")
|
||||||
elif field == "isFavorite":
|
|
||||||
formatted_value = "*" if raw_value else ""
|
|
||||||
elif field == "lastHeard":
|
elif field == "lastHeard":
|
||||||
formatted_value = getLH(raw_value)
|
formatted_value = getLH(raw_value)
|
||||||
elif field == "position.latitude":
|
elif field == "position.latitude":
|
||||||
@@ -389,7 +383,7 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
n = meshtastic.node.Node(self, nodeId, timeout=timeout)
|
n = meshtastic.node.Node(self, nodeId, timeout=timeout)
|
||||||
# Only request device settings and channel info when necessary
|
# Only request device settings and channel info when necessary
|
||||||
if requestChannels:
|
if requestChannels:
|
||||||
logger.debug("About to requestChannels")
|
logging.debug("About to requestChannels")
|
||||||
n.requestChannels()
|
n.requestChannels()
|
||||||
retries_left = requestChannelAttempts
|
retries_left = requestChannelAttempts
|
||||||
last_index: int = 0
|
last_index: int = 0
|
||||||
@@ -417,9 +411,7 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
wantResponse: bool = False,
|
wantResponse: bool = False,
|
||||||
onResponse: Optional[Callable[[dict], Any]] = None,
|
onResponse: Optional[Callable[[dict], Any]] = None,
|
||||||
channelIndex: int = 0,
|
channelIndex: int = 0,
|
||||||
portNum: portnums_pb2.PortNum.ValueType = portnums_pb2.PortNum.TEXT_MESSAGE_APP,
|
portNum: portnums_pb2.PortNum.ValueType = portnums_pb2.PortNum.TEXT_MESSAGE_APP
|
||||||
replyId: Optional[int]=None,
|
|
||||||
hopLimit: Optional[int]=None,
|
|
||||||
):
|
):
|
||||||
"""Send a utf8 string to some other node, if the node has a display it
|
"""Send a utf8 string to some other node, if the node has a display it
|
||||||
will also be shown on the device.
|
will also be shown on the device.
|
||||||
@@ -436,8 +428,6 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
send an application layer response
|
send an application layer response
|
||||||
portNum -- the application portnum (similar to IP port numbers)
|
portNum -- the application portnum (similar to IP port numbers)
|
||||||
of the destination, see portnums.proto for a list
|
of the destination, see portnums.proto for a list
|
||||||
replyId -- the ID of the message that this packet is a response to
|
|
||||||
hopLimit {int} -- hop limit to use
|
|
||||||
|
|
||||||
Returns the sent packet. The id field will be populated in this packet
|
Returns the sent packet. The id field will be populated in this packet
|
||||||
and can be used to track future message acks/naks.
|
and can be used to track future message acks/naks.
|
||||||
@@ -451,8 +441,6 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
wantResponse=wantResponse,
|
wantResponse=wantResponse,
|
||||||
onResponse=onResponse,
|
onResponse=onResponse,
|
||||||
channelIndex=channelIndex,
|
channelIndex=channelIndex,
|
||||||
replyId=replyId,
|
|
||||||
hopLimit=hopLimit,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -462,7 +450,6 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
destinationId: Union[int, str] = BROADCAST_ADDR,
|
destinationId: Union[int, str] = BROADCAST_ADDR,
|
||||||
onResponse: Optional[Callable[[dict], Any]] = None,
|
onResponse: Optional[Callable[[dict], Any]] = None,
|
||||||
channelIndex: int = 0,
|
channelIndex: int = 0,
|
||||||
hopLimit: Optional[int]=None,
|
|
||||||
):
|
):
|
||||||
"""Send an alert text to some other node. This is similar to a text message,
|
"""Send an alert text to some other node. This is similar to a text message,
|
||||||
but carries a higher priority and is capable of generating special notifications
|
but carries a higher priority and is capable of generating special notifications
|
||||||
@@ -474,7 +461,6 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
Keyword Arguments:
|
Keyword Arguments:
|
||||||
destinationId {nodeId or nodeNum} -- where to send this
|
destinationId {nodeId or nodeNum} -- where to send this
|
||||||
message (default: {BROADCAST_ADDR})
|
message (default: {BROADCAST_ADDR})
|
||||||
hopLimit {int} -- hop limit to use
|
|
||||||
|
|
||||||
Returns the sent packet. The id field will be populated in this packet
|
Returns the sent packet. The id field will be populated in this packet
|
||||||
and can be used to track future message acks/naks.
|
and can be used to track future message acks/naks.
|
||||||
@@ -488,22 +474,9 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
wantResponse=False,
|
wantResponse=False,
|
||||||
onResponse=onResponse,
|
onResponse=onResponse,
|
||||||
channelIndex=channelIndex,
|
channelIndex=channelIndex,
|
||||||
priority=mesh_pb2.MeshPacket.Priority.ALERT,
|
priority=mesh_pb2.MeshPacket.Priority.ALERT
|
||||||
hopLimit=hopLimit,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def sendMqttClientProxyMessage(self, topic: str, data: bytes):
|
|
||||||
"""Send an MQTT Client Proxy message to the radio.
|
|
||||||
|
|
||||||
Topic and data should be the MQTT topic and the message
|
|
||||||
payload from an MQTT broker, respectively."""
|
|
||||||
prox = mesh_pb2.MqttClientProxyMessage()
|
|
||||||
prox.topic = topic
|
|
||||||
prox.data = data
|
|
||||||
toRadio = mesh_pb2.ToRadio()
|
|
||||||
toRadio.mqttClientProxyMessage.CopyFrom(prox)
|
|
||||||
self._sendToRadio(toRadio)
|
|
||||||
|
|
||||||
def sendData(
|
def sendData(
|
||||||
self,
|
self,
|
||||||
data,
|
data,
|
||||||
@@ -518,7 +491,6 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
pkiEncrypted: Optional[bool]=False,
|
pkiEncrypted: Optional[bool]=False,
|
||||||
publicKey: Optional[bytes]=None,
|
publicKey: Optional[bytes]=None,
|
||||||
priority: mesh_pb2.MeshPacket.Priority.ValueType=mesh_pb2.MeshPacket.Priority.RELIABLE,
|
priority: mesh_pb2.MeshPacket.Priority.ValueType=mesh_pb2.MeshPacket.Priority.RELIABLE,
|
||||||
replyId: Optional[int]=None,
|
|
||||||
): # pylint: disable=R0913
|
): # pylint: disable=R0913
|
||||||
"""Send a data packet to some other node
|
"""Send a data packet to some other node
|
||||||
|
|
||||||
@@ -543,18 +515,17 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
will implicitly be true.
|
will implicitly be true.
|
||||||
channelIndex -- channel number to use
|
channelIndex -- channel number to use
|
||||||
hopLimit -- hop limit to use
|
hopLimit -- hop limit to use
|
||||||
replyId -- the ID of the message that this packet is a response to
|
|
||||||
|
|
||||||
Returns the sent packet. The id field will be populated in this packet
|
Returns the sent packet. The id field will be populated in this packet
|
||||||
and can be used to track future message acks/naks.
|
and can be used to track future message acks/naks.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if getattr(data, "SerializeToString", None):
|
if getattr(data, "SerializeToString", None):
|
||||||
logger.debug(f"Serializing protobuf as data: {stripnl(data)}")
|
logging.debug(f"Serializing protobuf as data: {stripnl(data)}")
|
||||||
data = data.SerializeToString()
|
data = data.SerializeToString()
|
||||||
|
|
||||||
logger.debug(f"len(data): {len(data)}")
|
logging.debug(f"len(data): {len(data)}")
|
||||||
logger.debug(
|
logging.debug(
|
||||||
f"mesh_pb2.Constants.DATA_PAYLOAD_LEN: {mesh_pb2.Constants.DATA_PAYLOAD_LEN}"
|
f"mesh_pb2.Constants.DATA_PAYLOAD_LEN: {mesh_pb2.Constants.DATA_PAYLOAD_LEN}"
|
||||||
)
|
)
|
||||||
if len(data) > mesh_pb2.Constants.DATA_PAYLOAD_LEN:
|
if len(data) > mesh_pb2.Constants.DATA_PAYLOAD_LEN:
|
||||||
@@ -571,13 +542,11 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
meshPacket.decoded.portnum = portNum
|
meshPacket.decoded.portnum = portNum
|
||||||
meshPacket.decoded.want_response = wantResponse
|
meshPacket.decoded.want_response = wantResponse
|
||||||
meshPacket.id = self._generatePacketId()
|
meshPacket.id = self._generatePacketId()
|
||||||
if replyId is not None:
|
|
||||||
meshPacket.decoded.reply_id = replyId
|
|
||||||
if priority is not None:
|
if priority is not None:
|
||||||
meshPacket.priority = priority
|
meshPacket.priority = priority
|
||||||
|
|
||||||
if onResponse is not None:
|
if onResponse is not None:
|
||||||
logger.debug(f"Setting a response handler for requestId {meshPacket.id}")
|
logging.debug(f"Setting a response handler for requestId {meshPacket.id}")
|
||||||
self._addResponseHandler(meshPacket.id, onResponse, ackPermitted=onResponseAckPermitted)
|
self._addResponseHandler(meshPacket.id, onResponse, ackPermitted=onResponseAckPermitted)
|
||||||
p = self._sendPacket(meshPacket, destinationId, wantAck=wantAck, hopLimit=hopLimit, pkiEncrypted=pkiEncrypted, publicKey=publicKey)
|
p = self._sendPacket(meshPacket, destinationId, wantAck=wantAck, hopLimit=hopLimit, pkiEncrypted=pkiEncrypted, publicKey=publicKey)
|
||||||
return p
|
return p
|
||||||
@@ -591,7 +560,6 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
wantAck: bool = False,
|
wantAck: bool = False,
|
||||||
wantResponse: bool = False,
|
wantResponse: bool = False,
|
||||||
channelIndex: int = 0,
|
channelIndex: int = 0,
|
||||||
hopLimit: Optional[int]=None,
|
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Send a position packet to some other node (normally a broadcast)
|
Send a position packet to some other node (normally a broadcast)
|
||||||
@@ -605,15 +573,15 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
p = mesh_pb2.Position()
|
p = mesh_pb2.Position()
|
||||||
if latitude != 0.0:
|
if latitude != 0.0:
|
||||||
p.latitude_i = int(latitude / 1e-7)
|
p.latitude_i = int(latitude / 1e-7)
|
||||||
logger.debug(f"p.latitude_i:{p.latitude_i}")
|
logging.debug(f"p.latitude_i:{p.latitude_i}")
|
||||||
|
|
||||||
if longitude != 0.0:
|
if longitude != 0.0:
|
||||||
p.longitude_i = int(longitude / 1e-7)
|
p.longitude_i = int(longitude / 1e-7)
|
||||||
logger.debug(f"p.longitude_i:{p.longitude_i}")
|
logging.debug(f"p.longitude_i:{p.longitude_i}")
|
||||||
|
|
||||||
if altitude != 0:
|
if altitude != 0:
|
||||||
p.altitude = int(altitude)
|
p.altitude = int(altitude)
|
||||||
logger.debug(f"p.altitude:{p.altitude}")
|
logging.debug(f"p.altitude:{p.altitude}")
|
||||||
|
|
||||||
if wantResponse:
|
if wantResponse:
|
||||||
onResponse = self.onResponsePosition
|
onResponse = self.onResponsePosition
|
||||||
@@ -628,7 +596,6 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
wantResponse=wantResponse,
|
wantResponse=wantResponse,
|
||||||
onResponse=onResponse,
|
onResponse=onResponse,
|
||||||
channelIndex=channelIndex,
|
channelIndex=channelIndex,
|
||||||
hopLimit=hopLimit,
|
|
||||||
)
|
)
|
||||||
if wantResponse:
|
if wantResponse:
|
||||||
self.waitForPosition()
|
self.waitForPosition()
|
||||||
@@ -681,19 +648,11 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
hopLimit=hopLimit,
|
hopLimit=hopLimit,
|
||||||
)
|
)
|
||||||
# extend timeout based on number of nodes, limit by configured hopLimit
|
# extend timeout based on number of nodes, limit by configured hopLimit
|
||||||
nodes_based_factor = (len(self.nodes) - 1) if self.nodes else (hopLimit + 1)
|
waitFactor = min(len(self.nodes) - 1 if self.nodes else 0, hopLimit)
|
||||||
waitFactor = max(1, min(nodes_based_factor, hopLimit + 1))
|
|
||||||
self.waitForTraceRoute(waitFactor)
|
self.waitForTraceRoute(waitFactor)
|
||||||
|
|
||||||
def onResponseTraceRoute(self, p: dict):
|
def onResponseTraceRoute(self, p: dict):
|
||||||
"""on response for trace route"""
|
"""on response for trace route"""
|
||||||
if p["decoded"]["portnum"] == "ROUTING_APP":
|
|
||||||
error = p["decoded"]["routing"]["errorReason"]
|
|
||||||
if error != "NONE":
|
|
||||||
print(f"Traceroute failed: {error}")
|
|
||||||
self._acknowledgment.receivedTraceRoute = True
|
|
||||||
return
|
|
||||||
|
|
||||||
UNK_SNR = -128 # Value representing unknown SNR
|
UNK_SNR = -128 # Value representing unknown SNR
|
||||||
|
|
||||||
routeDiscovery = mesh_pb2.RouteDiscovery()
|
routeDiscovery = mesh_pb2.RouteDiscovery()
|
||||||
@@ -742,8 +701,7 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
destinationId: Union[int, str] = BROADCAST_ADDR,
|
destinationId: Union[int, str] = BROADCAST_ADDR,
|
||||||
wantResponse: bool = False,
|
wantResponse: bool = False,
|
||||||
channelIndex: int = 0,
|
channelIndex: int = 0,
|
||||||
telemetryType: str = "device_metrics",
|
telemetryType: str = "device_metrics"
|
||||||
hopLimit: Optional[int]=None,
|
|
||||||
):
|
):
|
||||||
"""Send telemetry and optionally ask for a response"""
|
"""Send telemetry and optionally ask for a response"""
|
||||||
r = telemetry_pb2.Telemetry()
|
r = telemetry_pb2.Telemetry()
|
||||||
@@ -790,7 +748,6 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
wantResponse=wantResponse,
|
wantResponse=wantResponse,
|
||||||
onResponse=onResponse,
|
onResponse=onResponse,
|
||||||
channelIndex=channelIndex,
|
channelIndex=channelIndex,
|
||||||
hopLimit=hopLimit,
|
|
||||||
)
|
)
|
||||||
if wantResponse:
|
if wantResponse:
|
||||||
self.waitForTelemetry()
|
self.waitForTelemetry()
|
||||||
@@ -851,7 +808,6 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
self,
|
self,
|
||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
icon,
|
|
||||||
expire: int,
|
expire: int,
|
||||||
waypoint_id: Optional[int] = None,
|
waypoint_id: Optional[int] = None,
|
||||||
latitude: float = 0.0,
|
latitude: float = 0.0,
|
||||||
@@ -860,7 +816,6 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
wantAck: bool = True,
|
wantAck: bool = True,
|
||||||
wantResponse: bool = False,
|
wantResponse: bool = False,
|
||||||
channelIndex: int = 0,
|
channelIndex: int = 0,
|
||||||
hopLimit: Optional[int]=None,
|
|
||||||
): # pylint: disable=R0913
|
): # pylint: disable=R0913
|
||||||
"""
|
"""
|
||||||
Send a waypoint packet to some other node (normally a broadcast)
|
Send a waypoint packet to some other node (normally a broadcast)
|
||||||
@@ -871,22 +826,21 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
w = mesh_pb2.Waypoint()
|
w = mesh_pb2.Waypoint()
|
||||||
w.name = name
|
w.name = name
|
||||||
w.description = description
|
w.description = description
|
||||||
w.icon = icon
|
|
||||||
w.expire = expire
|
w.expire = expire
|
||||||
if waypoint_id is None:
|
if waypoint_id is None:
|
||||||
# Generate a waypoint's id, NOT a packet ID.
|
# Generate a waypoint's id, NOT a packet ID.
|
||||||
# same algorithm as https://github.com/meshtastic/js/blob/715e35d2374276a43ffa93c628e3710875d43907/src/meshDevice.ts#L791
|
# same algorithm as https://github.com/meshtastic/js/blob/715e35d2374276a43ffa93c628e3710875d43907/src/meshDevice.ts#L791
|
||||||
seed = secrets.randbits(32)
|
seed = secrets.randbits(32)
|
||||||
w.id = math.floor(seed * math.pow(2, -32) * 1e9)
|
w.id = math.floor(seed * math.pow(2, -32) * 1e9)
|
||||||
logger.debug(f"w.id:{w.id}")
|
logging.debug(f"w.id:{w.id}")
|
||||||
else:
|
else:
|
||||||
w.id = waypoint_id
|
w.id = waypoint_id
|
||||||
if latitude != 0.0:
|
if latitude != 0.0:
|
||||||
w.latitude_i = int(latitude * 1e7)
|
w.latitude_i = int(latitude * 1e7)
|
||||||
logger.debug(f"w.latitude_i:{w.latitude_i}")
|
logging.debug(f"w.latitude_i:{w.latitude_i}")
|
||||||
if longitude != 0.0:
|
if longitude != 0.0:
|
||||||
w.longitude_i = int(longitude * 1e7)
|
w.longitude_i = int(longitude * 1e7)
|
||||||
logger.debug(f"w.longitude_i:{w.longitude_i}")
|
logging.debug(f"w.longitude_i:{w.longitude_i}")
|
||||||
|
|
||||||
if wantResponse:
|
if wantResponse:
|
||||||
onResponse = self.onResponseWaypoint
|
onResponse = self.onResponseWaypoint
|
||||||
@@ -901,7 +855,6 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
wantResponse=wantResponse,
|
wantResponse=wantResponse,
|
||||||
onResponse=onResponse,
|
onResponse=onResponse,
|
||||||
channelIndex=channelIndex,
|
channelIndex=channelIndex,
|
||||||
hopLimit=hopLimit,
|
|
||||||
)
|
)
|
||||||
if wantResponse:
|
if wantResponse:
|
||||||
self.waitForWaypoint()
|
self.waitForWaypoint()
|
||||||
@@ -914,7 +867,6 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
wantAck: bool = True,
|
wantAck: bool = True,
|
||||||
wantResponse: bool = False,
|
wantResponse: bool = False,
|
||||||
channelIndex: int = 0,
|
channelIndex: int = 0,
|
||||||
hopLimit: Optional[int]=None,
|
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Send a waypoint deletion packet to some other node (normally a broadcast)
|
Send a waypoint deletion packet to some other node (normally a broadcast)
|
||||||
@@ -941,7 +893,6 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
wantResponse=wantResponse,
|
wantResponse=wantResponse,
|
||||||
onResponse=onResponse,
|
onResponse=onResponse,
|
||||||
channelIndex=channelIndex,
|
channelIndex=channelIndex,
|
||||||
hopLimit=hopLimit,
|
|
||||||
)
|
)
|
||||||
if wantResponse:
|
if wantResponse:
|
||||||
self.waitForWaypoint()
|
self.waitForWaypoint()
|
||||||
@@ -1004,7 +955,7 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
else:
|
else:
|
||||||
nodeNum = node["num"]
|
nodeNum = node["num"]
|
||||||
else:
|
else:
|
||||||
logger.warning("Warning: There were no self.nodes.")
|
logging.warning("Warning: There were no self.nodes.")
|
||||||
|
|
||||||
meshPacket.to = nodeNum
|
meshPacket.to = nodeNum
|
||||||
meshPacket.want_ack = wantAck
|
meshPacket.want_ack = wantAck
|
||||||
@@ -1028,11 +979,11 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
|
|
||||||
toRadio.packet.CopyFrom(meshPacket)
|
toRadio.packet.CopyFrom(meshPacket)
|
||||||
if self.noProto:
|
if self.noProto:
|
||||||
logger.warning(
|
logging.warning(
|
||||||
"Not sending packet because protocol use is disabled by noProto"
|
"Not sending packet because protocol use is disabled by noProto"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
logger.debug(f"Sending packet: {stripnl(meshPacket)}")
|
logging.debug(f"Sending packet: {stripnl(meshPacket)}")
|
||||||
self._sendToRadio(toRadio)
|
self._sendToRadio(toRadio)
|
||||||
return meshPacket
|
return meshPacket
|
||||||
|
|
||||||
@@ -1083,7 +1034,7 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
"""Get info about my node."""
|
"""Get info about my node."""
|
||||||
if self.myInfo is None or self.nodesByNum is None:
|
if self.myInfo is None or self.nodesByNum is None:
|
||||||
return None
|
return None
|
||||||
logger.debug(f"self.nodesByNum:{self.nodesByNum}")
|
logging.debug(f"self.nodesByNum:{self.nodesByNum}")
|
||||||
return self.nodesByNum.get(self.myInfo.my_node_num)
|
return self.nodesByNum.get(self.myInfo.my_node_num)
|
||||||
|
|
||||||
def getMyUser(self):
|
def getMyUser(self):
|
||||||
@@ -1114,20 +1065,6 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
return user.get("publicKey", None)
|
return user.get("publicKey", None)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def getCannedMessage(self):
|
|
||||||
"""Get canned message"""
|
|
||||||
node = self.localNode
|
|
||||||
if node is not None:
|
|
||||||
return node.get_canned_message()
|
|
||||||
return None
|
|
||||||
|
|
||||||
def getRingtone(self):
|
|
||||||
"""Get ringtone"""
|
|
||||||
node = self.localNode
|
|
||||||
if node is not None:
|
|
||||||
return node.get_ringtone()
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _waitConnected(self, timeout=30.0):
|
def _waitConnected(self, timeout=30.0):
|
||||||
"""Block until the initial node db download is complete, or timeout
|
"""Block until the initial node db download is complete, or timeout
|
||||||
and raise an exception"""
|
and raise an exception"""
|
||||||
@@ -1173,7 +1110,7 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
def callback():
|
def callback():
|
||||||
self.heartbeatTimer = None
|
self.heartbeatTimer = None
|
||||||
interval = 300
|
interval = 300
|
||||||
logger.debug(f"Sending heartbeat, interval {interval} seconds")
|
logging.debug(f"Sending heartbeat, interval {interval} seconds")
|
||||||
self.heartbeatTimer = threading.Timer(interval, callback)
|
self.heartbeatTimer = threading.Timer(interval, callback)
|
||||||
self.heartbeatTimer.start()
|
self.heartbeatTimer.start()
|
||||||
self.sendHeartbeat()
|
self.sendHeartbeat()
|
||||||
@@ -1231,11 +1168,11 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
def _sendToRadio(self, toRadio: mesh_pb2.ToRadio) -> None:
|
def _sendToRadio(self, toRadio: mesh_pb2.ToRadio) -> None:
|
||||||
"""Send a ToRadio protobuf to the device"""
|
"""Send a ToRadio protobuf to the device"""
|
||||||
if self.noProto:
|
if self.noProto:
|
||||||
logger.warning(
|
logging.warning(
|
||||||
"Not sending packet because protocol use is disabled by noProto"
|
"Not sending packet because protocol use is disabled by noProto"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
# logger.debug(f"Sending toRadio: {stripnl(toRadio)}")
|
# logging.debug(f"Sending toRadio: {stripnl(toRadio)}")
|
||||||
|
|
||||||
if not toRadio.HasField("packet"):
|
if not toRadio.HasField("packet"):
|
||||||
# not a meshpacket -- send immediately, give queue a chance,
|
# not a meshpacket -- send immediately, give queue a chance,
|
||||||
@@ -1248,38 +1185,38 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
resentQueue = collections.OrderedDict()
|
resentQueue = collections.OrderedDict()
|
||||||
|
|
||||||
while self.queue:
|
while self.queue:
|
||||||
# logger.warn("queue: " + " ".join(f'{k:08x}' for k in self.queue))
|
# logging.warn("queue: " + " ".join(f'{k:08x}' for k in self.queue))
|
||||||
while not self._queueHasFreeSpace():
|
while not self._queueHasFreeSpace():
|
||||||
logger.debug("Waiting for free space in TX Queue")
|
logging.debug("Waiting for free space in TX Queue")
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
try:
|
try:
|
||||||
toResend = self.queue.popitem(last=False)
|
toResend = self.queue.popitem(last=False)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
break
|
break
|
||||||
packetId, packet = toResend
|
packetId, packet = toResend
|
||||||
# logger.warn(f"packet: {packetId:08x} {packet}")
|
# logging.warn(f"packet: {packetId:08x} {packet}")
|
||||||
resentQueue[packetId] = packet
|
resentQueue[packetId] = packet
|
||||||
if packet is False:
|
if packet is False:
|
||||||
continue
|
continue
|
||||||
self._queueClaim()
|
self._queueClaim()
|
||||||
if packet != toRadio:
|
if packet != toRadio:
|
||||||
logger.debug(f"Resending packet ID {packetId:08x} {packet}")
|
logging.debug(f"Resending packet ID {packetId:08x} {packet}")
|
||||||
self._sendToRadioImpl(packet)
|
self._sendToRadioImpl(packet)
|
||||||
|
|
||||||
# logger.warn("resentQueue: " + " ".join(f'{k:08x}' for k in resentQueue))
|
# logging.warn("resentQueue: " + " ".join(f'{k:08x}' for k in resentQueue))
|
||||||
for packetId, packet in resentQueue.items():
|
for packetId, packet in resentQueue.items():
|
||||||
if (
|
if (
|
||||||
self.queue.pop(packetId, False) is False
|
self.queue.pop(packetId, False) is False
|
||||||
): # Packet got acked under us
|
): # Packet got acked under us
|
||||||
logger.debug(f"packet {packetId:08x} got acked under us")
|
logging.debug(f"packet {packetId:08x} got acked under us")
|
||||||
continue
|
continue
|
||||||
if packet:
|
if packet:
|
||||||
self.queue[packetId] = packet
|
self.queue[packetId] = packet
|
||||||
# logger.warn("queue + resentQueue: " + " ".join(f'{k:08x}' for k in self.queue))
|
# logging.warn("queue + resentQueue: " + " ".join(f'{k:08x}' for k in self.queue))
|
||||||
|
|
||||||
def _sendToRadioImpl(self, toRadio: mesh_pb2.ToRadio) -> None:
|
def _sendToRadioImpl(self, toRadio: mesh_pb2.ToRadio) -> None:
|
||||||
"""Send a ToRadio protobuf to the device"""
|
"""Send a ToRadio protobuf to the device"""
|
||||||
logger.error(f"Subclass must provide toradio: {toRadio}")
|
logging.error(f"Subclass must provide toradio: {toRadio}")
|
||||||
|
|
||||||
def _handleConfigComplete(self) -> None:
|
def _handleConfigComplete(self) -> None:
|
||||||
"""
|
"""
|
||||||
@@ -1295,22 +1232,22 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
|
|
||||||
def _handleQueueStatusFromRadio(self, queueStatus) -> None:
|
def _handleQueueStatusFromRadio(self, queueStatus) -> None:
|
||||||
self.queueStatus = queueStatus
|
self.queueStatus = queueStatus
|
||||||
logger.debug(
|
logging.debug(
|
||||||
f"TX QUEUE free {queueStatus.free} of {queueStatus.maxlen}, res = {queueStatus.res}, id = {queueStatus.mesh_packet_id:08x} "
|
f"TX QUEUE free {queueStatus.free} of {queueStatus.maxlen}, res = {queueStatus.res}, id = {queueStatus.mesh_packet_id:08x} "
|
||||||
)
|
)
|
||||||
|
|
||||||
if queueStatus.res:
|
if queueStatus.res:
|
||||||
return
|
return
|
||||||
|
|
||||||
# logger.warn("queue: " + " ".join(f'{k:08x}' for k in self.queue))
|
# logging.warn("queue: " + " ".join(f'{k:08x}' for k in self.queue))
|
||||||
justQueued = self.queue.pop(queueStatus.mesh_packet_id, None)
|
justQueued = self.queue.pop(queueStatus.mesh_packet_id, None)
|
||||||
|
|
||||||
if justQueued is None and queueStatus.mesh_packet_id != 0:
|
if justQueued is None and queueStatus.mesh_packet_id != 0:
|
||||||
self.queue[queueStatus.mesh_packet_id] = False
|
self.queue[queueStatus.mesh_packet_id] = False
|
||||||
logger.debug(
|
logging.debug(
|
||||||
f"Reply for unexpected packet ID {queueStatus.mesh_packet_id:08x}"
|
f"Reply for unexpected packet ID {queueStatus.mesh_packet_id:08x}"
|
||||||
)
|
)
|
||||||
# logger.warn("queue: " + " ".join(f'{k:08x}' for k in self.queue))
|
# logging.warn("queue: " + " ".join(f'{k:08x}' for k in self.queue))
|
||||||
|
|
||||||
def _handleFromRadio(self, fromRadioBytes):
|
def _handleFromRadio(self, fromRadioBytes):
|
||||||
"""
|
"""
|
||||||
@@ -1318,30 +1255,30 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
|
|
||||||
Called by subclasses."""
|
Called by subclasses."""
|
||||||
fromRadio = mesh_pb2.FromRadio()
|
fromRadio = mesh_pb2.FromRadio()
|
||||||
logger.debug(
|
logging.debug(
|
||||||
f"in mesh_interface.py _handleFromRadio() fromRadioBytes: {fromRadioBytes}"
|
f"in mesh_interface.py _handleFromRadio() fromRadioBytes: {fromRadioBytes}"
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
fromRadio.ParseFromString(fromRadioBytes)
|
fromRadio.ParseFromString(fromRadioBytes)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
logger.error(
|
logging.error(
|
||||||
f"Error while parsing FromRadio bytes:{fromRadioBytes} {ex}"
|
f"Error while parsing FromRadio bytes:{fromRadioBytes} {ex}"
|
||||||
)
|
)
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
raise ex
|
raise ex
|
||||||
asDict = google.protobuf.json_format.MessageToDict(fromRadio)
|
asDict = google.protobuf.json_format.MessageToDict(fromRadio)
|
||||||
logger.debug(f"Received from radio: {fromRadio}")
|
logging.debug(f"Received from radio: {fromRadio}")
|
||||||
if fromRadio.HasField("my_info"):
|
if fromRadio.HasField("my_info"):
|
||||||
self.myInfo = fromRadio.my_info
|
self.myInfo = fromRadio.my_info
|
||||||
self.localNode.nodeNum = self.myInfo.my_node_num
|
self.localNode.nodeNum = self.myInfo.my_node_num
|
||||||
logger.debug(f"Received myinfo: {stripnl(fromRadio.my_info)}")
|
logging.debug(f"Received myinfo: {stripnl(fromRadio.my_info)}")
|
||||||
|
|
||||||
elif fromRadio.HasField("metadata"):
|
elif fromRadio.HasField("metadata"):
|
||||||
self.metadata = fromRadio.metadata
|
self.metadata = fromRadio.metadata
|
||||||
logger.debug(f"Received device metadata: {stripnl(fromRadio.metadata)}")
|
logging.debug(f"Received device metadata: {stripnl(fromRadio.metadata)}")
|
||||||
|
|
||||||
elif fromRadio.HasField("node_info"):
|
elif fromRadio.HasField("node_info"):
|
||||||
logger.debug(f"Received nodeinfo: {asDict['nodeInfo']}")
|
logging.debug(f"Received nodeinfo: {asDict['nodeInfo']}")
|
||||||
|
|
||||||
node = self._getOrCreateByNum(asDict["nodeInfo"]["num"])
|
node = self._getOrCreateByNum(asDict["nodeInfo"]["num"])
|
||||||
node.update(asDict["nodeInfo"])
|
node.update(asDict["nodeInfo"])
|
||||||
@@ -1349,7 +1286,7 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
newpos = self._fixupPosition(node["position"])
|
newpos = self._fixupPosition(node["position"])
|
||||||
node["position"] = newpos
|
node["position"] = newpos
|
||||||
except:
|
except:
|
||||||
logger.debug("Node without position")
|
logging.debug("Node without position")
|
||||||
|
|
||||||
# no longer necessary since we're mutating directly in nodesByNum via _getOrCreateByNum
|
# no longer necessary since we're mutating directly in nodesByNum via _getOrCreateByNum
|
||||||
# self.nodesByNum[node["num"]] = node
|
# self.nodesByNum[node["num"]] = node
|
||||||
@@ -1364,7 +1301,7 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
elif fromRadio.config_complete_id == self.configId:
|
elif fromRadio.config_complete_id == self.configId:
|
||||||
# we ignore the config_complete_id, it is unneeded for our
|
# we ignore the config_complete_id, it is unneeded for our
|
||||||
# stream API fromRadio.config_complete_id
|
# stream API fromRadio.config_complete_id
|
||||||
logger.debug(f"Config complete ID {self.configId}")
|
logging.debug(f"Config complete ID {self.configId}")
|
||||||
self._handleConfigComplete()
|
self._handleConfigComplete()
|
||||||
elif fromRadio.HasField("channel"):
|
elif fromRadio.HasField("channel"):
|
||||||
self._handleChannel(fromRadio.channel)
|
self._handleChannel(fromRadio.channel)
|
||||||
@@ -1374,14 +1311,6 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
self._handleLogRecord(fromRadio.log_record)
|
self._handleLogRecord(fromRadio.log_record)
|
||||||
elif fromRadio.HasField("queueStatus"):
|
elif fromRadio.HasField("queueStatus"):
|
||||||
self._handleQueueStatusFromRadio(fromRadio.queueStatus)
|
self._handleQueueStatusFromRadio(fromRadio.queueStatus)
|
||||||
elif fromRadio.HasField("clientNotification"):
|
|
||||||
publishingThread.queueWork(
|
|
||||||
lambda: pub.sendMessage(
|
|
||||||
"meshtastic.clientNotification",
|
|
||||||
notification=fromRadio.clientNotification,
|
|
||||||
interface=self,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
elif fromRadio.HasField("mqttClientProxyMessage"):
|
elif fromRadio.HasField("mqttClientProxyMessage"):
|
||||||
publishingThread.queueWork(
|
publishingThread.queueWork(
|
||||||
@@ -1477,13 +1406,9 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
self.localNode.moduleConfig.paxcounter.CopyFrom(
|
self.localNode.moduleConfig.paxcounter.CopyFrom(
|
||||||
fromRadio.moduleConfig.paxcounter
|
fromRadio.moduleConfig.paxcounter
|
||||||
)
|
)
|
||||||
elif fromRadio.moduleConfig.HasField("traffic_management"):
|
|
||||||
self.localNode.moduleConfig.traffic_management.CopyFrom(
|
|
||||||
fromRadio.moduleConfig.traffic_management
|
|
||||||
)
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
logger.debug("Unexpected FromRadio payload")
|
logging.debug("Unexpected FromRadio payload")
|
||||||
|
|
||||||
def _fixupPosition(self, position: Dict) -> Dict:
|
def _fixupPosition(self, position: Dict) -> Dict:
|
||||||
"""Convert integer lat/lon into floats
|
"""Convert integer lat/lon into floats
|
||||||
@@ -1517,7 +1442,7 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
try:
|
try:
|
||||||
return self.nodesByNum[num]["user"]["id"] # type: ignore[index]
|
return self.nodesByNum[num]["user"]["id"] # type: ignore[index]
|
||||||
except:
|
except:
|
||||||
logger.debug(f"Node {num} not found for fromId")
|
logging.debug(f"Node {num} not found for fromId")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _getOrCreateByNum(self, nodeNum):
|
def _getOrCreateByNum(self, nodeNum):
|
||||||
@@ -1573,7 +1498,7 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
# from might be missing if the nodenum was zero.
|
# from might be missing if the nodenum was zero.
|
||||||
if not hack and "from" not in asDict:
|
if not hack and "from" not in asDict:
|
||||||
asDict["from"] = 0
|
asDict["from"] = 0
|
||||||
logger.error(
|
logging.error(
|
||||||
f"Device returned a packet we sent, ignoring: {stripnl(asDict)}"
|
f"Device returned a packet we sent, ignoring: {stripnl(asDict)}"
|
||||||
)
|
)
|
||||||
print(
|
print(
|
||||||
@@ -1587,11 +1512,11 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
try:
|
try:
|
||||||
asDict["fromId"] = self._nodeNumToId(asDict["from"], False)
|
asDict["fromId"] = self._nodeNumToId(asDict["from"], False)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
logger.warning(f"Not populating fromId {ex}")
|
logging.warning(f"Not populating fromId {ex}")
|
||||||
try:
|
try:
|
||||||
asDict["toId"] = self._nodeNumToId(asDict["to"])
|
asDict["toId"] = self._nodeNumToId(asDict["to"])
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
logger.warning(f"Not populating toId {ex}")
|
logging.warning(f"Not populating toId {ex}")
|
||||||
|
|
||||||
# We could provide our objects as DotMaps - which work with . notation or as dictionaries
|
# We could provide our objects as DotMaps - which work with . notation or as dictionaries
|
||||||
# asObj = DotMap(asDict)
|
# asObj = DotMap(asDict)
|
||||||
@@ -1611,7 +1536,7 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
# it to prevent confusion
|
# it to prevent confusion
|
||||||
if "portnum" not in decoded:
|
if "portnum" not in decoded:
|
||||||
decoded["portnum"] = portnum
|
decoded["portnum"] = portnum
|
||||||
logger.warning(f"portnum was not in decoded. Setting to:{portnum}")
|
logging.warning(f"portnum was not in decoded. Setting to:{portnum}")
|
||||||
else:
|
else:
|
||||||
portnum = decoded["portnum"]
|
portnum = decoded["portnum"]
|
||||||
|
|
||||||
@@ -1643,7 +1568,7 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
# Is this message in response to a request, if so, look for a handler
|
# Is this message in response to a request, if so, look for a handler
|
||||||
requestId = decoded.get("requestId")
|
requestId = decoded.get("requestId")
|
||||||
if requestId is not None:
|
if requestId is not None:
|
||||||
logger.debug(f"Got a response for requestId {requestId}")
|
logging.debug(f"Got a response for requestId {requestId}")
|
||||||
# We ignore ACK packets unless the callback is named `onAckNak`
|
# We ignore ACK packets unless the callback is named `onAckNak`
|
||||||
# or the handler is set as ackPermitted, but send NAKs and
|
# or the handler is set as ackPermitted, but send NAKs and
|
||||||
# other, data-containing responses to the handlers
|
# other, data-containing responses to the handlers
|
||||||
@@ -1660,12 +1585,12 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
or handler.ackPermitted
|
or handler.ackPermitted
|
||||||
):
|
):
|
||||||
handler = self.responseHandlers.pop(requestId, None)
|
handler = self.responseHandlers.pop(requestId, None)
|
||||||
logger.debug(
|
logging.debug(
|
||||||
f"Calling response handler for requestId {requestId}"
|
f"Calling response handler for requestId {requestId}"
|
||||||
)
|
)
|
||||||
handler.callback(asDict)
|
handler.callback(asDict)
|
||||||
|
|
||||||
logger.debug(f"Publishing {topic}: packet={stripnl(asDict)} ")
|
logging.debug(f"Publishing {topic}: packet={stripnl(asDict)} ")
|
||||||
publishingThread.queueWork(
|
publishingThread.queueWork(
|
||||||
lambda: pub.sendMessage(topic, packet=asDict, interface=self)
|
lambda: pub.sendMessage(topic, packet=asDict, interface=self)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import time
|
|||||||
|
|
||||||
from typing import Optional, Union, List
|
from typing import Optional, Union, List
|
||||||
|
|
||||||
from meshtastic.protobuf import admin_pb2, apponly_pb2, channel_pb2, config_pb2, localonly_pb2, mesh_pb2, portnums_pb2
|
from meshtastic.protobuf import admin_pb2, apponly_pb2, channel_pb2, localonly_pb2, mesh_pb2, portnums_pb2
|
||||||
from meshtastic.util import (
|
from meshtastic.util import (
|
||||||
Timeout,
|
Timeout,
|
||||||
camel_to_snake,
|
camel_to_snake,
|
||||||
@@ -16,12 +16,8 @@ from meshtastic.util import (
|
|||||||
pskToString,
|
pskToString,
|
||||||
stripnl,
|
stripnl,
|
||||||
message_to_json,
|
message_to_json,
|
||||||
generate_channel_hash,
|
|
||||||
to_node_num,
|
|
||||||
flags_to_list,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
class Node:
|
class Node:
|
||||||
"""A model of a (local or remote) node in the mesh
|
"""A model of a (local or remote) node in the mesh
|
||||||
@@ -46,40 +42,11 @@ class Node:
|
|||||||
|
|
||||||
self.gotResponse = None
|
self.gotResponse = None
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
r = f"Node({self.iface!r}, 0x{self.nodeNum:08x}"
|
|
||||||
if self.noProto:
|
|
||||||
r += ", noProto=True"
|
|
||||||
if self._timeout.expireTimeout != 300:
|
|
||||||
r += ", timeout={self._timeout.expireTimeout!r}"
|
|
||||||
r += ")"
|
|
||||||
return r
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def position_flags_list(position_flags: int) -> List[str]:
|
|
||||||
"Return a list of position flags from the given flags integer"
|
|
||||||
return flags_to_list(config_pb2.Config.PositionConfig.PositionFlags, position_flags)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def excluded_modules_list(excluded_modules: int) -> List[str]:
|
|
||||||
"Return a list of excluded modules from the given flags integer"
|
|
||||||
return flags_to_list(mesh_pb2.ExcludedModules, excluded_modules)
|
|
||||||
|
|
||||||
def module_available(self, excluded_bit: int) -> bool:
|
|
||||||
"""Check DeviceMetadata.excluded_modules to see if a module is available."""
|
|
||||||
meta = getattr(self.iface, "metadata", None)
|
|
||||||
if meta is None:
|
|
||||||
return True
|
|
||||||
try:
|
|
||||||
return (meta.excluded_modules & excluded_bit) == 0
|
|
||||||
except Exception:
|
|
||||||
return True
|
|
||||||
|
|
||||||
def showChannels(self):
|
def showChannels(self):
|
||||||
"""Show human readable description of our channels."""
|
"""Show human readable description of our channels."""
|
||||||
print("Channels:")
|
print("Channels:")
|
||||||
if self.channels:
|
if self.channels:
|
||||||
logger.debug(f"self.channels:{self.channels}")
|
logging.debug(f"self.channels:{self.channels}")
|
||||||
for c in self.channels:
|
for c in self.channels:
|
||||||
cStr = message_to_json(c.settings)
|
cStr = message_to_json(c.settings)
|
||||||
# don't show disabled channels
|
# don't show disabled channels
|
||||||
@@ -112,7 +79,7 @@ class Node:
|
|||||||
|
|
||||||
def requestChannels(self, startingIndex: int = 0):
|
def requestChannels(self, startingIndex: int = 0):
|
||||||
"""Send regular MeshPackets to ask channels."""
|
"""Send regular MeshPackets to ask channels."""
|
||||||
logger.debug(f"requestChannels for nodeNum:{self.nodeNum}")
|
logging.debug(f"requestChannels for nodeNum:{self.nodeNum}")
|
||||||
# only initialize if we're starting out fresh
|
# only initialize if we're starting out fresh
|
||||||
if startingIndex == 0:
|
if startingIndex == 0:
|
||||||
self.channels = None
|
self.channels = None
|
||||||
@@ -121,7 +88,7 @@ class Node:
|
|||||||
|
|
||||||
def onResponseRequestSettings(self, p):
|
def onResponseRequestSettings(self, p):
|
||||||
"""Handle the response packets for requesting settings _requestSettings()"""
|
"""Handle the response packets for requesting settings _requestSettings()"""
|
||||||
logger.debug(f"onResponseRequestSetting() p:{p}")
|
logging.debug(f"onResponseRequestSetting() p:{p}")
|
||||||
config_values = None
|
config_values = None
|
||||||
if "routing" in p["decoded"]:
|
if "routing" in p["decoded"]:
|
||||||
if p["decoded"]["routing"]["errorReason"] != "NONE":
|
if p["decoded"]["routing"]["errorReason"] != "NONE":
|
||||||
@@ -170,10 +137,11 @@ class Node:
|
|||||||
p.get_config_request = configType
|
p.get_config_request = configType
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
msgIndex = configType.index
|
||||||
if configType.containing_type.name == "LocalConfig":
|
if configType.containing_type.name == "LocalConfig":
|
||||||
p.get_config_request = admin_pb2.AdminMessage.ConfigType.Value(configType.name.upper() + "_CONFIG")
|
p.get_config_request = msgIndex
|
||||||
else:
|
else:
|
||||||
p.get_module_config_request = configType.index
|
p.get_module_config_request = msgIndex
|
||||||
|
|
||||||
self._sendAdmin(p, wantResponse=True, onResponse=onResponse)
|
self._sendAdmin(p, wantResponse=True, onResponse=onResponse)
|
||||||
if onResponse:
|
if onResponse:
|
||||||
@@ -244,12 +212,10 @@ class Node:
|
|||||||
p.set_module_config.ambient_lighting.CopyFrom(self.moduleConfig.ambient_lighting)
|
p.set_module_config.ambient_lighting.CopyFrom(self.moduleConfig.ambient_lighting)
|
||||||
elif config_name == "paxcounter":
|
elif config_name == "paxcounter":
|
||||||
p.set_module_config.paxcounter.CopyFrom(self.moduleConfig.paxcounter)
|
p.set_module_config.paxcounter.CopyFrom(self.moduleConfig.paxcounter)
|
||||||
elif config_name == "traffic_management":
|
|
||||||
p.set_module_config.traffic_management.CopyFrom(self.moduleConfig.traffic_management)
|
|
||||||
else:
|
else:
|
||||||
our_exit(f"Error: No valid config with name {config_name}")
|
our_exit(f"Error: No valid config with name {config_name}")
|
||||||
|
|
||||||
logger.debug(f"Wrote: {config_name}")
|
logging.debug(f"Wrote: {config_name}")
|
||||||
if self == self.iface.localNode:
|
if self == self.iface.localNode:
|
||||||
onResponse = None
|
onResponse = None
|
||||||
else:
|
else:
|
||||||
@@ -262,7 +228,7 @@ class Node:
|
|||||||
p = admin_pb2.AdminMessage()
|
p = admin_pb2.AdminMessage()
|
||||||
p.set_channel.CopyFrom(self.channels[channelIndex])
|
p.set_channel.CopyFrom(self.channels[channelIndex])
|
||||||
self._sendAdmin(p, adminIndex=adminIndex)
|
self._sendAdmin(p, adminIndex=adminIndex)
|
||||||
logger.debug(f"Wrote channel {channelIndex}")
|
logging.debug(f"Wrote channel {channelIndex}")
|
||||||
|
|
||||||
def getChannelByChannelIndex(self, channelIndex):
|
def getChannelByChannelIndex(self, channelIndex):
|
||||||
"""Get channel by channelIndex
|
"""Get channel by channelIndex
|
||||||
@@ -287,22 +253,12 @@ class Node:
|
|||||||
# for sending admin channels will also change
|
# for sending admin channels will also change
|
||||||
adminIndex = self.iface.localNode._getAdminChannelIndex()
|
adminIndex = self.iface.localNode._getAdminChannelIndex()
|
||||||
|
|
||||||
# Snapshot serialized channel payloads from channelIndex onward so we
|
|
||||||
# can avoid writing slots whose protobuf content did not change after
|
|
||||||
# the shift. Use bytes (not message objects), because _fixupChannels()
|
|
||||||
# mutates message fields in-place.
|
|
||||||
old_channels = [
|
|
||||||
self.channels[i].SerializeToString()
|
|
||||||
for i in range(channelIndex, len(self.channels))
|
|
||||||
]
|
|
||||||
|
|
||||||
self.channels.pop(channelIndex)
|
self.channels.pop(channelIndex)
|
||||||
self._fixupChannels() # expand back to 8 channels
|
self._fixupChannels() # expand back to 8 channels
|
||||||
|
|
||||||
index = channelIndex
|
index = channelIndex
|
||||||
for old_ch in old_channels:
|
while index < 8:
|
||||||
if self.channels[index].SerializeToString() != old_ch:
|
self.writeChannel(index, adminIndex=adminIndex)
|
||||||
self.writeChannel(index, adminIndex=adminIndex)
|
|
||||||
index += 1
|
index += 1
|
||||||
|
|
||||||
# if we are updating the local node, we might end up
|
# if we are updating the local node, we might end up
|
||||||
@@ -333,37 +289,28 @@ class Node:
|
|||||||
return c.index
|
return c.index
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def setOwner(self, long_name: Optional[str]=None, short_name: Optional[str]=None, is_licensed: bool=False, is_unmessagable: Optional[bool]=None):
|
def setOwner(self, long_name: Optional[str]=None, short_name: Optional[str]=None, is_licensed: bool=False):
|
||||||
"""Set device owner name"""
|
"""Set device owner name"""
|
||||||
logger.debug(f"in setOwner nodeNum:{self.nodeNum}")
|
logging.debug(f"in setOwner nodeNum:{self.nodeNum}")
|
||||||
self.ensureSessionKey()
|
self.ensureSessionKey()
|
||||||
p = admin_pb2.AdminMessage()
|
p = admin_pb2.AdminMessage()
|
||||||
|
|
||||||
nChars = 4
|
nChars = 4
|
||||||
if long_name is not None:
|
if long_name is not None:
|
||||||
long_name = long_name.strip()
|
long_name = long_name.strip()
|
||||||
# Validate that long_name is not empty or whitespace-only
|
|
||||||
if not long_name:
|
|
||||||
our_exit("ERROR: Long Name cannot be empty or contain only whitespace characters")
|
|
||||||
p.set_owner.long_name = long_name
|
p.set_owner.long_name = long_name
|
||||||
p.set_owner.is_licensed = is_licensed
|
p.set_owner.is_licensed = is_licensed
|
||||||
if short_name is not None:
|
if short_name is not None:
|
||||||
short_name = short_name.strip()
|
short_name = short_name.strip()
|
||||||
# Validate that short_name is not empty or whitespace-only
|
|
||||||
if not short_name:
|
|
||||||
our_exit("ERROR: Short Name cannot be empty or contain only whitespace characters")
|
|
||||||
if len(short_name) > nChars:
|
if len(short_name) > nChars:
|
||||||
short_name = short_name[:nChars]
|
short_name = short_name[:nChars]
|
||||||
print(f"Maximum is 4 characters, truncated to {short_name}")
|
print(f"Maximum is 4 characters, truncated to {short_name}")
|
||||||
p.set_owner.short_name = short_name
|
p.set_owner.short_name = short_name
|
||||||
if is_unmessagable is not None:
|
|
||||||
p.set_owner.is_unmessagable = is_unmessagable
|
|
||||||
|
|
||||||
# Note: These debug lines are used in unit tests
|
# Note: These debug lines are used in unit tests
|
||||||
logger.debug(f"p.set_owner.long_name:{p.set_owner.long_name}:")
|
logging.debug(f"p.set_owner.long_name:{p.set_owner.long_name}:")
|
||||||
logger.debug(f"p.set_owner.short_name:{p.set_owner.short_name}:")
|
logging.debug(f"p.set_owner.short_name:{p.set_owner.short_name}:")
|
||||||
logger.debug(f"p.set_owner.is_licensed:{p.set_owner.is_licensed}")
|
logging.debug(f"p.set_owner.is_licensed:{p.set_owner.is_licensed}")
|
||||||
logger.debug(f"p.set_owner.is_unmessagable:{p.set_owner.is_unmessagable}:")
|
|
||||||
# If sending to a remote node, wait for ACK/NAK
|
# If sending to a remote node, wait for ACK/NAK
|
||||||
if self == self.iface.localNode:
|
if self == self.iface.localNode:
|
||||||
onResponse = None
|
onResponse = None
|
||||||
@@ -390,46 +337,6 @@ class Node:
|
|||||||
s = s.replace("=", "").replace("+", "-").replace("/", "_")
|
s = s.replace("=", "").replace("+", "-").replace("/", "_")
|
||||||
return f"https://meshtastic.org/e/#{s}"
|
return f"https://meshtastic.org/e/#{s}"
|
||||||
|
|
||||||
def getContactURL(self, node_id: Union[int, str], should_ignore: bool = False, manually_verified: bool = False):
|
|
||||||
"""Generate a shareable contact URL for the specified node"""
|
|
||||||
nodeNum = to_node_num(node_id)
|
|
||||||
|
|
||||||
node = self.iface.nodesByNum.get(nodeNum)
|
|
||||||
if not node or not node.get("user"):
|
|
||||||
our_exit(f"Warning: Node {node_id} not found in NodeDB")
|
|
||||||
|
|
||||||
contact = admin_pb2.SharedContact()
|
|
||||||
contact.node_num = nodeNum
|
|
||||||
|
|
||||||
u = node["user"]
|
|
||||||
if u.get("id"):
|
|
||||||
contact.user.id = u["id"]
|
|
||||||
if u.get("macaddr"):
|
|
||||||
contact.user.macaddr = base64.b64decode(u["macaddr"])
|
|
||||||
if u.get("longName"):
|
|
||||||
contact.user.long_name = u["longName"]
|
|
||||||
if u.get("shortName"):
|
|
||||||
contact.user.short_name = u["shortName"]
|
|
||||||
if u.get("hwModel") and u["hwModel"] != "UNSET":
|
|
||||||
contact.user.hw_model = mesh_pb2.HardwareModel.Value(u["hwModel"])
|
|
||||||
if u.get("role"):
|
|
||||||
contact.user.role = config_pb2.Config.DeviceConfig.Role.Value(u["role"])
|
|
||||||
if u.get("publicKey"):
|
|
||||||
contact.user.public_key = base64.b64decode(u["publicKey"])
|
|
||||||
if u.get("isLicensed"):
|
|
||||||
contact.user.is_licensed = u["isLicensed"]
|
|
||||||
if u.get("isUnmessagable") is not None:
|
|
||||||
contact.user.is_unmessagable = u["isUnmessagable"]
|
|
||||||
if should_ignore:
|
|
||||||
contact.should_ignore = True
|
|
||||||
if manually_verified:
|
|
||||||
contact.manually_verified = True
|
|
||||||
|
|
||||||
data = contact.SerializeToString()
|
|
||||||
s = base64.urlsafe_b64encode(data).decode("ascii")
|
|
||||||
s = s.replace("=", "").replace("+", "-").replace("/", "_")
|
|
||||||
return f"https://meshtastic.org/v/#{s}"
|
|
||||||
|
|
||||||
def setURL(self, url: str, addOnly: bool = False):
|
def setURL(self, url: str, addOnly: bool = False):
|
||||||
"""Set mesh network URL"""
|
"""Set mesh network URL"""
|
||||||
if self.localConfig is None or self.channels is None:
|
if self.localConfig is None or self.channels is None:
|
||||||
@@ -486,7 +393,7 @@ class Node:
|
|||||||
ch.index = i
|
ch.index = i
|
||||||
ch.settings.CopyFrom(chs)
|
ch.settings.CopyFrom(chs)
|
||||||
self.channels[ch.index] = ch
|
self.channels[ch.index] = ch
|
||||||
logger.debug(f"Channel i:{i} ch:{ch}")
|
logging.debug(f"Channel i:{i} ch:{ch}")
|
||||||
self.writeChannel(ch.index)
|
self.writeChannel(ch.index)
|
||||||
i = i + 1
|
i = i + 1
|
||||||
|
|
||||||
@@ -495,35 +402,9 @@ class Node:
|
|||||||
self.ensureSessionKey()
|
self.ensureSessionKey()
|
||||||
self._sendAdmin(p)
|
self._sendAdmin(p)
|
||||||
|
|
||||||
def addContactURL(self, url: str):
|
|
||||||
"""Add a contact (User) to the NodeDB from a shareable URL"""
|
|
||||||
self.ensureSessionKey()
|
|
||||||
|
|
||||||
splitURL = url.split("/#")
|
|
||||||
if len(splitURL) == 1:
|
|
||||||
our_exit(f"Warning: Invalid URL '{url}'")
|
|
||||||
b64 = splitURL[-1]
|
|
||||||
|
|
||||||
missing_padding = len(b64) % 4
|
|
||||||
if missing_padding:
|
|
||||||
b64 += "=" * (4 - missing_padding)
|
|
||||||
|
|
||||||
decodedURL = base64.urlsafe_b64decode(b64)
|
|
||||||
contact = admin_pb2.SharedContact()
|
|
||||||
contact.ParseFromString(decodedURL)
|
|
||||||
|
|
||||||
p = admin_pb2.AdminMessage()
|
|
||||||
p.add_contact.CopyFrom(contact)
|
|
||||||
|
|
||||||
if self == self.iface.localNode:
|
|
||||||
onResponse = None
|
|
||||||
else:
|
|
||||||
onResponse = self.onAckNak
|
|
||||||
return self._sendAdmin(p, onResponse=onResponse)
|
|
||||||
|
|
||||||
def onResponseRequestRingtone(self, p):
|
def onResponseRequestRingtone(self, p):
|
||||||
"""Handle the response packet for requesting ringtone part 1"""
|
"""Handle the response packet for requesting ringtone part 1"""
|
||||||
logger.debug(f"onResponseRequestRingtone() p:{p}")
|
logging.debug(f"onResponseRequestRingtone() p:{p}")
|
||||||
errorFound = False
|
errorFound = False
|
||||||
if "routing" in p["decoded"]:
|
if "routing" in p["decoded"]:
|
||||||
if p["decoded"]["routing"]["errorReason"] != "NONE":
|
if p["decoded"]["routing"]["errorReason"] != "NONE":
|
||||||
@@ -536,16 +417,12 @@ class Node:
|
|||||||
self.ringtonePart = p["decoded"]["admin"][
|
self.ringtonePart = p["decoded"]["admin"][
|
||||||
"raw"
|
"raw"
|
||||||
].get_ringtone_response
|
].get_ringtone_response
|
||||||
logger.debug(f"self.ringtonePart:{self.ringtonePart}")
|
logging.debug(f"self.ringtonePart:{self.ringtonePart}")
|
||||||
self.gotResponse = True
|
self.gotResponse = True
|
||||||
|
|
||||||
def get_ringtone(self):
|
def get_ringtone(self):
|
||||||
"""Get the ringtone. Concatenate all pieces together and return a single string."""
|
"""Get the ringtone. Concatenate all pieces together and return a single string."""
|
||||||
logger.debug(f"in get_ringtone()")
|
logging.debug(f"in get_ringtone()")
|
||||||
if not self.module_available(mesh_pb2.EXTNOTIF_CONFIG):
|
|
||||||
logging.warning("External Notification module not present (excluded by firmware)")
|
|
||||||
return None
|
|
||||||
|
|
||||||
if not self.ringtone:
|
if not self.ringtone:
|
||||||
p1 = admin_pb2.AdminMessage()
|
p1 = admin_pb2.AdminMessage()
|
||||||
p1.get_ringtone_request = True
|
p1.get_ringtone_request = True
|
||||||
@@ -556,20 +433,18 @@ class Node:
|
|||||||
while self.gotResponse is False:
|
while self.gotResponse is False:
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
|
|
||||||
logger.debug(f"self.ringtone:{self.ringtone}")
|
logging.debug(f"self.ringtone:{self.ringtone}")
|
||||||
|
|
||||||
self.ringtone = ""
|
self.ringtone = ""
|
||||||
if self.ringtonePart:
|
if self.ringtonePart:
|
||||||
self.ringtone += self.ringtonePart
|
self.ringtone += self.ringtonePart
|
||||||
|
|
||||||
logger.debug(f"ringtone:{self.ringtone}")
|
print(f"ringtone:{self.ringtone}")
|
||||||
|
logging.debug(f"ringtone:{self.ringtone}")
|
||||||
return self.ringtone
|
return self.ringtone
|
||||||
|
|
||||||
def set_ringtone(self, ringtone):
|
def set_ringtone(self, ringtone):
|
||||||
"""Set the ringtone. The ringtone length must be less than 230 character."""
|
"""Set the ringtone. The ringtone length must be less than 230 character."""
|
||||||
if not self.module_available(mesh_pb2.EXTNOTIF_CONFIG):
|
|
||||||
logging.warning("External Notification module not present (excluded by firmware)")
|
|
||||||
return None
|
|
||||||
|
|
||||||
if len(ringtone) > 230:
|
if len(ringtone) > 230:
|
||||||
our_exit("Warning: The ringtone must be less than 230 characters.")
|
our_exit("Warning: The ringtone must be less than 230 characters.")
|
||||||
@@ -589,7 +464,7 @@ class Node:
|
|||||||
if i == 0:
|
if i == 0:
|
||||||
p.set_ringtone_message = chunk
|
p.set_ringtone_message = chunk
|
||||||
|
|
||||||
logger.debug(f"Setting ringtone '{chunk}' part {i+1}")
|
logging.debug(f"Setting ringtone '{chunk}' part {i+1}")
|
||||||
# If sending to a remote node, wait for ACK/NAK
|
# If sending to a remote node, wait for ACK/NAK
|
||||||
if self == self.iface.localNode:
|
if self == self.iface.localNode:
|
||||||
onResponse = None
|
onResponse = None
|
||||||
@@ -599,7 +474,7 @@ class Node:
|
|||||||
|
|
||||||
def onResponseRequestCannedMessagePluginMessageMessages(self, p):
|
def onResponseRequestCannedMessagePluginMessageMessages(self, p):
|
||||||
"""Handle the response packet for requesting canned message plugin message part 1"""
|
"""Handle the response packet for requesting canned message plugin message part 1"""
|
||||||
logger.debug(f"onResponseRequestCannedMessagePluginMessageMessages() p:{p}")
|
logging.debug(f"onResponseRequestCannedMessagePluginMessageMessages() p:{p}")
|
||||||
errorFound = False
|
errorFound = False
|
||||||
if "routing" in p["decoded"]:
|
if "routing" in p["decoded"]:
|
||||||
if p["decoded"]["routing"]["errorReason"] != "NONE":
|
if p["decoded"]["routing"]["errorReason"] != "NONE":
|
||||||
@@ -612,17 +487,14 @@ class Node:
|
|||||||
self.cannedPluginMessageMessages = p["decoded"]["admin"][
|
self.cannedPluginMessageMessages = p["decoded"]["admin"][
|
||||||
"raw"
|
"raw"
|
||||||
].get_canned_message_module_messages_response
|
].get_canned_message_module_messages_response
|
||||||
logger.debug(
|
logging.debug(
|
||||||
f"self.cannedPluginMessageMessages:{self.cannedPluginMessageMessages}"
|
f"self.cannedPluginMessageMessages:{self.cannedPluginMessageMessages}"
|
||||||
)
|
)
|
||||||
self.gotResponse = True
|
self.gotResponse = True
|
||||||
|
|
||||||
def get_canned_message(self):
|
def get_canned_message(self):
|
||||||
"""Get the canned message string. Concatenate all pieces together and return a single string."""
|
"""Get the canned message string. Concatenate all pieces together and return a single string."""
|
||||||
logger.debug(f"in get_canned_message()")
|
logging.debug(f"in get_canned_message()")
|
||||||
if not self.module_available(mesh_pb2.CANNEDMSG_CONFIG):
|
|
||||||
logging.warning("Canned Message module not present (excluded by firmware)")
|
|
||||||
return None
|
|
||||||
if not self.cannedPluginMessage:
|
if not self.cannedPluginMessage:
|
||||||
p1 = admin_pb2.AdminMessage()
|
p1 = admin_pb2.AdminMessage()
|
||||||
p1.get_canned_message_module_messages_request = True
|
p1.get_canned_message_module_messages_request = True
|
||||||
@@ -635,7 +507,7 @@ class Node:
|
|||||||
while self.gotResponse is False:
|
while self.gotResponse is False:
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
|
|
||||||
logger.debug(
|
logging.debug(
|
||||||
f"self.cannedPluginMessageMessages:{self.cannedPluginMessageMessages}"
|
f"self.cannedPluginMessageMessages:{self.cannedPluginMessageMessages}"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -643,14 +515,12 @@ class Node:
|
|||||||
if self.cannedPluginMessageMessages:
|
if self.cannedPluginMessageMessages:
|
||||||
self.cannedPluginMessage += self.cannedPluginMessageMessages
|
self.cannedPluginMessage += self.cannedPluginMessageMessages
|
||||||
|
|
||||||
logger.debug(f"canned_plugin_message:{self.cannedPluginMessage}")
|
print(f"canned_plugin_message:{self.cannedPluginMessage}")
|
||||||
|
logging.debug(f"canned_plugin_message:{self.cannedPluginMessage}")
|
||||||
return self.cannedPluginMessage
|
return self.cannedPluginMessage
|
||||||
|
|
||||||
def set_canned_message(self, message):
|
def set_canned_message(self, message):
|
||||||
"""Set the canned message. The canned messages length must be less than 200 character."""
|
"""Set the canned message. The canned messages length must be less than 200 character."""
|
||||||
if not self.module_available(mesh_pb2.CANNEDMSG_CONFIG):
|
|
||||||
logging.warning("Canned Message module not present (excluded by firmware)")
|
|
||||||
return None
|
|
||||||
|
|
||||||
if len(message) > 200:
|
if len(message) > 200:
|
||||||
our_exit("Warning: The canned message must be less than 200 characters.")
|
our_exit("Warning: The canned message must be less than 200 characters.")
|
||||||
@@ -670,7 +540,7 @@ class Node:
|
|||||||
if i == 0:
|
if i == 0:
|
||||||
p.set_canned_message_module_messages = chunk
|
p.set_canned_message_module_messages = chunk
|
||||||
|
|
||||||
logger.debug(f"Setting canned message '{chunk}' part {i+1}")
|
logging.debug(f"Setting canned message '{chunk}' part {i+1}")
|
||||||
# If sending to a remote node, wait for ACK/NAK
|
# If sending to a remote node, wait for ACK/NAK
|
||||||
if self == self.iface.localNode:
|
if self == self.iface.localNode:
|
||||||
onResponse = None
|
onResponse = None
|
||||||
@@ -684,7 +554,7 @@ class Node:
|
|||||||
self.ensureSessionKey()
|
self.ensureSessionKey()
|
||||||
p = admin_pb2.AdminMessage()
|
p = admin_pb2.AdminMessage()
|
||||||
p.exit_simulator = True
|
p.exit_simulator = True
|
||||||
logger.debug("in exitSimulator()")
|
logging.debug("in exitSimulator()")
|
||||||
|
|
||||||
return self._sendAdmin(p)
|
return self._sendAdmin(p)
|
||||||
|
|
||||||
@@ -693,7 +563,7 @@ class Node:
|
|||||||
self.ensureSessionKey()
|
self.ensureSessionKey()
|
||||||
p = admin_pb2.AdminMessage()
|
p = admin_pb2.AdminMessage()
|
||||||
p.reboot_seconds = secs
|
p.reboot_seconds = secs
|
||||||
logger.info(f"Telling node to reboot in {secs} seconds")
|
logging.info(f"Telling node to reboot in {secs} seconds")
|
||||||
|
|
||||||
# If sending to a remote node, wait for ACK/NAK
|
# If sending to a remote node, wait for ACK/NAK
|
||||||
if self == self.iface.localNode:
|
if self == self.iface.localNode:
|
||||||
@@ -707,7 +577,7 @@ class Node:
|
|||||||
self.ensureSessionKey()
|
self.ensureSessionKey()
|
||||||
p = admin_pb2.AdminMessage()
|
p = admin_pb2.AdminMessage()
|
||||||
p.begin_edit_settings = True
|
p.begin_edit_settings = True
|
||||||
logger.info(f"Telling open a transaction to edit settings")
|
logging.info(f"Telling open a transaction to edit settings")
|
||||||
|
|
||||||
# If sending to a remote node, wait for ACK/NAK
|
# If sending to a remote node, wait for ACK/NAK
|
||||||
if self == self.iface.localNode:
|
if self == self.iface.localNode:
|
||||||
@@ -721,7 +591,7 @@ class Node:
|
|||||||
self.ensureSessionKey()
|
self.ensureSessionKey()
|
||||||
p = admin_pb2.AdminMessage()
|
p = admin_pb2.AdminMessage()
|
||||||
p.commit_edit_settings = True
|
p.commit_edit_settings = True
|
||||||
logger.info(f"Telling node to commit open transaction for editing settings")
|
logging.info(f"Telling node to commit open transaction for editing settings")
|
||||||
|
|
||||||
# If sending to a remote node, wait for ACK/NAK
|
# If sending to a remote node, wait for ACK/NAK
|
||||||
if self == self.iface.localNode:
|
if self == self.iface.localNode:
|
||||||
@@ -731,11 +601,11 @@ class Node:
|
|||||||
return self._sendAdmin(p, onResponse=onResponse)
|
return self._sendAdmin(p, onResponse=onResponse)
|
||||||
|
|
||||||
def rebootOTA(self, secs: int = 10):
|
def rebootOTA(self, secs: int = 10):
|
||||||
"""Tell the node to reboot into factory firmware (firmware < 2.7.18)."""
|
"""Tell the node to reboot into factory firmware."""
|
||||||
self.ensureSessionKey()
|
self.ensureSessionKey()
|
||||||
p = admin_pb2.AdminMessage()
|
p = admin_pb2.AdminMessage()
|
||||||
p.reboot_ota_seconds = secs
|
p.reboot_ota_seconds = secs
|
||||||
logger.info(f"Telling node to reboot to OTA in {secs} seconds")
|
logging.info(f"Telling node to reboot to OTA in {secs} seconds")
|
||||||
|
|
||||||
# If sending to a remote node, wait for ACK/NAK
|
# If sending to a remote node, wait for ACK/NAK
|
||||||
if self == self.iface.localNode:
|
if self == self.iface.localNode:
|
||||||
@@ -744,28 +614,12 @@ class Node:
|
|||||||
onResponse = self.onAckNak
|
onResponse = self.onAckNak
|
||||||
return self._sendAdmin(p, onResponse=onResponse)
|
return self._sendAdmin(p, onResponse=onResponse)
|
||||||
|
|
||||||
def startOTA(
|
|
||||||
self,
|
|
||||||
ota_mode: admin_pb2.OTAMode.ValueType,
|
|
||||||
ota_file_hash: bytes,
|
|
||||||
):
|
|
||||||
"""Tell the node to start OTA mode (firmware >= 2.7.18)."""
|
|
||||||
if self != self.iface.localNode:
|
|
||||||
raise ValueError("startOTA only possible in local node")
|
|
||||||
|
|
||||||
self.ensureSessionKey()
|
|
||||||
p = admin_pb2.AdminMessage()
|
|
||||||
p.ota_request.reboot_ota_mode = ota_mode
|
|
||||||
p.ota_request.ota_hash = ota_file_hash
|
|
||||||
|
|
||||||
return self._sendAdmin(p)
|
|
||||||
|
|
||||||
def enterDFUMode(self):
|
def enterDFUMode(self):
|
||||||
"""Tell the node to enter DFU mode (NRF52)."""
|
"""Tell the node to enter DFU mode (NRF52)."""
|
||||||
self.ensureSessionKey()
|
self.ensureSessionKey()
|
||||||
p = admin_pb2.AdminMessage()
|
p = admin_pb2.AdminMessage()
|
||||||
p.enter_dfu_mode_request = True
|
p.enter_dfu_mode_request = True
|
||||||
logger.info(f"Telling node to enable DFU mode")
|
logging.info(f"Telling node to enable DFU mode")
|
||||||
|
|
||||||
# If sending to a remote node, wait for ACK/NAK
|
# If sending to a remote node, wait for ACK/NAK
|
||||||
if self == self.iface.localNode:
|
if self == self.iface.localNode:
|
||||||
@@ -779,7 +633,7 @@ class Node:
|
|||||||
self.ensureSessionKey()
|
self.ensureSessionKey()
|
||||||
p = admin_pb2.AdminMessage()
|
p = admin_pb2.AdminMessage()
|
||||||
p.shutdown_seconds = secs
|
p.shutdown_seconds = secs
|
||||||
logger.info(f"Telling node to shutdown in {secs} seconds")
|
logging.info(f"Telling node to shutdown in {secs} seconds")
|
||||||
|
|
||||||
# If sending to a remote node, wait for ACK/NAK
|
# If sending to a remote node, wait for ACK/NAK
|
||||||
if self == self.iface.localNode:
|
if self == self.iface.localNode:
|
||||||
@@ -792,7 +646,7 @@ class Node:
|
|||||||
"""Get the node's metadata."""
|
"""Get the node's metadata."""
|
||||||
p = admin_pb2.AdminMessage()
|
p = admin_pb2.AdminMessage()
|
||||||
p.get_device_metadata_request = True
|
p.get_device_metadata_request = True
|
||||||
logger.info(f"Requesting device metadata")
|
logging.info(f"Requesting device metadata")
|
||||||
|
|
||||||
self._sendAdmin(
|
self._sendAdmin(
|
||||||
p, wantResponse=True, onResponse=self.onRequestGetMetadata
|
p, wantResponse=True, onResponse=self.onRequestGetMetadata
|
||||||
@@ -804,11 +658,11 @@ class Node:
|
|||||||
self.ensureSessionKey()
|
self.ensureSessionKey()
|
||||||
p = admin_pb2.AdminMessage()
|
p = admin_pb2.AdminMessage()
|
||||||
if full:
|
if full:
|
||||||
p.factory_reset_device = 1
|
p.factory_reset_device = True
|
||||||
logger.info(f"Telling node to factory reset (full device reset)")
|
logging.info(f"Telling node to factory reset (full device reset)")
|
||||||
else:
|
else:
|
||||||
p.factory_reset_config = 1
|
p.factory_reset_config = True
|
||||||
logger.info(f"Telling node to factory reset (config reset)")
|
logging.info(f"Telling node to factory reset (config reset)")
|
||||||
|
|
||||||
# If sending to a remote node, wait for ACK/NAK
|
# If sending to a remote node, wait for ACK/NAK
|
||||||
if self == self.iface.localNode:
|
if self == self.iface.localNode:
|
||||||
@@ -820,7 +674,11 @@ class Node:
|
|||||||
def removeNode(self, nodeId: Union[int, str]):
|
def removeNode(self, nodeId: Union[int, str]):
|
||||||
"""Tell the node to remove a specific node by ID"""
|
"""Tell the node to remove a specific node by ID"""
|
||||||
self.ensureSessionKey()
|
self.ensureSessionKey()
|
||||||
nodeId = to_node_num(nodeId)
|
if isinstance(nodeId, str):
|
||||||
|
if nodeId.startswith("!"):
|
||||||
|
nodeId = int(nodeId[1:], 16)
|
||||||
|
else:
|
||||||
|
nodeId = int(nodeId)
|
||||||
|
|
||||||
p = admin_pb2.AdminMessage()
|
p = admin_pb2.AdminMessage()
|
||||||
p.remove_by_nodenum = nodeId
|
p.remove_by_nodenum = nodeId
|
||||||
@@ -834,7 +692,11 @@ class Node:
|
|||||||
def setFavorite(self, nodeId: Union[int, str]):
|
def setFavorite(self, nodeId: Union[int, str]):
|
||||||
"""Tell the node to set the specified node ID to be favorited on the NodeDB on the device"""
|
"""Tell the node to set the specified node ID to be favorited on the NodeDB on the device"""
|
||||||
self.ensureSessionKey()
|
self.ensureSessionKey()
|
||||||
nodeId = to_node_num(nodeId)
|
if isinstance(nodeId, str):
|
||||||
|
if nodeId.startswith("!"):
|
||||||
|
nodeId = int(nodeId[1:], 16)
|
||||||
|
else:
|
||||||
|
nodeId = int(nodeId)
|
||||||
|
|
||||||
p = admin_pb2.AdminMessage()
|
p = admin_pb2.AdminMessage()
|
||||||
p.set_favorite_node = nodeId
|
p.set_favorite_node = nodeId
|
||||||
@@ -845,10 +707,53 @@ class Node:
|
|||||||
onResponse = self.onAckNak
|
onResponse = self.onAckNak
|
||||||
return self._sendAdmin(p, onResponse=onResponse)
|
return self._sendAdmin(p, onResponse=onResponse)
|
||||||
|
|
||||||
def removeFavorite(self, nodeId: Union[int, str]):
|
def backupPreferences(self, location: Optional[admin_pb2.AdminMessage.BackupLocation.ValueType] = 0):
|
||||||
"""Tell the node to set the specified node ID to be un-favorited on the NodeDB on the device"""
|
"""Tell the node to backup preferences to flash."""
|
||||||
self.ensureSessionKey()
|
self.ensureSessionKey()
|
||||||
nodeId = to_node_num(nodeId)
|
|
||||||
|
p = admin_pb2.AdminMessage()
|
||||||
|
p.backup_preferences = location
|
||||||
|
|
||||||
|
if self == self.iface.localNode:
|
||||||
|
onResponse = None
|
||||||
|
else:
|
||||||
|
onResponse = self.onAckNak
|
||||||
|
return self._sendAdmin(p, onResponse=onResponse)
|
||||||
|
|
||||||
|
def restorePreferences(self, location: Optional[admin_pb2.AdminMessage.BackupLocation.ValueType] = 0):
|
||||||
|
"""Tell the node to restore preferences from backup."""
|
||||||
|
self.ensureSessionKey()
|
||||||
|
|
||||||
|
p = admin_pb2.AdminMessage()
|
||||||
|
p.restore_preferences = location
|
||||||
|
|
||||||
|
if self == self.iface.localNode:
|
||||||
|
onResponse = None
|
||||||
|
else:
|
||||||
|
onResponse = self.onAckNak
|
||||||
|
return self._sendAdmin(p, onResponse=onResponse)
|
||||||
|
|
||||||
|
def removePreferencesBackups(self, location: Optional[admin_pb2.AdminMessage.BackupLocation.ValueType] = 0):
|
||||||
|
"""Tell the node to remove backup preferences from the filesystem."""
|
||||||
|
self.ensureSessionKey()
|
||||||
|
|
||||||
|
p = admin_pb2.AdminMessage()
|
||||||
|
p.remove_backup_preferences = location
|
||||||
|
|
||||||
|
if self == self.iface.localNode:
|
||||||
|
onResponse = None
|
||||||
|
else:
|
||||||
|
onResponse = self.onAckNak
|
||||||
|
return self._sendAdmin(p, onResponse=onResponse)
|
||||||
|
|
||||||
|
def removeFavorite(self, nodeId: Union[int, str]):
|
||||||
|
"""Tell the node to set the specified node ID to be un-favorited on the NodeDB on the device."""
|
||||||
|
self.ensureSessionKey()
|
||||||
|
if isinstance(nodeId, str):
|
||||||
|
if nodeId.startswith("!"):
|
||||||
|
nodeId = int(nodeId[1:], 16)
|
||||||
|
else:
|
||||||
|
nodeId = int(nodeId)
|
||||||
|
|
||||||
p = admin_pb2.AdminMessage()
|
p = admin_pb2.AdminMessage()
|
||||||
p.remove_favorite_node = nodeId
|
p.remove_favorite_node = nodeId
|
||||||
@@ -862,7 +767,11 @@ class Node:
|
|||||||
def setIgnored(self, nodeId: Union[int, str]):
|
def setIgnored(self, nodeId: Union[int, str]):
|
||||||
"""Tell the node to set the specified node ID to be ignored on the NodeDB on the device"""
|
"""Tell the node to set the specified node ID to be ignored on the NodeDB on the device"""
|
||||||
self.ensureSessionKey()
|
self.ensureSessionKey()
|
||||||
nodeId = to_node_num(nodeId)
|
if isinstance(nodeId, str):
|
||||||
|
if nodeId.startswith("!"):
|
||||||
|
nodeId = int(nodeId[1:], 16)
|
||||||
|
else:
|
||||||
|
nodeId = int(nodeId)
|
||||||
|
|
||||||
p = admin_pb2.AdminMessage()
|
p = admin_pb2.AdminMessage()
|
||||||
p.set_ignored_node = nodeId
|
p.set_ignored_node = nodeId
|
||||||
@@ -876,7 +785,11 @@ class Node:
|
|||||||
def removeIgnored(self, nodeId: Union[int, str]):
|
def removeIgnored(self, nodeId: Union[int, str]):
|
||||||
"""Tell the node to set the specified node ID to be un-ignored on the NodeDB on the device"""
|
"""Tell the node to set the specified node ID to be un-ignored on the NodeDB on the device"""
|
||||||
self.ensureSessionKey()
|
self.ensureSessionKey()
|
||||||
nodeId = to_node_num(nodeId)
|
if isinstance(nodeId, str):
|
||||||
|
if nodeId.startswith("!"):
|
||||||
|
nodeId = int(nodeId[1:], 16)
|
||||||
|
else:
|
||||||
|
nodeId = int(nodeId)
|
||||||
|
|
||||||
p = admin_pb2.AdminMessage()
|
p = admin_pb2.AdminMessage()
|
||||||
p.remove_ignored_node = nodeId
|
p.remove_ignored_node = nodeId
|
||||||
@@ -892,7 +805,7 @@ class Node:
|
|||||||
self.ensureSessionKey()
|
self.ensureSessionKey()
|
||||||
p = admin_pb2.AdminMessage()
|
p = admin_pb2.AdminMessage()
|
||||||
p.nodedb_reset = True
|
p.nodedb_reset = True
|
||||||
logger.info(f"Telling node to reset the NodeDB")
|
logging.info(f"Telling node to reset the NodeDB")
|
||||||
|
|
||||||
# If sending to a remote node, wait for ACK/NAK
|
# If sending to a remote node, wait for ACK/NAK
|
||||||
if self == self.iface.localNode:
|
if self == self.iface.localNode:
|
||||||
@@ -933,7 +846,7 @@ class Node:
|
|||||||
self.ensureSessionKey()
|
self.ensureSessionKey()
|
||||||
p = admin_pb2.AdminMessage()
|
p = admin_pb2.AdminMessage()
|
||||||
p.remove_fixed_position = True
|
p.remove_fixed_position = True
|
||||||
logger.info(f"Telling node to remove fixed position")
|
logging.info(f"Telling node to remove fixed position")
|
||||||
|
|
||||||
if self == self.iface.localNode:
|
if self == self.iface.localNode:
|
||||||
onResponse = None
|
onResponse = None
|
||||||
@@ -948,7 +861,7 @@ class Node:
|
|||||||
timeSec = int(time.time())
|
timeSec = int(time.time())
|
||||||
p = admin_pb2.AdminMessage()
|
p = admin_pb2.AdminMessage()
|
||||||
p.set_time_only = timeSec
|
p.set_time_only = timeSec
|
||||||
logger.info(f"Setting node time to {timeSec}")
|
logging.info(f"Setting node time to {timeSec}")
|
||||||
|
|
||||||
if self == self.iface.localNode:
|
if self == self.iface.localNode:
|
||||||
onResponse = None
|
onResponse = None
|
||||||
@@ -980,7 +893,7 @@ class Node:
|
|||||||
|
|
||||||
def onRequestGetMetadata(self, p):
|
def onRequestGetMetadata(self, p):
|
||||||
"""Handle the response packet for requesting device metadata getMetadata()"""
|
"""Handle the response packet for requesting device metadata getMetadata()"""
|
||||||
logger.debug(f"onRequestGetMetadata() p:{p}")
|
logging.debug(f"onRequestGetMetadata() p:{p}")
|
||||||
|
|
||||||
if "routing" in p["decoded"]:
|
if "routing" in p["decoded"]:
|
||||||
if p["decoded"]["routing"]["errorReason"] != "NONE":
|
if p["decoded"]["routing"]["errorReason"] != "NONE":
|
||||||
@@ -992,42 +905,30 @@ class Node:
|
|||||||
portnums_pb2.PortNum.ROUTING_APP
|
portnums_pb2.PortNum.ROUTING_APP
|
||||||
):
|
):
|
||||||
if p["decoded"]["routing"]["errorReason"] != "NONE":
|
if p["decoded"]["routing"]["errorReason"] != "NONE":
|
||||||
logger.warning(
|
logging.warning(
|
||||||
f'Metadata request failed, error reason: {p["decoded"]["routing"]["errorReason"]}'
|
f'Metadata request failed, error reason: {p["decoded"]["routing"]["errorReason"]}'
|
||||||
)
|
)
|
||||||
self._timeout.expireTime = time.time() # Do not wait any longer
|
self._timeout.expireTime = time.time() # Do not wait any longer
|
||||||
return # Don't try to parse this routing message
|
return # Don't try to parse this routing message
|
||||||
logger.debug(f"Retrying metadata request.")
|
logging.debug(f"Retrying metadata request.")
|
||||||
self.getMetadata()
|
self.getMetadata()
|
||||||
return
|
return
|
||||||
|
|
||||||
c = p["decoded"]["admin"]["raw"].get_device_metadata_response
|
c = p["decoded"]["admin"]["raw"].get_device_metadata_response
|
||||||
self._timeout.reset() # We made forward progress
|
self._timeout.reset() # We made forward progress
|
||||||
logger.debug(f"Received metadata {stripnl(c)}")
|
logging.debug(f"Received metadata {stripnl(c)}")
|
||||||
print(f"\nfirmware_version: {c.firmware_version}")
|
print(f"\nfirmware_version: {c.firmware_version}")
|
||||||
print(f"device_state_version: {c.device_state_version}")
|
print(f"device_state_version: {c.device_state_version}")
|
||||||
if c.role in config_pb2.Config.DeviceConfig.Role.values():
|
|
||||||
print(f"role: {config_pb2.Config.DeviceConfig.Role.Name(c.role)}")
|
|
||||||
else:
|
|
||||||
print(f"role: {c.role}")
|
|
||||||
print(f"position_flags: {self.position_flags_list(c.position_flags)}")
|
|
||||||
if c.hw_model in mesh_pb2.HardwareModel.values():
|
|
||||||
print(f"hw_model: {mesh_pb2.HardwareModel.Name(c.hw_model)}")
|
|
||||||
else:
|
|
||||||
print(f"hw_model: {c.hw_model}")
|
|
||||||
print(f"hasPKC: {c.hasPKC}")
|
|
||||||
if c.excluded_modules > 0:
|
|
||||||
print(f"excluded_modules: {self.excluded_modules_list(c.excluded_modules)}")
|
|
||||||
|
|
||||||
def onResponseRequestChannel(self, p):
|
def onResponseRequestChannel(self, p):
|
||||||
"""Handle the response packet for requesting a channel _requestChannel()"""
|
"""Handle the response packet for requesting a channel _requestChannel()"""
|
||||||
logger.debug(f"onResponseRequestChannel() p:{p}")
|
logging.debug(f"onResponseRequestChannel() p:{p}")
|
||||||
|
|
||||||
if p["decoded"]["portnum"] == portnums_pb2.PortNum.Name(
|
if p["decoded"]["portnum"] == portnums_pb2.PortNum.Name(
|
||||||
portnums_pb2.PortNum.ROUTING_APP
|
portnums_pb2.PortNum.ROUTING_APP
|
||||||
):
|
):
|
||||||
if p["decoded"]["routing"]["errorReason"] != "NONE":
|
if p["decoded"]["routing"]["errorReason"] != "NONE":
|
||||||
logger.warning(
|
logging.warning(
|
||||||
f'Channel request failed, error reason: {p["decoded"]["routing"]["errorReason"]}'
|
f'Channel request failed, error reason: {p["decoded"]["routing"]["errorReason"]}'
|
||||||
)
|
)
|
||||||
self._timeout.expireTime = time.time() # Do not wait any longer
|
self._timeout.expireTime = time.time() # Do not wait any longer
|
||||||
@@ -1035,18 +936,18 @@ class Node:
|
|||||||
lastTried = 0
|
lastTried = 0
|
||||||
if len(self.partialChannels) > 0:
|
if len(self.partialChannels) > 0:
|
||||||
lastTried = self.partialChannels[-1].index
|
lastTried = self.partialChannels[-1].index
|
||||||
logger.debug(f"Retrying previous channel request.")
|
logging.debug(f"Retrying previous channel request.")
|
||||||
self._requestChannel(lastTried)
|
self._requestChannel(lastTried)
|
||||||
return
|
return
|
||||||
|
|
||||||
c = p["decoded"]["admin"]["raw"].get_channel_response
|
c = p["decoded"]["admin"]["raw"].get_channel_response
|
||||||
self.partialChannels.append(c)
|
self.partialChannels.append(c)
|
||||||
self._timeout.reset() # We made forward progress
|
self._timeout.reset() # We made forward progress
|
||||||
logger.debug(f"Received channel {stripnl(c)}")
|
logging.debug(f"Received channel {stripnl(c)}")
|
||||||
index = c.index
|
index = c.index
|
||||||
|
|
||||||
if index >= 8 - 1:
|
if index >= 8 - 1:
|
||||||
logger.debug("Finished downloading channels")
|
logging.debug("Finished downloading channels")
|
||||||
|
|
||||||
self.channels = self.partialChannels
|
self.channels = self.partialChannels
|
||||||
self._fixupChannels()
|
self._fixupChannels()
|
||||||
@@ -1081,11 +982,11 @@ class Node:
|
|||||||
print(
|
print(
|
||||||
f"Requesting channel {channelNum} info from remote node (this could take a while)"
|
f"Requesting channel {channelNum} info from remote node (this could take a while)"
|
||||||
)
|
)
|
||||||
logger.debug(
|
logging.debug(
|
||||||
f"Requesting channel {channelNum} info from remote node (this could take a while)"
|
f"Requesting channel {channelNum} info from remote node (this could take a while)"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
logger.debug(f"Requesting channel {channelNum}")
|
logging.debug(f"Requesting channel {channelNum}")
|
||||||
|
|
||||||
return self._sendAdmin(
|
return self._sendAdmin(
|
||||||
p, wantResponse=True, onResponse=self.onResponseRequestChannel
|
p, wantResponse=True, onResponse=self.onResponseRequestChannel
|
||||||
@@ -1102,7 +1003,7 @@ class Node:
|
|||||||
"""Send an admin message to the specified node (or the local node if destNodeNum is zero)"""
|
"""Send an admin message to the specified node (or the local node if destNodeNum is zero)"""
|
||||||
|
|
||||||
if self.noProto:
|
if self.noProto:
|
||||||
logger.warning(
|
logging.warning(
|
||||||
f"Not sending packet because protocol use is disabled by noProto"
|
f"Not sending packet because protocol use is disabled by noProto"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
@@ -1110,15 +1011,18 @@ class Node:
|
|||||||
adminIndex == 0
|
adminIndex == 0
|
||||||
): # unless a special channel index was used, we want to use the admin index
|
): # unless a special channel index was used, we want to use the admin index
|
||||||
adminIndex = self.iface.localNode._getAdminChannelIndex()
|
adminIndex = self.iface.localNode._getAdminChannelIndex()
|
||||||
logger.debug(f"adminIndex:{adminIndex}")
|
logging.debug(f"adminIndex:{adminIndex}")
|
||||||
nodeid = to_node_num(self.nodeNum)
|
if isinstance(self.nodeNum, int):
|
||||||
|
nodeid = self.nodeNum
|
||||||
|
else: # assume string starting with !
|
||||||
|
nodeid = int(self.nodeNum[1:],16)
|
||||||
if "adminSessionPassKey" in self.iface._getOrCreateByNum(nodeid):
|
if "adminSessionPassKey" in self.iface._getOrCreateByNum(nodeid):
|
||||||
p.session_passkey = self.iface._getOrCreateByNum(nodeid).get("adminSessionPassKey")
|
p.session_passkey = self.iface._getOrCreateByNum(nodeid).get("adminSessionPassKey")
|
||||||
return self.iface.sendData(
|
return self.iface.sendData(
|
||||||
p,
|
p,
|
||||||
self.nodeNum,
|
self.nodeNum,
|
||||||
portNum=portnums_pb2.PortNum.ADMIN_APP,
|
portNum=portnums_pb2.PortNum.ADMIN_APP,
|
||||||
wantAck=True,
|
wantAck=False,
|
||||||
wantResponse=wantResponse,
|
wantResponse=wantResponse,
|
||||||
onResponse=onResponse,
|
onResponse=onResponse,
|
||||||
channelIndex=adminIndex,
|
channelIndex=adminIndex,
|
||||||
@@ -1128,27 +1032,13 @@ class Node:
|
|||||||
def ensureSessionKey(self):
|
def ensureSessionKey(self):
|
||||||
"""If our entry in iface.nodesByNum doesn't already have an adminSessionPassKey, make a request to get one"""
|
"""If our entry in iface.nodesByNum doesn't already have an adminSessionPassKey, make a request to get one"""
|
||||||
if self.noProto:
|
if self.noProto:
|
||||||
logger.warning(
|
logging.warning(
|
||||||
f"Not ensuring session key, because protocol use is disabled by noProto"
|
f"Not ensuring session key, because protocol use is disabled by noProto"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
nodeid = to_node_num(self.nodeNum)
|
if isinstance(self.nodeNum, int):
|
||||||
|
nodeid = self.nodeNum
|
||||||
|
else: # assume string starting with !
|
||||||
|
nodeid = int(self.nodeNum[1:],16)
|
||||||
if self.iface._getOrCreateByNum(nodeid).get("adminSessionPassKey") is None:
|
if self.iface._getOrCreateByNum(nodeid).get("adminSessionPassKey") is None:
|
||||||
self.requestConfig(admin_pb2.AdminMessage.SESSIONKEY_CONFIG)
|
self.requestConfig(admin_pb2.AdminMessage.SESSIONKEY_CONFIG)
|
||||||
|
|
||||||
def get_channels_with_hash(self):
|
|
||||||
"""Return a list of dicts with channel info and hash."""
|
|
||||||
result = []
|
|
||||||
if self.channels:
|
|
||||||
for c in self.channels:
|
|
||||||
if c.settings and hasattr(c.settings, "name") and hasattr(c.settings, "psk"):
|
|
||||||
hash_val = generate_channel_hash(c.settings.name, c.settings.psk)
|
|
||||||
else:
|
|
||||||
hash_val = None
|
|
||||||
result.append({
|
|
||||||
"index": c.index,
|
|
||||||
"role": channel_pb2.Channel.Role.Name(c.role),
|
|
||||||
"name": c.settings.name if c.settings and hasattr(c.settings, "name") else "",
|
|
||||||
"hash": hash_val,
|
|
||||||
})
|
|
||||||
return result
|
|
||||||
|
|||||||
@@ -1,128 +0,0 @@
|
|||||||
"""Meshtastic ESP32 Unified OTA
|
|
||||||
"""
|
|
||||||
import os
|
|
||||||
import hashlib
|
|
||||||
import socket
|
|
||||||
import logging
|
|
||||||
from typing import Optional, Callable
|
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
def _file_sha256(filename: str):
|
|
||||||
"""Calculate SHA256 hash of a file."""
|
|
||||||
sha256_hash = hashlib.sha256()
|
|
||||||
|
|
||||||
with open(filename, "rb") as f:
|
|
||||||
for byte_block in iter(lambda: f.read(4096), b""):
|
|
||||||
sha256_hash.update(byte_block)
|
|
||||||
|
|
||||||
return sha256_hash
|
|
||||||
|
|
||||||
|
|
||||||
class OTAError(Exception):
|
|
||||||
"""Exception for OTA errors."""
|
|
||||||
|
|
||||||
|
|
||||||
class ESP32WiFiOTA:
|
|
||||||
"""ESP32 WiFi Unified OTA updates."""
|
|
||||||
|
|
||||||
def __init__(self, filename: str, hostname: str, port: int = 3232):
|
|
||||||
self._filename = filename
|
|
||||||
self._hostname = hostname
|
|
||||||
self._port = port
|
|
||||||
self._socket: Optional[socket.socket] = None
|
|
||||||
|
|
||||||
if not os.path.exists(self._filename):
|
|
||||||
raise FileNotFoundError(f"File {self._filename} does not exist")
|
|
||||||
|
|
||||||
self._file_hash = _file_sha256(self._filename)
|
|
||||||
|
|
||||||
def _read_line(self) -> str:
|
|
||||||
"""Read a line from the socket."""
|
|
||||||
if not self._socket:
|
|
||||||
raise ConnectionError("Socket not connected")
|
|
||||||
|
|
||||||
line = b""
|
|
||||||
while not line.endswith(b"\n"):
|
|
||||||
char = self._socket.recv(1)
|
|
||||||
|
|
||||||
if not char:
|
|
||||||
raise ConnectionError("Connection closed while waiting for response")
|
|
||||||
|
|
||||||
line += char
|
|
||||||
|
|
||||||
return line.decode("utf-8").strip()
|
|
||||||
|
|
||||||
def hash_bytes(self) -> bytes:
|
|
||||||
"""Return the hash as bytes."""
|
|
||||||
return self._file_hash.digest()
|
|
||||||
|
|
||||||
def hash_hex(self) -> str:
|
|
||||||
"""Return the hash as a hex string."""
|
|
||||||
return self._file_hash.hexdigest()
|
|
||||||
|
|
||||||
def update(self, progress_callback: Optional[Callable[[int, int], None]] = None):
|
|
||||||
"""Perform the OTA update."""
|
|
||||||
with open(self._filename, "rb") as f:
|
|
||||||
data = f.read()
|
|
||||||
size = len(data)
|
|
||||||
|
|
||||||
logger.info(f"Starting OTA update with {self._filename} ({size} bytes, hash {self.hash_hex()})")
|
|
||||||
|
|
||||||
self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
||||||
self._socket.settimeout(15)
|
|
||||||
try:
|
|
||||||
self._socket.connect((self._hostname, self._port))
|
|
||||||
logger.debug(f"Connected to {self._hostname}:{self._port}")
|
|
||||||
|
|
||||||
# Send start command
|
|
||||||
self._socket.sendall(f"OTA {size} {self.hash_hex()}\n".encode("utf-8"))
|
|
||||||
|
|
||||||
# Wait for OK from the device
|
|
||||||
while True:
|
|
||||||
response = self._read_line()
|
|
||||||
if response == "OK":
|
|
||||||
break
|
|
||||||
|
|
||||||
if response == "ERASING":
|
|
||||||
logger.info("Device is erasing flash...")
|
|
||||||
elif response.startswith("ERR "):
|
|
||||||
raise OTAError(f"Device reported error: {response}")
|
|
||||||
else:
|
|
||||||
logger.warning(f"Unexpected response: {response}")
|
|
||||||
|
|
||||||
# Stream firmware
|
|
||||||
sent_bytes = 0
|
|
||||||
chunk_size = 1024
|
|
||||||
while sent_bytes < size:
|
|
||||||
chunk = data[sent_bytes : sent_bytes + chunk_size]
|
|
||||||
self._socket.sendall(chunk)
|
|
||||||
sent_bytes += len(chunk)
|
|
||||||
|
|
||||||
if progress_callback:
|
|
||||||
progress_callback(sent_bytes, size)
|
|
||||||
else:
|
|
||||||
print(f"[{sent_bytes / size * 100:5.1f}%] Sent {sent_bytes} of {size} bytes...", end="\r")
|
|
||||||
|
|
||||||
if not progress_callback:
|
|
||||||
print()
|
|
||||||
|
|
||||||
# Wait for OK from device
|
|
||||||
logger.info("Firmware sent, waiting for verification...")
|
|
||||||
while True:
|
|
||||||
response = self._read_line()
|
|
||||||
if response == "OK":
|
|
||||||
logger.info("OTA update completed successfully!")
|
|
||||||
break
|
|
||||||
|
|
||||||
if response.startswith("ERR "):
|
|
||||||
raise OTAError(f"OTA update failed: {response}")
|
|
||||||
elif response != "ACK":
|
|
||||||
logger.warning(f"Unexpected final response: {response}")
|
|
||||||
|
|
||||||
finally:
|
|
||||||
if self._socket:
|
|
||||||
self._socket.close()
|
|
||||||
self._socket = None
|
|
||||||
87
meshtastic/protobuf/admin_pb2.py
generated
87
meshtastic/protobuf/admin_pb2.py
generated
File diff suppressed because one or more lines are too long
621
meshtastic/protobuf/admin_pb2.pyi
generated
621
meshtastic/protobuf/admin_pb2.pyi
generated
@@ -25,44 +25,6 @@ else:
|
|||||||
|
|
||||||
DESCRIPTOR: google.protobuf.descriptor.FileDescriptor
|
DESCRIPTOR: google.protobuf.descriptor.FileDescriptor
|
||||||
|
|
||||||
class _OTAMode:
|
|
||||||
ValueType = typing.NewType("ValueType", builtins.int)
|
|
||||||
V: typing_extensions.TypeAlias = ValueType
|
|
||||||
|
|
||||||
class _OTAModeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_OTAMode.ValueType], builtins.type):
|
|
||||||
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
|
|
||||||
NO_REBOOT_OTA: _OTAMode.ValueType # 0
|
|
||||||
"""
|
|
||||||
Do not reboot into OTA mode
|
|
||||||
"""
|
|
||||||
OTA_BLE: _OTAMode.ValueType # 1
|
|
||||||
"""
|
|
||||||
Reboot into OTA mode for BLE firmware update
|
|
||||||
"""
|
|
||||||
OTA_WIFI: _OTAMode.ValueType # 2
|
|
||||||
"""
|
|
||||||
Reboot into OTA mode for WiFi firmware update
|
|
||||||
"""
|
|
||||||
|
|
||||||
class OTAMode(_OTAMode, metaclass=_OTAModeEnumTypeWrapper):
|
|
||||||
"""
|
|
||||||
Firmware update mode for OTA updates
|
|
||||||
"""
|
|
||||||
|
|
||||||
NO_REBOOT_OTA: OTAMode.ValueType # 0
|
|
||||||
"""
|
|
||||||
Do not reboot into OTA mode
|
|
||||||
"""
|
|
||||||
OTA_BLE: OTAMode.ValueType # 1
|
|
||||||
"""
|
|
||||||
Reboot into OTA mode for BLE firmware update
|
|
||||||
"""
|
|
||||||
OTA_WIFI: OTAMode.ValueType # 2
|
|
||||||
"""
|
|
||||||
Reboot into OTA mode for WiFi firmware update
|
|
||||||
"""
|
|
||||||
global___OTAMode = OTAMode
|
|
||||||
|
|
||||||
@typing.final
|
@typing.final
|
||||||
class AdminMessage(google.protobuf.message.Message):
|
class AdminMessage(google.protobuf.message.Message):
|
||||||
"""
|
"""
|
||||||
@@ -112,9 +74,7 @@ class AdminMessage(google.protobuf.message.Message):
|
|||||||
TODO: REPLACE
|
TODO: REPLACE
|
||||||
"""
|
"""
|
||||||
SESSIONKEY_CONFIG: AdminMessage._ConfigType.ValueType # 8
|
SESSIONKEY_CONFIG: AdminMessage._ConfigType.ValueType # 8
|
||||||
"""
|
""""""
|
||||||
Session key config
|
|
||||||
"""
|
|
||||||
DEVICEUI_CONFIG: AdminMessage._ConfigType.ValueType # 9
|
DEVICEUI_CONFIG: AdminMessage._ConfigType.ValueType # 9
|
||||||
"""
|
"""
|
||||||
device-ui config
|
device-ui config
|
||||||
@@ -158,9 +118,7 @@ class AdminMessage(google.protobuf.message.Message):
|
|||||||
TODO: REPLACE
|
TODO: REPLACE
|
||||||
"""
|
"""
|
||||||
SESSIONKEY_CONFIG: AdminMessage.ConfigType.ValueType # 8
|
SESSIONKEY_CONFIG: AdminMessage.ConfigType.ValueType # 8
|
||||||
"""
|
""""""
|
||||||
Session key config
|
|
||||||
"""
|
|
||||||
DEVICEUI_CONFIG: AdminMessage.ConfigType.ValueType # 9
|
DEVICEUI_CONFIG: AdminMessage.ConfigType.ValueType # 9
|
||||||
"""
|
"""
|
||||||
device-ui config
|
device-ui config
|
||||||
@@ -224,18 +182,6 @@ class AdminMessage(google.protobuf.message.Message):
|
|||||||
"""
|
"""
|
||||||
TODO: REPLACE
|
TODO: REPLACE
|
||||||
"""
|
"""
|
||||||
STATUSMESSAGE_CONFIG: AdminMessage._ModuleConfigType.ValueType # 13
|
|
||||||
"""
|
|
||||||
TODO: REPLACE
|
|
||||||
"""
|
|
||||||
TRAFFICMANAGEMENT_CONFIG: AdminMessage._ModuleConfigType.ValueType # 14
|
|
||||||
"""
|
|
||||||
Traffic management module config
|
|
||||||
"""
|
|
||||||
TAK_CONFIG: AdminMessage._ModuleConfigType.ValueType # 15
|
|
||||||
"""
|
|
||||||
TAK module config
|
|
||||||
"""
|
|
||||||
|
|
||||||
class ModuleConfigType(_ModuleConfigType, metaclass=_ModuleConfigTypeEnumTypeWrapper):
|
class ModuleConfigType(_ModuleConfigType, metaclass=_ModuleConfigTypeEnumTypeWrapper):
|
||||||
"""
|
"""
|
||||||
@@ -294,18 +240,6 @@ class AdminMessage(google.protobuf.message.Message):
|
|||||||
"""
|
"""
|
||||||
TODO: REPLACE
|
TODO: REPLACE
|
||||||
"""
|
"""
|
||||||
STATUSMESSAGE_CONFIG: AdminMessage.ModuleConfigType.ValueType # 13
|
|
||||||
"""
|
|
||||||
TODO: REPLACE
|
|
||||||
"""
|
|
||||||
TRAFFICMANAGEMENT_CONFIG: AdminMessage.ModuleConfigType.ValueType # 14
|
|
||||||
"""
|
|
||||||
Traffic management module config
|
|
||||||
"""
|
|
||||||
TAK_CONFIG: AdminMessage.ModuleConfigType.ValueType # 15
|
|
||||||
"""
|
|
||||||
TAK module config
|
|
||||||
"""
|
|
||||||
|
|
||||||
class _BackupLocation:
|
class _BackupLocation:
|
||||||
ValueType = typing.NewType("ValueType", builtins.int)
|
ValueType = typing.NewType("ValueType", builtins.int)
|
||||||
@@ -332,72 +266,6 @@ class AdminMessage(google.protobuf.message.Message):
|
|||||||
Backup to the SD card
|
Backup to the SD card
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@typing.final
|
|
||||||
class InputEvent(google.protobuf.message.Message):
|
|
||||||
"""
|
|
||||||
Input event message to be sent to the node.
|
|
||||||
"""
|
|
||||||
|
|
||||||
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
|
||||||
|
|
||||||
EVENT_CODE_FIELD_NUMBER: builtins.int
|
|
||||||
KB_CHAR_FIELD_NUMBER: builtins.int
|
|
||||||
TOUCH_X_FIELD_NUMBER: builtins.int
|
|
||||||
TOUCH_Y_FIELD_NUMBER: builtins.int
|
|
||||||
event_code: builtins.int
|
|
||||||
"""
|
|
||||||
The input event code
|
|
||||||
"""
|
|
||||||
kb_char: builtins.int
|
|
||||||
"""
|
|
||||||
Keyboard character code
|
|
||||||
"""
|
|
||||||
touch_x: builtins.int
|
|
||||||
"""
|
|
||||||
The touch X coordinate
|
|
||||||
"""
|
|
||||||
touch_y: builtins.int
|
|
||||||
"""
|
|
||||||
The touch Y coordinate
|
|
||||||
"""
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
*,
|
|
||||||
event_code: builtins.int = ...,
|
|
||||||
kb_char: builtins.int = ...,
|
|
||||||
touch_x: builtins.int = ...,
|
|
||||||
touch_y: builtins.int = ...,
|
|
||||||
) -> None: ...
|
|
||||||
def ClearField(self, field_name: typing.Literal["event_code", b"event_code", "kb_char", b"kb_char", "touch_x", b"touch_x", "touch_y", b"touch_y"]) -> None: ...
|
|
||||||
|
|
||||||
@typing.final
|
|
||||||
class OTAEvent(google.protobuf.message.Message):
|
|
||||||
"""
|
|
||||||
User is requesting an over the air update.
|
|
||||||
Node will reboot into the OTA loader
|
|
||||||
"""
|
|
||||||
|
|
||||||
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
|
||||||
|
|
||||||
REBOOT_OTA_MODE_FIELD_NUMBER: builtins.int
|
|
||||||
OTA_HASH_FIELD_NUMBER: builtins.int
|
|
||||||
reboot_ota_mode: global___OTAMode.ValueType
|
|
||||||
"""
|
|
||||||
Tell the node to reboot into OTA mode for firmware update via BLE or WiFi (ESP32 only for now)
|
|
||||||
"""
|
|
||||||
ota_hash: builtins.bytes
|
|
||||||
"""
|
|
||||||
A 32 byte hash of the OTA firmware.
|
|
||||||
Used to verify the integrity of the firmware before applying an update.
|
|
||||||
"""
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
*,
|
|
||||||
reboot_ota_mode: global___OTAMode.ValueType = ...,
|
|
||||||
ota_hash: builtins.bytes = ...,
|
|
||||||
) -> None: ...
|
|
||||||
def ClearField(self, field_name: typing.Literal["ota_hash", b"ota_hash", "reboot_ota_mode", b"reboot_ota_mode"]) -> None: ...
|
|
||||||
|
|
||||||
SESSION_PASSKEY_FIELD_NUMBER: builtins.int
|
SESSION_PASSKEY_FIELD_NUMBER: builtins.int
|
||||||
GET_CHANNEL_REQUEST_FIELD_NUMBER: builtins.int
|
GET_CHANNEL_REQUEST_FIELD_NUMBER: builtins.int
|
||||||
GET_CHANNEL_RESPONSE_FIELD_NUMBER: builtins.int
|
GET_CHANNEL_RESPONSE_FIELD_NUMBER: builtins.int
|
||||||
@@ -424,7 +292,6 @@ class AdminMessage(google.protobuf.message.Message):
|
|||||||
BACKUP_PREFERENCES_FIELD_NUMBER: builtins.int
|
BACKUP_PREFERENCES_FIELD_NUMBER: builtins.int
|
||||||
RESTORE_PREFERENCES_FIELD_NUMBER: builtins.int
|
RESTORE_PREFERENCES_FIELD_NUMBER: builtins.int
|
||||||
REMOVE_BACKUP_PREFERENCES_FIELD_NUMBER: builtins.int
|
REMOVE_BACKUP_PREFERENCES_FIELD_NUMBER: builtins.int
|
||||||
SEND_INPUT_EVENT_FIELD_NUMBER: builtins.int
|
|
||||||
SET_OWNER_FIELD_NUMBER: builtins.int
|
SET_OWNER_FIELD_NUMBER: builtins.int
|
||||||
SET_CHANNEL_FIELD_NUMBER: builtins.int
|
SET_CHANNEL_FIELD_NUMBER: builtins.int
|
||||||
SET_CONFIG_FIELD_NUMBER: builtins.int
|
SET_CONFIG_FIELD_NUMBER: builtins.int
|
||||||
@@ -442,11 +309,8 @@ class AdminMessage(google.protobuf.message.Message):
|
|||||||
STORE_UI_CONFIG_FIELD_NUMBER: builtins.int
|
STORE_UI_CONFIG_FIELD_NUMBER: builtins.int
|
||||||
SET_IGNORED_NODE_FIELD_NUMBER: builtins.int
|
SET_IGNORED_NODE_FIELD_NUMBER: builtins.int
|
||||||
REMOVE_IGNORED_NODE_FIELD_NUMBER: builtins.int
|
REMOVE_IGNORED_NODE_FIELD_NUMBER: builtins.int
|
||||||
TOGGLE_MUTED_NODE_FIELD_NUMBER: builtins.int
|
|
||||||
BEGIN_EDIT_SETTINGS_FIELD_NUMBER: builtins.int
|
BEGIN_EDIT_SETTINGS_FIELD_NUMBER: builtins.int
|
||||||
COMMIT_EDIT_SETTINGS_FIELD_NUMBER: builtins.int
|
COMMIT_EDIT_SETTINGS_FIELD_NUMBER: builtins.int
|
||||||
ADD_CONTACT_FIELD_NUMBER: builtins.int
|
|
||||||
KEY_VERIFICATION_FIELD_NUMBER: builtins.int
|
|
||||||
FACTORY_RESET_DEVICE_FIELD_NUMBER: builtins.int
|
FACTORY_RESET_DEVICE_FIELD_NUMBER: builtins.int
|
||||||
REBOOT_OTA_SECONDS_FIELD_NUMBER: builtins.int
|
REBOOT_OTA_SECONDS_FIELD_NUMBER: builtins.int
|
||||||
EXIT_SIMULATOR_FIELD_NUMBER: builtins.int
|
EXIT_SIMULATOR_FIELD_NUMBER: builtins.int
|
||||||
@@ -454,9 +318,6 @@ class AdminMessage(google.protobuf.message.Message):
|
|||||||
SHUTDOWN_SECONDS_FIELD_NUMBER: builtins.int
|
SHUTDOWN_SECONDS_FIELD_NUMBER: builtins.int
|
||||||
FACTORY_RESET_CONFIG_FIELD_NUMBER: builtins.int
|
FACTORY_RESET_CONFIG_FIELD_NUMBER: builtins.int
|
||||||
NODEDB_RESET_FIELD_NUMBER: builtins.int
|
NODEDB_RESET_FIELD_NUMBER: builtins.int
|
||||||
OTA_REQUEST_FIELD_NUMBER: builtins.int
|
|
||||||
SENSOR_CONFIG_FIELD_NUMBER: builtins.int
|
|
||||||
LOCKDOWN_AUTH_FIELD_NUMBER: builtins.int
|
|
||||||
session_passkey: builtins.bytes
|
session_passkey: builtins.bytes
|
||||||
"""
|
"""
|
||||||
The node generates this key and sends it with any get_x_response packets.
|
The node generates this key and sends it with any get_x_response packets.
|
||||||
@@ -574,10 +435,6 @@ class AdminMessage(google.protobuf.message.Message):
|
|||||||
"""
|
"""
|
||||||
Set specified node-num to be un-ignored on the NodeDB on the device
|
Set specified node-num to be un-ignored on the NodeDB on the device
|
||||||
"""
|
"""
|
||||||
toggle_muted_node: builtins.int
|
|
||||||
"""
|
|
||||||
Set specified node-num to be muted
|
|
||||||
"""
|
|
||||||
begin_edit_settings: builtins.bool
|
begin_edit_settings: builtins.bool
|
||||||
"""
|
"""
|
||||||
Begins an edit transaction for config, module config, owner, and channel settings changes
|
Begins an edit transaction for config, module config, owner, and channel settings changes
|
||||||
@@ -595,7 +452,6 @@ class AdminMessage(google.protobuf.message.Message):
|
|||||||
"""
|
"""
|
||||||
Tell the node to reboot into the OTA Firmware in this many seconds (or <0 to cancel reboot)
|
Tell the node to reboot into the OTA Firmware in this many seconds (or <0 to cancel reboot)
|
||||||
Only Implemented for ESP32 Devices. This needs to be issued to send a new main firmware via bluetooth.
|
Only Implemented for ESP32 Devices. This needs to be issued to send a new main firmware via bluetooth.
|
||||||
Deprecated in favor of reboot_ota_mode in 2.7.17
|
|
||||||
"""
|
"""
|
||||||
exit_simulator: builtins.bool
|
exit_simulator: builtins.bool
|
||||||
"""
|
"""
|
||||||
@@ -614,10 +470,9 @@ class AdminMessage(google.protobuf.message.Message):
|
|||||||
"""
|
"""
|
||||||
Tell the node to factory reset config; all device state and configuration will be returned to factory defaults; BLE bonds will be preserved.
|
Tell the node to factory reset config; all device state and configuration will be returned to factory defaults; BLE bonds will be preserved.
|
||||||
"""
|
"""
|
||||||
nodedb_reset: builtins.bool
|
nodedb_reset: builtins.int
|
||||||
"""
|
"""
|
||||||
Tell the node to reset the nodedb.
|
Tell the node to reset the nodedb.
|
||||||
When true, favorites are preserved through reset.
|
|
||||||
"""
|
"""
|
||||||
@property
|
@property
|
||||||
def get_channel_response(self) -> meshtastic.protobuf.channel_pb2.Channel:
|
def get_channel_response(self) -> meshtastic.protobuf.channel_pb2.Channel:
|
||||||
@@ -667,13 +522,6 @@ class AdminMessage(google.protobuf.message.Message):
|
|||||||
Respond with the mesh's nodes with their available gpio pins for RemoteHardware module use
|
Respond with the mesh's nodes with their available gpio pins for RemoteHardware module use
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@property
|
|
||||||
def send_input_event(self) -> global___AdminMessage.InputEvent:
|
|
||||||
"""
|
|
||||||
Send an input event to the node.
|
|
||||||
This is used to trigger physical input events like button presses, touch events, etc.
|
|
||||||
"""
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def set_owner(self) -> meshtastic.protobuf.mesh_pb2.User:
|
def set_owner(self) -> meshtastic.protobuf.mesh_pb2.User:
|
||||||
"""
|
"""
|
||||||
@@ -720,43 +568,6 @@ class AdminMessage(google.protobuf.message.Message):
|
|||||||
Tell the node to store UI data persistently.
|
Tell the node to store UI data persistently.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@property
|
|
||||||
def add_contact(self) -> global___SharedContact:
|
|
||||||
"""
|
|
||||||
Add a contact (User) to the nodedb
|
|
||||||
"""
|
|
||||||
|
|
||||||
@property
|
|
||||||
def key_verification(self) -> global___KeyVerificationAdmin:
|
|
||||||
"""
|
|
||||||
Initiate or respond to a key verification request
|
|
||||||
"""
|
|
||||||
|
|
||||||
@property
|
|
||||||
def ota_request(self) -> global___AdminMessage.OTAEvent:
|
|
||||||
"""
|
|
||||||
Tell the node to reset into the OTA Loader
|
|
||||||
"""
|
|
||||||
|
|
||||||
@property
|
|
||||||
def sensor_config(self) -> global___SensorConfig:
|
|
||||||
"""
|
|
||||||
Parameters and sensor configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
@property
|
|
||||||
def lockdown_auth(self) -> global___LockdownAuth:
|
|
||||||
"""
|
|
||||||
Lockdown passphrase delivery / unlock / lock-now command for hardened
|
|
||||||
firmware builds (see MESHTASTIC_LOCKDOWN). Used to provision the
|
|
||||||
passphrase on first boot, unlock encrypted storage on subsequent
|
|
||||||
reboots, re-verify on already-unlocked devices to authorize a new
|
|
||||||
client connection, or immediately re-lock the device.
|
|
||||||
|
|
||||||
Replaces the earlier scheme that repurposed SecurityConfig.private_key
|
|
||||||
to carry passphrase bytes; that hack is retired.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
@@ -786,7 +597,6 @@ class AdminMessage(google.protobuf.message.Message):
|
|||||||
backup_preferences: global___AdminMessage.BackupLocation.ValueType = ...,
|
backup_preferences: global___AdminMessage.BackupLocation.ValueType = ...,
|
||||||
restore_preferences: global___AdminMessage.BackupLocation.ValueType = ...,
|
restore_preferences: global___AdminMessage.BackupLocation.ValueType = ...,
|
||||||
remove_backup_preferences: global___AdminMessage.BackupLocation.ValueType = ...,
|
remove_backup_preferences: global___AdminMessage.BackupLocation.ValueType = ...,
|
||||||
send_input_event: global___AdminMessage.InputEvent | None = ...,
|
|
||||||
set_owner: meshtastic.protobuf.mesh_pb2.User | None = ...,
|
set_owner: meshtastic.protobuf.mesh_pb2.User | None = ...,
|
||||||
set_channel: meshtastic.protobuf.channel_pb2.Channel | None = ...,
|
set_channel: meshtastic.protobuf.channel_pb2.Channel | None = ...,
|
||||||
set_config: meshtastic.protobuf.config_pb2.Config | None = ...,
|
set_config: meshtastic.protobuf.config_pb2.Config | None = ...,
|
||||||
@@ -804,90 +614,22 @@ class AdminMessage(google.protobuf.message.Message):
|
|||||||
store_ui_config: meshtastic.protobuf.device_ui_pb2.DeviceUIConfig | None = ...,
|
store_ui_config: meshtastic.protobuf.device_ui_pb2.DeviceUIConfig | None = ...,
|
||||||
set_ignored_node: builtins.int = ...,
|
set_ignored_node: builtins.int = ...,
|
||||||
remove_ignored_node: builtins.int = ...,
|
remove_ignored_node: builtins.int = ...,
|
||||||
toggle_muted_node: builtins.int = ...,
|
|
||||||
begin_edit_settings: builtins.bool = ...,
|
begin_edit_settings: builtins.bool = ...,
|
||||||
commit_edit_settings: builtins.bool = ...,
|
commit_edit_settings: builtins.bool = ...,
|
||||||
add_contact: global___SharedContact | None = ...,
|
|
||||||
key_verification: global___KeyVerificationAdmin | None = ...,
|
|
||||||
factory_reset_device: builtins.int = ...,
|
factory_reset_device: builtins.int = ...,
|
||||||
reboot_ota_seconds: builtins.int = ...,
|
reboot_ota_seconds: builtins.int = ...,
|
||||||
exit_simulator: builtins.bool = ...,
|
exit_simulator: builtins.bool = ...,
|
||||||
reboot_seconds: builtins.int = ...,
|
reboot_seconds: builtins.int = ...,
|
||||||
shutdown_seconds: builtins.int = ...,
|
shutdown_seconds: builtins.int = ...,
|
||||||
factory_reset_config: builtins.int = ...,
|
factory_reset_config: builtins.int = ...,
|
||||||
nodedb_reset: builtins.bool = ...,
|
nodedb_reset: builtins.int = ...,
|
||||||
ota_request: global___AdminMessage.OTAEvent | None = ...,
|
|
||||||
sensor_config: global___SensorConfig | None = ...,
|
|
||||||
lockdown_auth: global___LockdownAuth | None = ...,
|
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
def HasField(self, field_name: typing.Literal["add_contact", b"add_contact", "backup_preferences", b"backup_preferences", "begin_edit_settings", b"begin_edit_settings", "commit_edit_settings", b"commit_edit_settings", "delete_file_request", b"delete_file_request", "enter_dfu_mode_request", b"enter_dfu_mode_request", "exit_simulator", b"exit_simulator", "factory_reset_config", b"factory_reset_config", "factory_reset_device", b"factory_reset_device", "get_canned_message_module_messages_request", b"get_canned_message_module_messages_request", "get_canned_message_module_messages_response", b"get_canned_message_module_messages_response", "get_channel_request", b"get_channel_request", "get_channel_response", b"get_channel_response", "get_config_request", b"get_config_request", "get_config_response", b"get_config_response", "get_device_connection_status_request", b"get_device_connection_status_request", "get_device_connection_status_response", b"get_device_connection_status_response", "get_device_metadata_request", b"get_device_metadata_request", "get_device_metadata_response", b"get_device_metadata_response", "get_module_config_request", b"get_module_config_request", "get_module_config_response", b"get_module_config_response", "get_node_remote_hardware_pins_request", b"get_node_remote_hardware_pins_request", "get_node_remote_hardware_pins_response", b"get_node_remote_hardware_pins_response", "get_owner_request", b"get_owner_request", "get_owner_response", b"get_owner_response", "get_ringtone_request", b"get_ringtone_request", "get_ringtone_response", b"get_ringtone_response", "get_ui_config_request", b"get_ui_config_request", "get_ui_config_response", b"get_ui_config_response", "key_verification", b"key_verification", "lockdown_auth", b"lockdown_auth", "nodedb_reset", b"nodedb_reset", "ota_request", b"ota_request", "payload_variant", b"payload_variant", "reboot_ota_seconds", b"reboot_ota_seconds", "reboot_seconds", b"reboot_seconds", "remove_backup_preferences", b"remove_backup_preferences", "remove_by_nodenum", b"remove_by_nodenum", "remove_favorite_node", b"remove_favorite_node", "remove_fixed_position", b"remove_fixed_position", "remove_ignored_node", b"remove_ignored_node", "restore_preferences", b"restore_preferences", "send_input_event", b"send_input_event", "sensor_config", b"sensor_config", "set_canned_message_module_messages", b"set_canned_message_module_messages", "set_channel", b"set_channel", "set_config", b"set_config", "set_favorite_node", b"set_favorite_node", "set_fixed_position", b"set_fixed_position", "set_ham_mode", b"set_ham_mode", "set_ignored_node", b"set_ignored_node", "set_module_config", b"set_module_config", "set_owner", b"set_owner", "set_ringtone_message", b"set_ringtone_message", "set_scale", b"set_scale", "set_time_only", b"set_time_only", "shutdown_seconds", b"shutdown_seconds", "store_ui_config", b"store_ui_config", "toggle_muted_node", b"toggle_muted_node"]) -> builtins.bool: ...
|
def HasField(self, field_name: typing.Literal["backup_preferences", b"backup_preferences", "begin_edit_settings", b"begin_edit_settings", "commit_edit_settings", b"commit_edit_settings", "delete_file_request", b"delete_file_request", "enter_dfu_mode_request", b"enter_dfu_mode_request", "exit_simulator", b"exit_simulator", "factory_reset_config", b"factory_reset_config", "factory_reset_device", b"factory_reset_device", "get_canned_message_module_messages_request", b"get_canned_message_module_messages_request", "get_canned_message_module_messages_response", b"get_canned_message_module_messages_response", "get_channel_request", b"get_channel_request", "get_channel_response", b"get_channel_response", "get_config_request", b"get_config_request", "get_config_response", b"get_config_response", "get_device_connection_status_request", b"get_device_connection_status_request", "get_device_connection_status_response", b"get_device_connection_status_response", "get_device_metadata_request", b"get_device_metadata_request", "get_device_metadata_response", b"get_device_metadata_response", "get_module_config_request", b"get_module_config_request", "get_module_config_response", b"get_module_config_response", "get_node_remote_hardware_pins_request", b"get_node_remote_hardware_pins_request", "get_node_remote_hardware_pins_response", b"get_node_remote_hardware_pins_response", "get_owner_request", b"get_owner_request", "get_owner_response", b"get_owner_response", "get_ringtone_request", b"get_ringtone_request", "get_ringtone_response", b"get_ringtone_response", "get_ui_config_request", b"get_ui_config_request", "get_ui_config_response", b"get_ui_config_response", "nodedb_reset", b"nodedb_reset", "payload_variant", b"payload_variant", "reboot_ota_seconds", b"reboot_ota_seconds", "reboot_seconds", b"reboot_seconds", "remove_backup_preferences", b"remove_backup_preferences", "remove_by_nodenum", b"remove_by_nodenum", "remove_favorite_node", b"remove_favorite_node", "remove_fixed_position", b"remove_fixed_position", "remove_ignored_node", b"remove_ignored_node", "restore_preferences", b"restore_preferences", "set_canned_message_module_messages", b"set_canned_message_module_messages", "set_channel", b"set_channel", "set_config", b"set_config", "set_favorite_node", b"set_favorite_node", "set_fixed_position", b"set_fixed_position", "set_ham_mode", b"set_ham_mode", "set_ignored_node", b"set_ignored_node", "set_module_config", b"set_module_config", "set_owner", b"set_owner", "set_ringtone_message", b"set_ringtone_message", "set_scale", b"set_scale", "set_time_only", b"set_time_only", "shutdown_seconds", b"shutdown_seconds", "store_ui_config", b"store_ui_config"]) -> builtins.bool: ...
|
||||||
def ClearField(self, field_name: typing.Literal["add_contact", b"add_contact", "backup_preferences", b"backup_preferences", "begin_edit_settings", b"begin_edit_settings", "commit_edit_settings", b"commit_edit_settings", "delete_file_request", b"delete_file_request", "enter_dfu_mode_request", b"enter_dfu_mode_request", "exit_simulator", b"exit_simulator", "factory_reset_config", b"factory_reset_config", "factory_reset_device", b"factory_reset_device", "get_canned_message_module_messages_request", b"get_canned_message_module_messages_request", "get_canned_message_module_messages_response", b"get_canned_message_module_messages_response", "get_channel_request", b"get_channel_request", "get_channel_response", b"get_channel_response", "get_config_request", b"get_config_request", "get_config_response", b"get_config_response", "get_device_connection_status_request", b"get_device_connection_status_request", "get_device_connection_status_response", b"get_device_connection_status_response", "get_device_metadata_request", b"get_device_metadata_request", "get_device_metadata_response", b"get_device_metadata_response", "get_module_config_request", b"get_module_config_request", "get_module_config_response", b"get_module_config_response", "get_node_remote_hardware_pins_request", b"get_node_remote_hardware_pins_request", "get_node_remote_hardware_pins_response", b"get_node_remote_hardware_pins_response", "get_owner_request", b"get_owner_request", "get_owner_response", b"get_owner_response", "get_ringtone_request", b"get_ringtone_request", "get_ringtone_response", b"get_ringtone_response", "get_ui_config_request", b"get_ui_config_request", "get_ui_config_response", b"get_ui_config_response", "key_verification", b"key_verification", "lockdown_auth", b"lockdown_auth", "nodedb_reset", b"nodedb_reset", "ota_request", b"ota_request", "payload_variant", b"payload_variant", "reboot_ota_seconds", b"reboot_ota_seconds", "reboot_seconds", b"reboot_seconds", "remove_backup_preferences", b"remove_backup_preferences", "remove_by_nodenum", b"remove_by_nodenum", "remove_favorite_node", b"remove_favorite_node", "remove_fixed_position", b"remove_fixed_position", "remove_ignored_node", b"remove_ignored_node", "restore_preferences", b"restore_preferences", "send_input_event", b"send_input_event", "sensor_config", b"sensor_config", "session_passkey", b"session_passkey", "set_canned_message_module_messages", b"set_canned_message_module_messages", "set_channel", b"set_channel", "set_config", b"set_config", "set_favorite_node", b"set_favorite_node", "set_fixed_position", b"set_fixed_position", "set_ham_mode", b"set_ham_mode", "set_ignored_node", b"set_ignored_node", "set_module_config", b"set_module_config", "set_owner", b"set_owner", "set_ringtone_message", b"set_ringtone_message", "set_scale", b"set_scale", "set_time_only", b"set_time_only", "shutdown_seconds", b"shutdown_seconds", "store_ui_config", b"store_ui_config", "toggle_muted_node", b"toggle_muted_node"]) -> None: ...
|
def ClearField(self, field_name: typing.Literal["backup_preferences", b"backup_preferences", "begin_edit_settings", b"begin_edit_settings", "commit_edit_settings", b"commit_edit_settings", "delete_file_request", b"delete_file_request", "enter_dfu_mode_request", b"enter_dfu_mode_request", "exit_simulator", b"exit_simulator", "factory_reset_config", b"factory_reset_config", "factory_reset_device", b"factory_reset_device", "get_canned_message_module_messages_request", b"get_canned_message_module_messages_request", "get_canned_message_module_messages_response", b"get_canned_message_module_messages_response", "get_channel_request", b"get_channel_request", "get_channel_response", b"get_channel_response", "get_config_request", b"get_config_request", "get_config_response", b"get_config_response", "get_device_connection_status_request", b"get_device_connection_status_request", "get_device_connection_status_response", b"get_device_connection_status_response", "get_device_metadata_request", b"get_device_metadata_request", "get_device_metadata_response", b"get_device_metadata_response", "get_module_config_request", b"get_module_config_request", "get_module_config_response", b"get_module_config_response", "get_node_remote_hardware_pins_request", b"get_node_remote_hardware_pins_request", "get_node_remote_hardware_pins_response", b"get_node_remote_hardware_pins_response", "get_owner_request", b"get_owner_request", "get_owner_response", b"get_owner_response", "get_ringtone_request", b"get_ringtone_request", "get_ringtone_response", b"get_ringtone_response", "get_ui_config_request", b"get_ui_config_request", "get_ui_config_response", b"get_ui_config_response", "nodedb_reset", b"nodedb_reset", "payload_variant", b"payload_variant", "reboot_ota_seconds", b"reboot_ota_seconds", "reboot_seconds", b"reboot_seconds", "remove_backup_preferences", b"remove_backup_preferences", "remove_by_nodenum", b"remove_by_nodenum", "remove_favorite_node", b"remove_favorite_node", "remove_fixed_position", b"remove_fixed_position", "remove_ignored_node", b"remove_ignored_node", "restore_preferences", b"restore_preferences", "session_passkey", b"session_passkey", "set_canned_message_module_messages", b"set_canned_message_module_messages", "set_channel", b"set_channel", "set_config", b"set_config", "set_favorite_node", b"set_favorite_node", "set_fixed_position", b"set_fixed_position", "set_ham_mode", b"set_ham_mode", "set_ignored_node", b"set_ignored_node", "set_module_config", b"set_module_config", "set_owner", b"set_owner", "set_ringtone_message", b"set_ringtone_message", "set_scale", b"set_scale", "set_time_only", b"set_time_only", "shutdown_seconds", b"shutdown_seconds", "store_ui_config", b"store_ui_config"]) -> None: ...
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["payload_variant", b"payload_variant"]) -> typing.Literal["get_channel_request", "get_channel_response", "get_owner_request", "get_owner_response", "get_config_request", "get_config_response", "get_module_config_request", "get_module_config_response", "get_canned_message_module_messages_request", "get_canned_message_module_messages_response", "get_device_metadata_request", "get_device_metadata_response", "get_ringtone_request", "get_ringtone_response", "get_device_connection_status_request", "get_device_connection_status_response", "set_ham_mode", "get_node_remote_hardware_pins_request", "get_node_remote_hardware_pins_response", "enter_dfu_mode_request", "delete_file_request", "set_scale", "backup_preferences", "restore_preferences", "remove_backup_preferences", "send_input_event", "set_owner", "set_channel", "set_config", "set_module_config", "set_canned_message_module_messages", "set_ringtone_message", "remove_by_nodenum", "set_favorite_node", "remove_favorite_node", "set_fixed_position", "remove_fixed_position", "set_time_only", "get_ui_config_request", "get_ui_config_response", "store_ui_config", "set_ignored_node", "remove_ignored_node", "toggle_muted_node", "begin_edit_settings", "commit_edit_settings", "add_contact", "key_verification", "factory_reset_device", "reboot_ota_seconds", "exit_simulator", "reboot_seconds", "shutdown_seconds", "factory_reset_config", "nodedb_reset", "ota_request", "sensor_config", "lockdown_auth"] | None: ...
|
def WhichOneof(self, oneof_group: typing.Literal["payload_variant", b"payload_variant"]) -> typing.Literal["get_channel_request", "get_channel_response", "get_owner_request", "get_owner_response", "get_config_request", "get_config_response", "get_module_config_request", "get_module_config_response", "get_canned_message_module_messages_request", "get_canned_message_module_messages_response", "get_device_metadata_request", "get_device_metadata_response", "get_ringtone_request", "get_ringtone_response", "get_device_connection_status_request", "get_device_connection_status_response", "set_ham_mode", "get_node_remote_hardware_pins_request", "get_node_remote_hardware_pins_response", "enter_dfu_mode_request", "delete_file_request", "set_scale", "backup_preferences", "restore_preferences", "remove_backup_preferences", "set_owner", "set_channel", "set_config", "set_module_config", "set_canned_message_module_messages", "set_ringtone_message", "remove_by_nodenum", "set_favorite_node", "remove_favorite_node", "set_fixed_position", "remove_fixed_position", "set_time_only", "get_ui_config_request", "get_ui_config_response", "store_ui_config", "set_ignored_node", "remove_ignored_node", "begin_edit_settings", "commit_edit_settings", "factory_reset_device", "reboot_ota_seconds", "exit_simulator", "reboot_seconds", "shutdown_seconds", "factory_reset_config", "nodedb_reset"] | None: ...
|
||||||
|
|
||||||
global___AdminMessage = AdminMessage
|
global___AdminMessage = AdminMessage
|
||||||
|
|
||||||
@typing.final
|
|
||||||
class LockdownAuth(google.protobuf.message.Message):
|
|
||||||
"""
|
|
||||||
Lockdown passphrase delivery payload.
|
|
||||||
|
|
||||||
One message handles three operations distinguished by content:
|
|
||||||
- Provision (first-time): passphrase set, lock_now=false. Firmware
|
|
||||||
generates DEK, wraps with passphrase-derived KEK, persists.
|
|
||||||
- Unlock: passphrase set, lock_now=false. Firmware verifies
|
|
||||||
passphrase against stored DEK, unlocks storage, authorizes the
|
|
||||||
connection that delivered this packet.
|
|
||||||
- Lock now: lock_now=true, passphrase ignored. Firmware revokes
|
|
||||||
all client auth and reboots into the locked state.
|
|
||||||
|
|
||||||
Firmware decides between provision and unlock based on its own state
|
|
||||||
(whether a DEK file already exists). Clients do not need to track
|
|
||||||
which case applies.
|
|
||||||
"""
|
|
||||||
|
|
||||||
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
|
||||||
|
|
||||||
PASSPHRASE_FIELD_NUMBER: builtins.int
|
|
||||||
BOOTS_REMAINING_FIELD_NUMBER: builtins.int
|
|
||||||
VALID_UNTIL_EPOCH_FIELD_NUMBER: builtins.int
|
|
||||||
LOCK_NOW_FIELD_NUMBER: builtins.int
|
|
||||||
passphrase: builtins.bytes
|
|
||||||
"""
|
|
||||||
Passphrase bytes (1-32). Empty when lock_now is true.
|
|
||||||
Capped to 32 to match the proto cap on related security fields.
|
|
||||||
"""
|
|
||||||
boots_remaining: builtins.int
|
|
||||||
"""
|
|
||||||
Optional override of the boot-count token TTL granted on success.
|
|
||||||
0 = use firmware default (TOKEN_DEFAULT_BOOTS).
|
|
||||||
On reboot the firmware decrements this; when it reaches 0 the
|
|
||||||
device boots fully locked and requires a fresh passphrase.
|
|
||||||
"""
|
|
||||||
valid_until_epoch: builtins.int
|
|
||||||
"""
|
|
||||||
Optional wall-clock expiry for the unlock token, as absolute
|
|
||||||
Unix-epoch seconds. 0 = no time limit (only the boot-count TTL
|
|
||||||
applies). On boot, if the device RTC is set and now > this value,
|
|
||||||
the token is treated as expired.
|
|
||||||
"""
|
|
||||||
lock_now: builtins.bool
|
|
||||||
"""
|
|
||||||
If true, ignore passphrase fields, immediately revoke all
|
|
||||||
connection-level admin authorization, and reboot the device into
|
|
||||||
the locked state. Always honoured regardless of current lock state.
|
|
||||||
"""
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
*,
|
|
||||||
passphrase: builtins.bytes = ...,
|
|
||||||
boots_remaining: builtins.int = ...,
|
|
||||||
valid_until_epoch: builtins.int = ...,
|
|
||||||
lock_now: builtins.bool = ...,
|
|
||||||
) -> None: ...
|
|
||||||
def ClearField(self, field_name: typing.Literal["boots_remaining", b"boots_remaining", "lock_now", b"lock_now", "passphrase", b"passphrase", "valid_until_epoch", b"valid_until_epoch"]) -> None: ...
|
|
||||||
|
|
||||||
global___LockdownAuth = LockdownAuth
|
|
||||||
|
|
||||||
@typing.final
|
@typing.final
|
||||||
class HamParameters(google.protobuf.message.Message):
|
class HamParameters(google.protobuf.message.Message):
|
||||||
"""
|
"""
|
||||||
@@ -953,352 +695,3 @@ class NodeRemoteHardwarePinsResponse(google.protobuf.message.Message):
|
|||||||
def ClearField(self, field_name: typing.Literal["node_remote_hardware_pins", b"node_remote_hardware_pins"]) -> None: ...
|
def ClearField(self, field_name: typing.Literal["node_remote_hardware_pins", b"node_remote_hardware_pins"]) -> None: ...
|
||||||
|
|
||||||
global___NodeRemoteHardwarePinsResponse = NodeRemoteHardwarePinsResponse
|
global___NodeRemoteHardwarePinsResponse = NodeRemoteHardwarePinsResponse
|
||||||
|
|
||||||
@typing.final
|
|
||||||
class SharedContact(google.protobuf.message.Message):
|
|
||||||
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
|
||||||
|
|
||||||
NODE_NUM_FIELD_NUMBER: builtins.int
|
|
||||||
USER_FIELD_NUMBER: builtins.int
|
|
||||||
SHOULD_IGNORE_FIELD_NUMBER: builtins.int
|
|
||||||
MANUALLY_VERIFIED_FIELD_NUMBER: builtins.int
|
|
||||||
node_num: builtins.int
|
|
||||||
"""
|
|
||||||
The node number of the contact
|
|
||||||
"""
|
|
||||||
should_ignore: builtins.bool
|
|
||||||
"""
|
|
||||||
Add this contact to the blocked / ignored list
|
|
||||||
"""
|
|
||||||
manually_verified: builtins.bool
|
|
||||||
"""
|
|
||||||
Set the IS_KEY_MANUALLY_VERIFIED bit
|
|
||||||
"""
|
|
||||||
@property
|
|
||||||
def user(self) -> meshtastic.protobuf.mesh_pb2.User:
|
|
||||||
"""
|
|
||||||
The User of the contact
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
*,
|
|
||||||
node_num: builtins.int = ...,
|
|
||||||
user: meshtastic.protobuf.mesh_pb2.User | None = ...,
|
|
||||||
should_ignore: builtins.bool = ...,
|
|
||||||
manually_verified: builtins.bool = ...,
|
|
||||||
) -> None: ...
|
|
||||||
def HasField(self, field_name: typing.Literal["user", b"user"]) -> builtins.bool: ...
|
|
||||||
def ClearField(self, field_name: typing.Literal["manually_verified", b"manually_verified", "node_num", b"node_num", "should_ignore", b"should_ignore", "user", b"user"]) -> None: ...
|
|
||||||
|
|
||||||
global___SharedContact = SharedContact
|
|
||||||
|
|
||||||
@typing.final
|
|
||||||
class KeyVerificationAdmin(google.protobuf.message.Message):
|
|
||||||
"""
|
|
||||||
This message is used by a client to initiate or complete a key verification
|
|
||||||
"""
|
|
||||||
|
|
||||||
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
|
||||||
|
|
||||||
class _MessageType:
|
|
||||||
ValueType = typing.NewType("ValueType", builtins.int)
|
|
||||||
V: typing_extensions.TypeAlias = ValueType
|
|
||||||
|
|
||||||
class _MessageTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[KeyVerificationAdmin._MessageType.ValueType], builtins.type):
|
|
||||||
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
|
|
||||||
INITIATE_VERIFICATION: KeyVerificationAdmin._MessageType.ValueType # 0
|
|
||||||
"""
|
|
||||||
This is the first stage, where a client initiates
|
|
||||||
"""
|
|
||||||
PROVIDE_SECURITY_NUMBER: KeyVerificationAdmin._MessageType.ValueType # 1
|
|
||||||
"""
|
|
||||||
After the nonce has been returned over the mesh, the client prompts for the security number
|
|
||||||
And uses this message to provide it to the node.
|
|
||||||
"""
|
|
||||||
DO_VERIFY: KeyVerificationAdmin._MessageType.ValueType # 2
|
|
||||||
"""
|
|
||||||
Once the user has compared the verification message, this message notifies the node.
|
|
||||||
"""
|
|
||||||
DO_NOT_VERIFY: KeyVerificationAdmin._MessageType.ValueType # 3
|
|
||||||
"""
|
|
||||||
This is the cancel path, can be taken at any point
|
|
||||||
"""
|
|
||||||
|
|
||||||
class MessageType(_MessageType, metaclass=_MessageTypeEnumTypeWrapper):
|
|
||||||
"""
|
|
||||||
Three stages of this request.
|
|
||||||
"""
|
|
||||||
|
|
||||||
INITIATE_VERIFICATION: KeyVerificationAdmin.MessageType.ValueType # 0
|
|
||||||
"""
|
|
||||||
This is the first stage, where a client initiates
|
|
||||||
"""
|
|
||||||
PROVIDE_SECURITY_NUMBER: KeyVerificationAdmin.MessageType.ValueType # 1
|
|
||||||
"""
|
|
||||||
After the nonce has been returned over the mesh, the client prompts for the security number
|
|
||||||
And uses this message to provide it to the node.
|
|
||||||
"""
|
|
||||||
DO_VERIFY: KeyVerificationAdmin.MessageType.ValueType # 2
|
|
||||||
"""
|
|
||||||
Once the user has compared the verification message, this message notifies the node.
|
|
||||||
"""
|
|
||||||
DO_NOT_VERIFY: KeyVerificationAdmin.MessageType.ValueType # 3
|
|
||||||
"""
|
|
||||||
This is the cancel path, can be taken at any point
|
|
||||||
"""
|
|
||||||
|
|
||||||
MESSAGE_TYPE_FIELD_NUMBER: builtins.int
|
|
||||||
REMOTE_NODENUM_FIELD_NUMBER: builtins.int
|
|
||||||
NONCE_FIELD_NUMBER: builtins.int
|
|
||||||
SECURITY_NUMBER_FIELD_NUMBER: builtins.int
|
|
||||||
message_type: global___KeyVerificationAdmin.MessageType.ValueType
|
|
||||||
remote_nodenum: builtins.int
|
|
||||||
"""
|
|
||||||
The nodenum we're requesting
|
|
||||||
"""
|
|
||||||
nonce: builtins.int
|
|
||||||
"""
|
|
||||||
The nonce is used to track the connection
|
|
||||||
"""
|
|
||||||
security_number: builtins.int
|
|
||||||
"""
|
|
||||||
The 4 digit code generated by the remote node, and communicated outside the mesh
|
|
||||||
"""
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
*,
|
|
||||||
message_type: global___KeyVerificationAdmin.MessageType.ValueType = ...,
|
|
||||||
remote_nodenum: builtins.int = ...,
|
|
||||||
nonce: builtins.int = ...,
|
|
||||||
security_number: builtins.int | None = ...,
|
|
||||||
) -> None: ...
|
|
||||||
def HasField(self, field_name: typing.Literal["_security_number", b"_security_number", "security_number", b"security_number"]) -> builtins.bool: ...
|
|
||||||
def ClearField(self, field_name: typing.Literal["_security_number", b"_security_number", "message_type", b"message_type", "nonce", b"nonce", "remote_nodenum", b"remote_nodenum", "security_number", b"security_number"]) -> None: ...
|
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_security_number", b"_security_number"]) -> typing.Literal["security_number"] | None: ...
|
|
||||||
|
|
||||||
global___KeyVerificationAdmin = KeyVerificationAdmin
|
|
||||||
|
|
||||||
@typing.final
|
|
||||||
class SensorConfig(google.protobuf.message.Message):
|
|
||||||
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
|
||||||
|
|
||||||
SCD4X_CONFIG_FIELD_NUMBER: builtins.int
|
|
||||||
SEN5X_CONFIG_FIELD_NUMBER: builtins.int
|
|
||||||
SCD30_CONFIG_FIELD_NUMBER: builtins.int
|
|
||||||
SHTXX_CONFIG_FIELD_NUMBER: builtins.int
|
|
||||||
@property
|
|
||||||
def scd4x_config(self) -> global___SCD4X_config:
|
|
||||||
"""
|
|
||||||
SCD4X CO2 Sensor configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
@property
|
|
||||||
def sen5x_config(self) -> global___SEN5X_config:
|
|
||||||
"""
|
|
||||||
SEN5X PM Sensor configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
@property
|
|
||||||
def scd30_config(self) -> global___SCD30_config:
|
|
||||||
"""
|
|
||||||
SCD30 CO2 Sensor configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
@property
|
|
||||||
def shtxx_config(self) -> global___SHTXX_config:
|
|
||||||
"""
|
|
||||||
SHTXX temperature and relative humidity sensor configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
*,
|
|
||||||
scd4x_config: global___SCD4X_config | None = ...,
|
|
||||||
sen5x_config: global___SEN5X_config | None = ...,
|
|
||||||
scd30_config: global___SCD30_config | None = ...,
|
|
||||||
shtxx_config: global___SHTXX_config | None = ...,
|
|
||||||
) -> None: ...
|
|
||||||
def HasField(self, field_name: typing.Literal["scd30_config", b"scd30_config", "scd4x_config", b"scd4x_config", "sen5x_config", b"sen5x_config", "shtxx_config", b"shtxx_config"]) -> builtins.bool: ...
|
|
||||||
def ClearField(self, field_name: typing.Literal["scd30_config", b"scd30_config", "scd4x_config", b"scd4x_config", "sen5x_config", b"sen5x_config", "shtxx_config", b"shtxx_config"]) -> None: ...
|
|
||||||
|
|
||||||
global___SensorConfig = SensorConfig
|
|
||||||
|
|
||||||
@typing.final
|
|
||||||
class SCD4X_config(google.protobuf.message.Message):
|
|
||||||
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
|
||||||
|
|
||||||
SET_ASC_FIELD_NUMBER: builtins.int
|
|
||||||
SET_TARGET_CO2_CONC_FIELD_NUMBER: builtins.int
|
|
||||||
SET_TEMPERATURE_FIELD_NUMBER: builtins.int
|
|
||||||
SET_ALTITUDE_FIELD_NUMBER: builtins.int
|
|
||||||
SET_AMBIENT_PRESSURE_FIELD_NUMBER: builtins.int
|
|
||||||
FACTORY_RESET_FIELD_NUMBER: builtins.int
|
|
||||||
SET_POWER_MODE_FIELD_NUMBER: builtins.int
|
|
||||||
set_asc: builtins.bool
|
|
||||||
"""
|
|
||||||
Set Automatic self-calibration enabled
|
|
||||||
"""
|
|
||||||
set_target_co2_conc: builtins.int
|
|
||||||
"""
|
|
||||||
Recalibration target CO2 concentration in ppm (FRC or ASC)
|
|
||||||
"""
|
|
||||||
set_temperature: builtins.float
|
|
||||||
"""
|
|
||||||
Reference temperature in degC
|
|
||||||
"""
|
|
||||||
set_altitude: builtins.int
|
|
||||||
"""
|
|
||||||
Altitude of sensor in meters above sea level. 0 - 3000m (overrides ambient pressure)
|
|
||||||
"""
|
|
||||||
set_ambient_pressure: builtins.int
|
|
||||||
"""
|
|
||||||
Sensor ambient pressure in Pa. 70000 - 120000 Pa (overrides altitude)
|
|
||||||
"""
|
|
||||||
factory_reset: builtins.bool
|
|
||||||
"""
|
|
||||||
Perform a factory reset of the sensor
|
|
||||||
"""
|
|
||||||
set_power_mode: builtins.bool
|
|
||||||
"""
|
|
||||||
Power mode for sensor (true for low power, false for normal)
|
|
||||||
"""
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
*,
|
|
||||||
set_asc: builtins.bool | None = ...,
|
|
||||||
set_target_co2_conc: builtins.int | None = ...,
|
|
||||||
set_temperature: builtins.float | None = ...,
|
|
||||||
set_altitude: builtins.int | None = ...,
|
|
||||||
set_ambient_pressure: builtins.int | None = ...,
|
|
||||||
factory_reset: builtins.bool | None = ...,
|
|
||||||
set_power_mode: builtins.bool | None = ...,
|
|
||||||
) -> None: ...
|
|
||||||
def HasField(self, field_name: typing.Literal["_factory_reset", b"_factory_reset", "_set_altitude", b"_set_altitude", "_set_ambient_pressure", b"_set_ambient_pressure", "_set_asc", b"_set_asc", "_set_power_mode", b"_set_power_mode", "_set_target_co2_conc", b"_set_target_co2_conc", "_set_temperature", b"_set_temperature", "factory_reset", b"factory_reset", "set_altitude", b"set_altitude", "set_ambient_pressure", b"set_ambient_pressure", "set_asc", b"set_asc", "set_power_mode", b"set_power_mode", "set_target_co2_conc", b"set_target_co2_conc", "set_temperature", b"set_temperature"]) -> builtins.bool: ...
|
|
||||||
def ClearField(self, field_name: typing.Literal["_factory_reset", b"_factory_reset", "_set_altitude", b"_set_altitude", "_set_ambient_pressure", b"_set_ambient_pressure", "_set_asc", b"_set_asc", "_set_power_mode", b"_set_power_mode", "_set_target_co2_conc", b"_set_target_co2_conc", "_set_temperature", b"_set_temperature", "factory_reset", b"factory_reset", "set_altitude", b"set_altitude", "set_ambient_pressure", b"set_ambient_pressure", "set_asc", b"set_asc", "set_power_mode", b"set_power_mode", "set_target_co2_conc", b"set_target_co2_conc", "set_temperature", b"set_temperature"]) -> None: ...
|
|
||||||
@typing.overload
|
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_factory_reset", b"_factory_reset"]) -> typing.Literal["factory_reset"] | None: ...
|
|
||||||
@typing.overload
|
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_set_altitude", b"_set_altitude"]) -> typing.Literal["set_altitude"] | None: ...
|
|
||||||
@typing.overload
|
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_set_ambient_pressure", b"_set_ambient_pressure"]) -> typing.Literal["set_ambient_pressure"] | None: ...
|
|
||||||
@typing.overload
|
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_set_asc", b"_set_asc"]) -> typing.Literal["set_asc"] | None: ...
|
|
||||||
@typing.overload
|
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_set_power_mode", b"_set_power_mode"]) -> typing.Literal["set_power_mode"] | None: ...
|
|
||||||
@typing.overload
|
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_set_target_co2_conc", b"_set_target_co2_conc"]) -> typing.Literal["set_target_co2_conc"] | None: ...
|
|
||||||
@typing.overload
|
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_set_temperature", b"_set_temperature"]) -> typing.Literal["set_temperature"] | None: ...
|
|
||||||
|
|
||||||
global___SCD4X_config = SCD4X_config
|
|
||||||
|
|
||||||
@typing.final
|
|
||||||
class SEN5X_config(google.protobuf.message.Message):
|
|
||||||
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
|
||||||
|
|
||||||
SET_TEMPERATURE_FIELD_NUMBER: builtins.int
|
|
||||||
SET_ONE_SHOT_MODE_FIELD_NUMBER: builtins.int
|
|
||||||
set_temperature: builtins.float
|
|
||||||
"""
|
|
||||||
Reference temperature in degC
|
|
||||||
"""
|
|
||||||
set_one_shot_mode: builtins.bool
|
|
||||||
"""
|
|
||||||
One-shot mode (true for low power - one-shot mode, false for normal - continuous mode)
|
|
||||||
"""
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
*,
|
|
||||||
set_temperature: builtins.float | None = ...,
|
|
||||||
set_one_shot_mode: builtins.bool | None = ...,
|
|
||||||
) -> None: ...
|
|
||||||
def HasField(self, field_name: typing.Literal["_set_one_shot_mode", b"_set_one_shot_mode", "_set_temperature", b"_set_temperature", "set_one_shot_mode", b"set_one_shot_mode", "set_temperature", b"set_temperature"]) -> builtins.bool: ...
|
|
||||||
def ClearField(self, field_name: typing.Literal["_set_one_shot_mode", b"_set_one_shot_mode", "_set_temperature", b"_set_temperature", "set_one_shot_mode", b"set_one_shot_mode", "set_temperature", b"set_temperature"]) -> None: ...
|
|
||||||
@typing.overload
|
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_set_one_shot_mode", b"_set_one_shot_mode"]) -> typing.Literal["set_one_shot_mode"] | None: ...
|
|
||||||
@typing.overload
|
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_set_temperature", b"_set_temperature"]) -> typing.Literal["set_temperature"] | None: ...
|
|
||||||
|
|
||||||
global___SEN5X_config = SEN5X_config
|
|
||||||
|
|
||||||
@typing.final
|
|
||||||
class SCD30_config(google.protobuf.message.Message):
|
|
||||||
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
|
||||||
|
|
||||||
SET_ASC_FIELD_NUMBER: builtins.int
|
|
||||||
SET_TARGET_CO2_CONC_FIELD_NUMBER: builtins.int
|
|
||||||
SET_TEMPERATURE_FIELD_NUMBER: builtins.int
|
|
||||||
SET_ALTITUDE_FIELD_NUMBER: builtins.int
|
|
||||||
SET_MEASUREMENT_INTERVAL_FIELD_NUMBER: builtins.int
|
|
||||||
SOFT_RESET_FIELD_NUMBER: builtins.int
|
|
||||||
set_asc: builtins.bool
|
|
||||||
"""
|
|
||||||
Set Automatic self-calibration enabled
|
|
||||||
"""
|
|
||||||
set_target_co2_conc: builtins.int
|
|
||||||
"""
|
|
||||||
Recalibration target CO2 concentration in ppm (FRC or ASC)
|
|
||||||
"""
|
|
||||||
set_temperature: builtins.float
|
|
||||||
"""
|
|
||||||
Reference temperature in degC
|
|
||||||
"""
|
|
||||||
set_altitude: builtins.int
|
|
||||||
"""
|
|
||||||
Altitude of sensor in meters above sea level. 0 - 3000m (overrides ambient pressure)
|
|
||||||
"""
|
|
||||||
set_measurement_interval: builtins.int
|
|
||||||
"""
|
|
||||||
Power mode for sensor (true for low power, false for normal)
|
|
||||||
"""
|
|
||||||
soft_reset: builtins.bool
|
|
||||||
"""
|
|
||||||
Perform a factory reset of the sensor
|
|
||||||
"""
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
*,
|
|
||||||
set_asc: builtins.bool | None = ...,
|
|
||||||
set_target_co2_conc: builtins.int | None = ...,
|
|
||||||
set_temperature: builtins.float | None = ...,
|
|
||||||
set_altitude: builtins.int | None = ...,
|
|
||||||
set_measurement_interval: builtins.int | None = ...,
|
|
||||||
soft_reset: builtins.bool | None = ...,
|
|
||||||
) -> None: ...
|
|
||||||
def HasField(self, field_name: typing.Literal["_set_altitude", b"_set_altitude", "_set_asc", b"_set_asc", "_set_measurement_interval", b"_set_measurement_interval", "_set_target_co2_conc", b"_set_target_co2_conc", "_set_temperature", b"_set_temperature", "_soft_reset", b"_soft_reset", "set_altitude", b"set_altitude", "set_asc", b"set_asc", "set_measurement_interval", b"set_measurement_interval", "set_target_co2_conc", b"set_target_co2_conc", "set_temperature", b"set_temperature", "soft_reset", b"soft_reset"]) -> builtins.bool: ...
|
|
||||||
def ClearField(self, field_name: typing.Literal["_set_altitude", b"_set_altitude", "_set_asc", b"_set_asc", "_set_measurement_interval", b"_set_measurement_interval", "_set_target_co2_conc", b"_set_target_co2_conc", "_set_temperature", b"_set_temperature", "_soft_reset", b"_soft_reset", "set_altitude", b"set_altitude", "set_asc", b"set_asc", "set_measurement_interval", b"set_measurement_interval", "set_target_co2_conc", b"set_target_co2_conc", "set_temperature", b"set_temperature", "soft_reset", b"soft_reset"]) -> None: ...
|
|
||||||
@typing.overload
|
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_set_altitude", b"_set_altitude"]) -> typing.Literal["set_altitude"] | None: ...
|
|
||||||
@typing.overload
|
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_set_asc", b"_set_asc"]) -> typing.Literal["set_asc"] | None: ...
|
|
||||||
@typing.overload
|
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_set_measurement_interval", b"_set_measurement_interval"]) -> typing.Literal["set_measurement_interval"] | None: ...
|
|
||||||
@typing.overload
|
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_set_target_co2_conc", b"_set_target_co2_conc"]) -> typing.Literal["set_target_co2_conc"] | None: ...
|
|
||||||
@typing.overload
|
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_set_temperature", b"_set_temperature"]) -> typing.Literal["set_temperature"] | None: ...
|
|
||||||
@typing.overload
|
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_soft_reset", b"_soft_reset"]) -> typing.Literal["soft_reset"] | None: ...
|
|
||||||
|
|
||||||
global___SCD30_config = SCD30_config
|
|
||||||
|
|
||||||
@typing.final
|
|
||||||
class SHTXX_config(google.protobuf.message.Message):
|
|
||||||
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
|
||||||
|
|
||||||
SET_ACCURACY_FIELD_NUMBER: builtins.int
|
|
||||||
set_accuracy: builtins.int
|
|
||||||
"""
|
|
||||||
Accuracy mode (0 = low, 1 = medium, 2 = high)
|
|
||||||
"""
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
*,
|
|
||||||
set_accuracy: builtins.int | None = ...,
|
|
||||||
) -> None: ...
|
|
||||||
def HasField(self, field_name: typing.Literal["_set_accuracy", b"_set_accuracy", "set_accuracy", b"set_accuracy"]) -> builtins.bool: ...
|
|
||||||
def ClearField(self, field_name: typing.Literal["_set_accuracy", b"_set_accuracy", "set_accuracy", b"set_accuracy"]) -> None: ...
|
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_set_accuracy", b"_set_accuracy"]) -> typing.Literal["set_accuracy"] | None: ...
|
|
||||||
|
|
||||||
global___SHTXX_config = SHTXX_config
|
|
||||||
|
|||||||
11
meshtastic/protobuf/apponly_pb2.py
generated
11
meshtastic/protobuf/apponly_pb2.py
generated
@@ -13,19 +13,16 @@ _sym_db = _symbol_database.Default()
|
|||||||
|
|
||||||
from meshtastic.protobuf import channel_pb2 as meshtastic_dot_protobuf_dot_channel__pb2
|
from meshtastic.protobuf import channel_pb2 as meshtastic_dot_protobuf_dot_channel__pb2
|
||||||
from meshtastic.protobuf import config_pb2 as meshtastic_dot_protobuf_dot_config__pb2
|
from meshtastic.protobuf import config_pb2 as meshtastic_dot_protobuf_dot_config__pb2
|
||||||
from meshtastic.protobuf import nanopb_pb2 as meshtastic_dot_protobuf_dot_nanopb__pb2
|
|
||||||
|
|
||||||
|
|
||||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n!meshtastic/protobuf/apponly.proto\x12\x13meshtastic.protobuf\x1a!meshtastic/protobuf/channel.proto\x1a meshtastic/protobuf/config.proto\x1a meshtastic/protobuf/nanopb.proto\"\x88\x01\n\nChannelSet\x12=\n\x08settings\x18\x01 \x03(\x0b\x32$.meshtastic.protobuf.ChannelSettingsB\x05\x92?\x02\x10\x08\x12;\n\x0blora_config\x18\x02 \x01(\x0b\x32&.meshtastic.protobuf.Config.LoRaConfigBc\n\x14org.meshtastic.protoB\rAppOnlyProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
|
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n!meshtastic/protobuf/apponly.proto\x12\x13meshtastic.protobuf\x1a!meshtastic/protobuf/channel.proto\x1a meshtastic/protobuf/config.proto\"\x81\x01\n\nChannelSet\x12\x36\n\x08settings\x18\x01 \x03(\x0b\x32$.meshtastic.protobuf.ChannelSettings\x12;\n\x0blora_config\x18\x02 \x01(\x0b\x32&.meshtastic.protobuf.Config.LoRaConfigBb\n\x13\x63om.geeksville.meshB\rAppOnlyProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
|
||||||
|
|
||||||
_globals = globals()
|
_globals = globals()
|
||||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.apponly_pb2', _globals)
|
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.apponly_pb2', _globals)
|
||||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||||
DESCRIPTOR._options = None
|
DESCRIPTOR._options = None
|
||||||
DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\rAppOnlyProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\rAppOnlyProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||||
_CHANNELSET.fields_by_name['settings']._options = None
|
_globals['_CHANNELSET']._serialized_start=128
|
||||||
_CHANNELSET.fields_by_name['settings']._serialized_options = b'\222?\002\020\010'
|
_globals['_CHANNELSET']._serialized_end=257
|
||||||
_globals['_CHANNELSET']._serialized_start=162
|
|
||||||
_globals['_CHANNELSET']._serialized_end=298
|
|
||||||
# @@protoc_insertion_point(module_scope)
|
# @@protoc_insertion_point(module_scope)
|
||||||
|
|||||||
249
meshtastic/protobuf/atak_pb2.py
generated
249
meshtastic/protobuf/atak_pb2.py
generated
File diff suppressed because one or more lines are too long
3382
meshtastic/protobuf/atak_pb2.pyi
generated
3382
meshtastic/protobuf/atak_pb2.pyi
generated
File diff suppressed because it is too large
Load Diff
11
meshtastic/protobuf/cannedmessages_pb2.py
generated
11
meshtastic/protobuf/cannedmessages_pb2.py
generated
@@ -11,19 +11,16 @@ from google.protobuf.internal import builder as _builder
|
|||||||
_sym_db = _symbol_database.Default()
|
_sym_db = _symbol_database.Default()
|
||||||
|
|
||||||
|
|
||||||
from meshtastic.protobuf import nanopb_pb2 as meshtastic_dot_protobuf_dot_nanopb__pb2
|
|
||||||
|
|
||||||
|
|
||||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n(meshtastic/protobuf/cannedmessages.proto\x12\x13meshtastic.protobuf\x1a meshtastic/protobuf/nanopb.proto\"5\n\x19\x43\x61nnedMessageModuleConfig\x12\x18\n\x08messages\x18\x01 \x01(\tB\x06\x92?\x03\x08\xc9\x01\x42o\n\x14org.meshtastic.protoB\x19\x43\x61nnedMessageConfigProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
|
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n(meshtastic/protobuf/cannedmessages.proto\x12\x13meshtastic.protobuf\"-\n\x19\x43\x61nnedMessageModuleConfig\x12\x10\n\x08messages\x18\x01 \x01(\tBn\n\x13\x63om.geeksville.meshB\x19\x43\x61nnedMessageConfigProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
|
||||||
|
|
||||||
_globals = globals()
|
_globals = globals()
|
||||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.cannedmessages_pb2', _globals)
|
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.cannedmessages_pb2', _globals)
|
||||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||||
DESCRIPTOR._options = None
|
DESCRIPTOR._options = None
|
||||||
DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\031CannedMessageConfigProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\031CannedMessageConfigProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||||
_CANNEDMESSAGEMODULECONFIG.fields_by_name['messages']._options = None
|
_globals['_CANNEDMESSAGEMODULECONFIG']._serialized_start=65
|
||||||
_CANNEDMESSAGEMODULECONFIG.fields_by_name['messages']._serialized_options = b'\222?\003\010\311\001'
|
_globals['_CANNEDMESSAGEMODULECONFIG']._serialized_end=110
|
||||||
_globals['_CANNEDMESSAGEMODULECONFIG']._serialized_start=99
|
|
||||||
_globals['_CANNEDMESSAGEMODULECONFIG']._serialized_end=152
|
|
||||||
# @@protoc_insertion_point(module_scope)
|
# @@protoc_insertion_point(module_scope)
|
||||||
|
|||||||
27
meshtastic/protobuf/channel_pb2.py
generated
27
meshtastic/protobuf/channel_pb2.py
generated
@@ -11,31 +11,24 @@ from google.protobuf.internal import builder as _builder
|
|||||||
_sym_db = _symbol_database.Default()
|
_sym_db = _symbol_database.Default()
|
||||||
|
|
||||||
|
|
||||||
from meshtastic.protobuf import nanopb_pb2 as meshtastic_dot_protobuf_dot_nanopb__pb2
|
|
||||||
|
|
||||||
|
|
||||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n!meshtastic/protobuf/channel.proto\x12\x13meshtastic.protobuf\x1a meshtastic/protobuf/nanopb.proto\"\xcf\x01\n\x0f\x43hannelSettings\x12\x17\n\x0b\x63hannel_num\x18\x01 \x01(\rB\x02\x18\x01\x12\x12\n\x03psk\x18\x02 \x01(\x0c\x42\x05\x92?\x02\x08 \x12\x13\n\x04name\x18\x03 \x01(\tB\x05\x92?\x02\x08\x0c\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\">\n\x0eModuleSettings\x12\x1a\n\x12position_precision\x18\x01 \x01(\r\x12\x10\n\x08is_muted\x18\x02 \x01(\x08\"\xba\x01\n\x07\x43hannel\x12\x14\n\x05index\x18\x01 \x01(\x05\x42\x05\x92?\x02\x38\x08\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\x63\n\x14org.meshtastic.protoB\rChannelProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
|
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n!meshtastic/protobuf/channel.proto\x12\x13meshtastic.protobuf\"\xc1\x01\n\x0f\x43hannelSettings\x12\x17\n\x0b\x63hannel_num\x18\x01 \x01(\rB\x02\x18\x01\x12\x0b\n\x03psk\x18\x02 \x01(\x0c\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\n\n\x02id\x18\x04 \x01(\x07\x12\x16\n\x0euplink_enabled\x18\x05 \x01(\x08\x12\x18\n\x10\x64ownlink_enabled\x18\x06 \x01(\x08\x12<\n\x0fmodule_settings\x18\x07 \x01(\x0b\x32#.meshtastic.protobuf.ModuleSettings\"E\n\x0eModuleSettings\x12\x1a\n\x12position_precision\x18\x01 \x01(\r\x12\x17\n\x0fis_client_muted\x18\x02 \x01(\x08\"\xb3\x01\n\x07\x43hannel\x12\r\n\x05index\x18\x01 \x01(\x05\x12\x36\n\x08settings\x18\x02 \x01(\x0b\x32$.meshtastic.protobuf.ChannelSettings\x12/\n\x04role\x18\x03 \x01(\x0e\x32!.meshtastic.protobuf.Channel.Role\"0\n\x04Role\x12\x0c\n\x08\x44ISABLED\x10\x00\x12\x0b\n\x07PRIMARY\x10\x01\x12\r\n\tSECONDARY\x10\x02\x42\x62\n\x13\x63om.geeksville.meshB\rChannelProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
|
||||||
|
|
||||||
_globals = globals()
|
_globals = globals()
|
||||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.channel_pb2', _globals)
|
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.channel_pb2', _globals)
|
||||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||||
DESCRIPTOR._options = None
|
DESCRIPTOR._options = None
|
||||||
DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\rChannelProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\rChannelProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||||
_CHANNELSETTINGS.fields_by_name['channel_num']._options = None
|
_CHANNELSETTINGS.fields_by_name['channel_num']._options = None
|
||||||
_CHANNELSETTINGS.fields_by_name['channel_num']._serialized_options = b'\030\001'
|
_CHANNELSETTINGS.fields_by_name['channel_num']._serialized_options = b'\030\001'
|
||||||
_CHANNELSETTINGS.fields_by_name['psk']._options = None
|
_globals['_CHANNELSETTINGS']._serialized_start=59
|
||||||
_CHANNELSETTINGS.fields_by_name['psk']._serialized_options = b'\222?\002\010 '
|
_globals['_CHANNELSETTINGS']._serialized_end=252
|
||||||
_CHANNELSETTINGS.fields_by_name['name']._options = None
|
_globals['_MODULESETTINGS']._serialized_start=254
|
||||||
_CHANNELSETTINGS.fields_by_name['name']._serialized_options = b'\222?\002\010\014'
|
_globals['_MODULESETTINGS']._serialized_end=323
|
||||||
_CHANNEL.fields_by_name['index']._options = None
|
_globals['_CHANNEL']._serialized_start=326
|
||||||
_CHANNEL.fields_by_name['index']._serialized_options = b'\222?\0028\010'
|
_globals['_CHANNEL']._serialized_end=505
|
||||||
_globals['_CHANNELSETTINGS']._serialized_start=93
|
_globals['_CHANNEL_ROLE']._serialized_start=457
|
||||||
_globals['_CHANNELSETTINGS']._serialized_end=300
|
_globals['_CHANNEL_ROLE']._serialized_end=505
|
||||||
_globals['_MODULESETTINGS']._serialized_start=302
|
|
||||||
_globals['_MODULESETTINGS']._serialized_end=364
|
|
||||||
_globals['_CHANNEL']._serialized_start=367
|
|
||||||
_globals['_CHANNEL']._serialized_end=553
|
|
||||||
_globals['_CHANNEL_ROLE']._serialized_start=505
|
|
||||||
_globals['_CHANNEL_ROLE']._serialized_end=553
|
|
||||||
# @@protoc_insertion_point(module_scope)
|
# @@protoc_insertion_point(module_scope)
|
||||||
|
|||||||
12
meshtastic/protobuf/channel_pb2.pyi
generated
12
meshtastic/protobuf/channel_pb2.pyi
generated
@@ -1,7 +1,7 @@
|
|||||||
"""
|
"""
|
||||||
@generated by mypy-protobuf. Do not edit manually!
|
@generated by mypy-protobuf. Do not edit manually!
|
||||||
isort:skip_file
|
isort:skip_file
|
||||||
trunk-ignore(buf-lint/PACKAGE_DIRECTORY_MATCH)"""
|
"""
|
||||||
|
|
||||||
import builtins
|
import builtins
|
||||||
import google.protobuf.descriptor
|
import google.protobuf.descriptor
|
||||||
@@ -127,23 +127,23 @@ class ModuleSettings(google.protobuf.message.Message):
|
|||||||
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
||||||
|
|
||||||
POSITION_PRECISION_FIELD_NUMBER: builtins.int
|
POSITION_PRECISION_FIELD_NUMBER: builtins.int
|
||||||
IS_MUTED_FIELD_NUMBER: builtins.int
|
IS_CLIENT_MUTED_FIELD_NUMBER: builtins.int
|
||||||
position_precision: builtins.int
|
position_precision: builtins.int
|
||||||
"""
|
"""
|
||||||
Bits of precision for the location sent in position packets.
|
Bits of precision for the location sent in position packets.
|
||||||
"""
|
"""
|
||||||
is_muted: builtins.bool
|
is_client_muted: builtins.bool
|
||||||
"""
|
"""
|
||||||
Controls whether or not the client / device should mute the current channel
|
Controls whether or not the phone / clients should mute the current channel
|
||||||
Useful for noisy public channels you don't necessarily want to disable
|
Useful for noisy public channels you don't necessarily want to disable
|
||||||
"""
|
"""
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
position_precision: builtins.int = ...,
|
position_precision: builtins.int = ...,
|
||||||
is_muted: builtins.bool = ...,
|
is_client_muted: builtins.bool = ...,
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
def ClearField(self, field_name: typing.Literal["is_muted", b"is_muted", "position_precision", b"position_precision"]) -> None: ...
|
def ClearField(self, field_name: typing.Literal["is_client_muted", b"is_client_muted", "position_precision", b"position_precision"]) -> None: ...
|
||||||
|
|
||||||
global___ModuleSettings = ModuleSettings
|
global___ModuleSettings = ModuleSettings
|
||||||
|
|
||||||
|
|||||||
17
meshtastic/protobuf/clientonly_pb2.py
generated
17
meshtastic/protobuf/clientonly_pb2.py
generated
@@ -13,25 +13,16 @@ _sym_db = _symbol_database.Default()
|
|||||||
|
|
||||||
from meshtastic.protobuf import localonly_pb2 as meshtastic_dot_protobuf_dot_localonly__pb2
|
from meshtastic.protobuf import localonly_pb2 as meshtastic_dot_protobuf_dot_localonly__pb2
|
||||||
from meshtastic.protobuf import mesh_pb2 as meshtastic_dot_protobuf_dot_mesh__pb2
|
from meshtastic.protobuf import mesh_pb2 as meshtastic_dot_protobuf_dot_mesh__pb2
|
||||||
from meshtastic.protobuf import nanopb_pb2 as meshtastic_dot_protobuf_dot_nanopb__pb2
|
|
||||||
|
|
||||||
|
|
||||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n$meshtastic/protobuf/clientonly.proto\x12\x13meshtastic.protobuf\x1a#meshtastic/protobuf/localonly.proto\x1a\x1emeshtastic/protobuf/mesh.proto\x1a meshtastic/protobuf/nanopb.proto\"\xe2\x03\n\rDeviceProfile\x12\x1d\n\tlong_name\x18\x01 \x01(\tB\x05\x92?\x02\x08(H\x00\x88\x01\x01\x12\x1e\n\nshort_name\x18\x02 \x01(\tB\x05\x92?\x02\x08\x05H\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\x1d\n\x08ringtone\x18\x07 \x01(\tB\x06\x92?\x03\x08\xe7\x01H\x06\x88\x01\x01\x12$\n\x0f\x63\x61nned_messages\x18\x08 \x01(\tB\x06\x92?\x03\x08\xc9\x01H\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_messagesBf\n\x14org.meshtastic.protoB\x10\x43lientOnlyProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
|
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')
|
||||||
|
|
||||||
_globals = globals()
|
_globals = globals()
|
||||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.clientonly_pb2', _globals)
|
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.clientonly_pb2', _globals)
|
||||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||||
DESCRIPTOR._options = None
|
DESCRIPTOR._options = None
|
||||||
DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\020ClientOnlyProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\020ClientOnlyProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||||
_DEVICEPROFILE.fields_by_name['long_name']._options = None
|
_globals['_DEVICEPROFILE']._serialized_start=131
|
||||||
_DEVICEPROFILE.fields_by_name['long_name']._serialized_options = b'\222?\002\010('
|
_globals['_DEVICEPROFILE']._serialized_end=583
|
||||||
_DEVICEPROFILE.fields_by_name['short_name']._options = None
|
|
||||||
_DEVICEPROFILE.fields_by_name['short_name']._serialized_options = b'\222?\002\010\005'
|
|
||||||
_DEVICEPROFILE.fields_by_name['ringtone']._options = None
|
|
||||||
_DEVICEPROFILE.fields_by_name['ringtone']._serialized_options = b'\222?\003\010\347\001'
|
|
||||||
_DEVICEPROFILE.fields_by_name['canned_messages']._options = None
|
|
||||||
_DEVICEPROFILE.fields_by_name['canned_messages']._serialized_options = b'\222?\003\010\311\001'
|
|
||||||
_globals['_DEVICEPROFILE']._serialized_start=165
|
|
||||||
_globals['_DEVICEPROFILE']._serialized_end=647
|
|
||||||
# @@protoc_insertion_point(module_scope)
|
# @@protoc_insertion_point(module_scope)
|
||||||
|
|||||||
147
meshtastic/protobuf/config_pb2.py
generated
147
meshtastic/protobuf/config_pb2.py
generated
File diff suppressed because one or more lines are too long
423
meshtastic/protobuf/config_pb2.pyi
generated
423
meshtastic/protobuf/config_pb2.pyi
generated
@@ -64,7 +64,6 @@ class Config(google.protobuf.message.Message):
|
|||||||
Description: Infrastructure node for extending network coverage by relaying messages with minimal overhead. Not visible in Nodes list.
|
Description: Infrastructure node for extending network coverage by relaying messages with minimal overhead. Not visible in Nodes list.
|
||||||
Technical Details: Mesh packets will simply be rebroadcasted over this node. Nodes configured with this role will not originate NodeInfo, Position, Telemetry
|
Technical Details: Mesh packets will simply be rebroadcasted over this node. Nodes configured with this role will not originate NodeInfo, Position, Telemetry
|
||||||
or any other packet type. They will simply rebroadcast any mesh packets on the same frequency, channel num, spread factor, and coding rate.
|
or any other packet type. They will simply rebroadcast any mesh packets on the same frequency, channel num, spread factor, and coding rate.
|
||||||
Deprecated in v2.7.11 because it creates "holes" in the mesh rebroadcast chain.
|
|
||||||
"""
|
"""
|
||||||
TRACKER: Config.DeviceConfig._Role.ValueType # 5
|
TRACKER: Config.DeviceConfig._Role.ValueType # 5
|
||||||
"""
|
"""
|
||||||
@@ -117,13 +116,6 @@ class Config(google.protobuf.message.Message):
|
|||||||
but should not be given priority over other routers in order to avoid unnecessaraily
|
but should not be given priority over other routers in order to avoid unnecessaraily
|
||||||
consuming hops.
|
consuming hops.
|
||||||
"""
|
"""
|
||||||
CLIENT_BASE: Config.DeviceConfig._Role.ValueType # 12
|
|
||||||
"""
|
|
||||||
Description: Treats packets from or to favorited nodes as ROUTER_LATE, and all other packets as CLIENT.
|
|
||||||
Technical Details: Used for stronger attic/roof nodes to distribute messages more widely
|
|
||||||
from weaker, indoor, or less-well-positioned nodes. Recommended for users with multiple nodes
|
|
||||||
where one CLIENT_BASE acts as a more powerful base station, such as an attic/roof node.
|
|
||||||
"""
|
|
||||||
|
|
||||||
class Role(_Role, metaclass=_RoleEnumTypeWrapper):
|
class Role(_Role, metaclass=_RoleEnumTypeWrapper):
|
||||||
"""
|
"""
|
||||||
@@ -156,7 +148,6 @@ class Config(google.protobuf.message.Message):
|
|||||||
Description: Infrastructure node for extending network coverage by relaying messages with minimal overhead. Not visible in Nodes list.
|
Description: Infrastructure node for extending network coverage by relaying messages with minimal overhead. Not visible in Nodes list.
|
||||||
Technical Details: Mesh packets will simply be rebroadcasted over this node. Nodes configured with this role will not originate NodeInfo, Position, Telemetry
|
Technical Details: Mesh packets will simply be rebroadcasted over this node. Nodes configured with this role will not originate NodeInfo, Position, Telemetry
|
||||||
or any other packet type. They will simply rebroadcast any mesh packets on the same frequency, channel num, spread factor, and coding rate.
|
or any other packet type. They will simply rebroadcast any mesh packets on the same frequency, channel num, spread factor, and coding rate.
|
||||||
Deprecated in v2.7.11 because it creates "holes" in the mesh rebroadcast chain.
|
|
||||||
"""
|
"""
|
||||||
TRACKER: Config.DeviceConfig.Role.ValueType # 5
|
TRACKER: Config.DeviceConfig.Role.ValueType # 5
|
||||||
"""
|
"""
|
||||||
@@ -209,13 +200,6 @@ class Config(google.protobuf.message.Message):
|
|||||||
but should not be given priority over other routers in order to avoid unnecessaraily
|
but should not be given priority over other routers in order to avoid unnecessaraily
|
||||||
consuming hops.
|
consuming hops.
|
||||||
"""
|
"""
|
||||||
CLIENT_BASE: Config.DeviceConfig.Role.ValueType # 12
|
|
||||||
"""
|
|
||||||
Description: Treats packets from or to favorited nodes as ROUTER_LATE, and all other packets as CLIENT.
|
|
||||||
Technical Details: Used for stronger attic/roof nodes to distribute messages more widely
|
|
||||||
from weaker, indoor, or less-well-positioned nodes. Recommended for users with multiple nodes
|
|
||||||
where one CLIENT_BASE acts as a more powerful base station, such as an attic/roof node.
|
|
||||||
"""
|
|
||||||
|
|
||||||
class _RebroadcastMode:
|
class _RebroadcastMode:
|
||||||
ValueType = typing.NewType("ValueType", builtins.int)
|
ValueType = typing.NewType("ValueType", builtins.int)
|
||||||
@@ -288,73 +272,6 @@ class Config(google.protobuf.message.Message):
|
|||||||
Only rebroadcasts packets with standard portnums: NodeInfo, Text, Position, Telemetry, and Routing.
|
Only rebroadcasts packets with standard portnums: NodeInfo, Text, Position, Telemetry, and Routing.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class _BuzzerMode:
|
|
||||||
ValueType = typing.NewType("ValueType", builtins.int)
|
|
||||||
V: typing_extensions.TypeAlias = ValueType
|
|
||||||
|
|
||||||
class _BuzzerModeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[Config.DeviceConfig._BuzzerMode.ValueType], builtins.type):
|
|
||||||
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
|
|
||||||
ALL_ENABLED: Config.DeviceConfig._BuzzerMode.ValueType # 0
|
|
||||||
"""
|
|
||||||
Default behavior.
|
|
||||||
Buzzer is enabled for all audio feedback including button presses and alerts.
|
|
||||||
"""
|
|
||||||
DISABLED: Config.DeviceConfig._BuzzerMode.ValueType # 1
|
|
||||||
"""
|
|
||||||
Disabled.
|
|
||||||
All buzzer audio feedback is disabled.
|
|
||||||
"""
|
|
||||||
NOTIFICATIONS_ONLY: Config.DeviceConfig._BuzzerMode.ValueType # 2
|
|
||||||
"""
|
|
||||||
Notifications Only.
|
|
||||||
Buzzer is enabled only for notifications and alerts, but not for button presses.
|
|
||||||
External notification config determines the specifics of the notification behavior.
|
|
||||||
"""
|
|
||||||
SYSTEM_ONLY: Config.DeviceConfig._BuzzerMode.ValueType # 3
|
|
||||||
"""
|
|
||||||
Non-notification system buzzer tones only.
|
|
||||||
Buzzer is enabled only for non-notification tones such as button presses, startup, shutdown, but not for alerts.
|
|
||||||
"""
|
|
||||||
DIRECT_MSG_ONLY: Config.DeviceConfig._BuzzerMode.ValueType # 4
|
|
||||||
"""
|
|
||||||
Direct Message notifications only.
|
|
||||||
Buzzer is enabled only for direct messages and alerts, but not for button presses.
|
|
||||||
External notification config determines the specifics of the notification behavior.
|
|
||||||
"""
|
|
||||||
|
|
||||||
class BuzzerMode(_BuzzerMode, metaclass=_BuzzerModeEnumTypeWrapper):
|
|
||||||
"""
|
|
||||||
Defines buzzer behavior for audio feedback
|
|
||||||
"""
|
|
||||||
|
|
||||||
ALL_ENABLED: Config.DeviceConfig.BuzzerMode.ValueType # 0
|
|
||||||
"""
|
|
||||||
Default behavior.
|
|
||||||
Buzzer is enabled for all audio feedback including button presses and alerts.
|
|
||||||
"""
|
|
||||||
DISABLED: Config.DeviceConfig.BuzzerMode.ValueType # 1
|
|
||||||
"""
|
|
||||||
Disabled.
|
|
||||||
All buzzer audio feedback is disabled.
|
|
||||||
"""
|
|
||||||
NOTIFICATIONS_ONLY: Config.DeviceConfig.BuzzerMode.ValueType # 2
|
|
||||||
"""
|
|
||||||
Notifications Only.
|
|
||||||
Buzzer is enabled only for notifications and alerts, but not for button presses.
|
|
||||||
External notification config determines the specifics of the notification behavior.
|
|
||||||
"""
|
|
||||||
SYSTEM_ONLY: Config.DeviceConfig.BuzzerMode.ValueType # 3
|
|
||||||
"""
|
|
||||||
Non-notification system buzzer tones only.
|
|
||||||
Buzzer is enabled only for non-notification tones such as button presses, startup, shutdown, but not for alerts.
|
|
||||||
"""
|
|
||||||
DIRECT_MSG_ONLY: Config.DeviceConfig.BuzzerMode.ValueType # 4
|
|
||||||
"""
|
|
||||||
Direct Message notifications only.
|
|
||||||
Buzzer is enabled only for direct messages and alerts, but not for button presses.
|
|
||||||
External notification config determines the specifics of the notification behavior.
|
|
||||||
"""
|
|
||||||
|
|
||||||
ROLE_FIELD_NUMBER: builtins.int
|
ROLE_FIELD_NUMBER: builtins.int
|
||||||
SERIAL_ENABLED_FIELD_NUMBER: builtins.int
|
SERIAL_ENABLED_FIELD_NUMBER: builtins.int
|
||||||
BUTTON_GPIO_FIELD_NUMBER: builtins.int
|
BUTTON_GPIO_FIELD_NUMBER: builtins.int
|
||||||
@@ -366,7 +283,6 @@ class Config(google.protobuf.message.Message):
|
|||||||
DISABLE_TRIPLE_CLICK_FIELD_NUMBER: builtins.int
|
DISABLE_TRIPLE_CLICK_FIELD_NUMBER: builtins.int
|
||||||
TZDEF_FIELD_NUMBER: builtins.int
|
TZDEF_FIELD_NUMBER: builtins.int
|
||||||
LED_HEARTBEAT_DISABLED_FIELD_NUMBER: builtins.int
|
LED_HEARTBEAT_DISABLED_FIELD_NUMBER: builtins.int
|
||||||
BUZZER_MODE_FIELD_NUMBER: builtins.int
|
|
||||||
role: global___Config.DeviceConfig.Role.ValueType
|
role: global___Config.DeviceConfig.Role.ValueType
|
||||||
"""
|
"""
|
||||||
Sets the role of node
|
Sets the role of node
|
||||||
@@ -417,11 +333,6 @@ class Config(google.protobuf.message.Message):
|
|||||||
"""
|
"""
|
||||||
If true, disable the default blinking LED (LED_PIN) behavior on the device
|
If true, disable the default blinking LED (LED_PIN) behavior on the device
|
||||||
"""
|
"""
|
||||||
buzzer_mode: global___Config.DeviceConfig.BuzzerMode.ValueType
|
|
||||||
"""
|
|
||||||
Controls buzzer behavior for audio feedback
|
|
||||||
Defaults to ENABLED
|
|
||||||
"""
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
@@ -436,9 +347,8 @@ class Config(google.protobuf.message.Message):
|
|||||||
disable_triple_click: builtins.bool = ...,
|
disable_triple_click: builtins.bool = ...,
|
||||||
tzdef: builtins.str = ...,
|
tzdef: builtins.str = ...,
|
||||||
led_heartbeat_disabled: builtins.bool = ...,
|
led_heartbeat_disabled: builtins.bool = ...,
|
||||||
buzzer_mode: global___Config.DeviceConfig.BuzzerMode.ValueType = ...,
|
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
def ClearField(self, field_name: typing.Literal["button_gpio", b"button_gpio", "buzzer_gpio", b"buzzer_gpio", "buzzer_mode", b"buzzer_mode", "disable_triple_click", b"disable_triple_click", "double_tap_as_button_press", b"double_tap_as_button_press", "is_managed", b"is_managed", "led_heartbeat_disabled", b"led_heartbeat_disabled", "node_info_broadcast_secs", b"node_info_broadcast_secs", "rebroadcast_mode", b"rebroadcast_mode", "role", b"role", "serial_enabled", b"serial_enabled", "tzdef", b"tzdef"]) -> None: ...
|
def ClearField(self, field_name: typing.Literal["button_gpio", b"button_gpio", "buzzer_gpio", b"buzzer_gpio", "disable_triple_click", b"disable_triple_click", "double_tap_as_button_press", b"double_tap_as_button_press", "is_managed", b"is_managed", "led_heartbeat_disabled", b"led_heartbeat_disabled", "node_info_broadcast_secs", b"node_info_broadcast_secs", "rebroadcast_mode", b"rebroadcast_mode", "role", b"role", "serial_enabled", b"serial_enabled", "tzdef", b"tzdef"]) -> None: ...
|
||||||
|
|
||||||
@typing.final
|
@typing.final
|
||||||
class PositionConfig(google.protobuf.message.Message):
|
class PositionConfig(google.protobuf.message.Message):
|
||||||
@@ -871,7 +781,6 @@ class Config(google.protobuf.message.Message):
|
|||||||
IPV4_CONFIG_FIELD_NUMBER: builtins.int
|
IPV4_CONFIG_FIELD_NUMBER: builtins.int
|
||||||
RSYSLOG_SERVER_FIELD_NUMBER: builtins.int
|
RSYSLOG_SERVER_FIELD_NUMBER: builtins.int
|
||||||
ENABLED_PROTOCOLS_FIELD_NUMBER: builtins.int
|
ENABLED_PROTOCOLS_FIELD_NUMBER: builtins.int
|
||||||
IPV6_ENABLED_FIELD_NUMBER: builtins.int
|
|
||||||
wifi_enabled: builtins.bool
|
wifi_enabled: builtins.bool
|
||||||
"""
|
"""
|
||||||
Enable WiFi (disables Bluetooth)
|
Enable WiFi (disables Bluetooth)
|
||||||
@@ -887,7 +796,7 @@ class Config(google.protobuf.message.Message):
|
|||||||
"""
|
"""
|
||||||
ntp_server: builtins.str
|
ntp_server: builtins.str
|
||||||
"""
|
"""
|
||||||
NTP server to use if WiFi is conneced, defaults to `meshtastic.pool.ntp.org`
|
NTP server to use if WiFi is conneced, defaults to `0.pool.ntp.org`
|
||||||
"""
|
"""
|
||||||
eth_enabled: builtins.bool
|
eth_enabled: builtins.bool
|
||||||
"""
|
"""
|
||||||
@@ -905,10 +814,6 @@ class Config(google.protobuf.message.Message):
|
|||||||
"""
|
"""
|
||||||
Flags for enabling/disabling network protocols
|
Flags for enabling/disabling network protocols
|
||||||
"""
|
"""
|
||||||
ipv6_enabled: builtins.bool
|
|
||||||
"""
|
|
||||||
Enable/Disable ipv6 support
|
|
||||||
"""
|
|
||||||
@property
|
@property
|
||||||
def ipv4_config(self) -> global___Config.NetworkConfig.IpV4Config:
|
def ipv4_config(self) -> global___Config.NetworkConfig.IpV4Config:
|
||||||
"""
|
"""
|
||||||
@@ -927,10 +832,9 @@ class Config(google.protobuf.message.Message):
|
|||||||
ipv4_config: global___Config.NetworkConfig.IpV4Config | None = ...,
|
ipv4_config: global___Config.NetworkConfig.IpV4Config | None = ...,
|
||||||
rsyslog_server: builtins.str = ...,
|
rsyslog_server: builtins.str = ...,
|
||||||
enabled_protocols: builtins.int = ...,
|
enabled_protocols: builtins.int = ...,
|
||||||
ipv6_enabled: builtins.bool = ...,
|
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
def HasField(self, field_name: typing.Literal["ipv4_config", b"ipv4_config"]) -> builtins.bool: ...
|
def HasField(self, field_name: typing.Literal["ipv4_config", b"ipv4_config"]) -> builtins.bool: ...
|
||||||
def ClearField(self, field_name: typing.Literal["address_mode", b"address_mode", "enabled_protocols", b"enabled_protocols", "eth_enabled", b"eth_enabled", "ipv4_config", b"ipv4_config", "ipv6_enabled", b"ipv6_enabled", "ntp_server", b"ntp_server", "rsyslog_server", b"rsyslog_server", "wifi_enabled", b"wifi_enabled", "wifi_psk", b"wifi_psk", "wifi_ssid", b"wifi_ssid"]) -> None: ...
|
def ClearField(self, field_name: typing.Literal["address_mode", b"address_mode", "enabled_protocols", b"enabled_protocols", "eth_enabled", b"eth_enabled", "ipv4_config", b"ipv4_config", "ntp_server", b"ntp_server", "rsyslog_server", b"rsyslog_server", "wifi_enabled", b"wifi_enabled", "wifi_psk", b"wifi_psk", "wifi_ssid", b"wifi_ssid"]) -> None: ...
|
||||||
|
|
||||||
@typing.final
|
@typing.final
|
||||||
class DisplayConfig(google.protobuf.message.Message):
|
class DisplayConfig(google.protobuf.message.Message):
|
||||||
@@ -940,20 +844,80 @@ class Config(google.protobuf.message.Message):
|
|||||||
|
|
||||||
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
||||||
|
|
||||||
class _DeprecatedGpsCoordinateFormat:
|
class _GpsCoordinateFormat:
|
||||||
ValueType = typing.NewType("ValueType", builtins.int)
|
ValueType = typing.NewType("ValueType", builtins.int)
|
||||||
V: typing_extensions.TypeAlias = ValueType
|
V: typing_extensions.TypeAlias = ValueType
|
||||||
|
|
||||||
class _DeprecatedGpsCoordinateFormatEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[Config.DisplayConfig._DeprecatedGpsCoordinateFormat.ValueType], builtins.type):
|
class _GpsCoordinateFormatEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[Config.DisplayConfig._GpsCoordinateFormat.ValueType], builtins.type):
|
||||||
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
|
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
|
||||||
UNUSED: Config.DisplayConfig._DeprecatedGpsCoordinateFormat.ValueType # 0
|
DEC: Config.DisplayConfig._GpsCoordinateFormat.ValueType # 0
|
||||||
|
|
||||||
class DeprecatedGpsCoordinateFormat(_DeprecatedGpsCoordinateFormat, metaclass=_DeprecatedGpsCoordinateFormatEnumTypeWrapper):
|
|
||||||
"""
|
"""
|
||||||
Deprecated in 2.7.4: Unused
|
GPS coordinates are displayed in the normal decimal degrees format:
|
||||||
|
DD.DDDDDD DDD.DDDDDD
|
||||||
|
"""
|
||||||
|
DMS: Config.DisplayConfig._GpsCoordinateFormat.ValueType # 1
|
||||||
|
"""
|
||||||
|
GPS coordinates are displayed in the degrees minutes seconds format:
|
||||||
|
DD°MM'SS"C DDD°MM'SS"C, where C is the compass point representing the locations quadrant
|
||||||
|
"""
|
||||||
|
UTM: Config.DisplayConfig._GpsCoordinateFormat.ValueType # 2
|
||||||
|
"""
|
||||||
|
Universal Transverse Mercator format:
|
||||||
|
ZZB EEEEEE NNNNNNN, where Z is zone, B is band, E is easting, N is northing
|
||||||
|
"""
|
||||||
|
MGRS: Config.DisplayConfig._GpsCoordinateFormat.ValueType # 3
|
||||||
|
"""
|
||||||
|
Military Grid Reference System format:
|
||||||
|
ZZB CD EEEEE NNNNN, where Z is zone, B is band, C is the east 100k square, D is the north 100k square,
|
||||||
|
E is easting, N is northing
|
||||||
|
"""
|
||||||
|
OLC: Config.DisplayConfig._GpsCoordinateFormat.ValueType # 4
|
||||||
|
"""
|
||||||
|
Open Location Code (aka Plus Codes).
|
||||||
|
"""
|
||||||
|
OSGR: Config.DisplayConfig._GpsCoordinateFormat.ValueType # 5
|
||||||
|
"""
|
||||||
|
Ordnance Survey Grid Reference (the National Grid System of the UK).
|
||||||
|
Format: AB EEEEE NNNNN, where A is the east 100k square, B is the north 100k square,
|
||||||
|
E is the easting, N is the northing
|
||||||
"""
|
"""
|
||||||
|
|
||||||
UNUSED: Config.DisplayConfig.DeprecatedGpsCoordinateFormat.ValueType # 0
|
class GpsCoordinateFormat(_GpsCoordinateFormat, metaclass=_GpsCoordinateFormatEnumTypeWrapper):
|
||||||
|
"""
|
||||||
|
How the GPS coordinates are displayed on the OLED screen.
|
||||||
|
"""
|
||||||
|
|
||||||
|
DEC: Config.DisplayConfig.GpsCoordinateFormat.ValueType # 0
|
||||||
|
"""
|
||||||
|
GPS coordinates are displayed in the normal decimal degrees format:
|
||||||
|
DD.DDDDDD DDD.DDDDDD
|
||||||
|
"""
|
||||||
|
DMS: Config.DisplayConfig.GpsCoordinateFormat.ValueType # 1
|
||||||
|
"""
|
||||||
|
GPS coordinates are displayed in the degrees minutes seconds format:
|
||||||
|
DD°MM'SS"C DDD°MM'SS"C, where C is the compass point representing the locations quadrant
|
||||||
|
"""
|
||||||
|
UTM: Config.DisplayConfig.GpsCoordinateFormat.ValueType # 2
|
||||||
|
"""
|
||||||
|
Universal Transverse Mercator format:
|
||||||
|
ZZB EEEEEE NNNNNNN, where Z is zone, B is band, E is easting, N is northing
|
||||||
|
"""
|
||||||
|
MGRS: Config.DisplayConfig.GpsCoordinateFormat.ValueType # 3
|
||||||
|
"""
|
||||||
|
Military Grid Reference System format:
|
||||||
|
ZZB CD EEEEE NNNNN, where Z is zone, B is band, C is the east 100k square, D is the north 100k square,
|
||||||
|
E is easting, N is northing
|
||||||
|
"""
|
||||||
|
OLC: Config.DisplayConfig.GpsCoordinateFormat.ValueType # 4
|
||||||
|
"""
|
||||||
|
Open Location Code (aka Plus Codes).
|
||||||
|
"""
|
||||||
|
OSGR: Config.DisplayConfig.GpsCoordinateFormat.ValueType # 5
|
||||||
|
"""
|
||||||
|
Ordnance Survey Grid Reference (the National Grid System of the UK).
|
||||||
|
Format: AB EEEEE NNNNN, where A is the east 100k square, B is the north 100k square,
|
||||||
|
E is the easting, N is the northing
|
||||||
|
"""
|
||||||
|
|
||||||
class _DisplayUnits:
|
class _DisplayUnits:
|
||||||
ValueType = typing.NewType("ValueType", builtins.int)
|
ValueType = typing.NewType("ValueType", builtins.int)
|
||||||
@@ -992,28 +956,20 @@ class Config(google.protobuf.message.Message):
|
|||||||
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
|
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
|
||||||
OLED_AUTO: Config.DisplayConfig._OledType.ValueType # 0
|
OLED_AUTO: Config.DisplayConfig._OledType.ValueType # 0
|
||||||
"""
|
"""
|
||||||
Default / Autodetect
|
Default / Auto
|
||||||
"""
|
"""
|
||||||
OLED_SSD1306: Config.DisplayConfig._OledType.ValueType # 1
|
OLED_SSD1306: Config.DisplayConfig._OledType.ValueType # 1
|
||||||
"""
|
"""
|
||||||
Default / Autodetect
|
Default / Auto
|
||||||
"""
|
"""
|
||||||
OLED_SH1106: Config.DisplayConfig._OledType.ValueType # 2
|
OLED_SH1106: Config.DisplayConfig._OledType.ValueType # 2
|
||||||
"""
|
"""
|
||||||
Default / Autodetect
|
Default / Auto
|
||||||
"""
|
"""
|
||||||
OLED_SH1107: Config.DisplayConfig._OledType.ValueType # 3
|
OLED_SH1107: Config.DisplayConfig._OledType.ValueType # 3
|
||||||
"""
|
"""
|
||||||
Can not be auto detected but set by proto. Used for 128x64 screens
|
|
||||||
"""
|
|
||||||
OLED_SH1107_128_128: Config.DisplayConfig._OledType.ValueType # 4
|
|
||||||
"""
|
|
||||||
Can not be auto detected but set by proto. Used for 128x128 screens
|
Can not be auto detected but set by proto. Used for 128x128 screens
|
||||||
"""
|
"""
|
||||||
OLED_SH1107_ROTATED: Config.DisplayConfig._OledType.ValueType # 5
|
|
||||||
"""
|
|
||||||
Can not be auto detected but set by proto. Used for 64x128 rotated screens
|
|
||||||
"""
|
|
||||||
|
|
||||||
class OledType(_OledType, metaclass=_OledTypeEnumTypeWrapper):
|
class OledType(_OledType, metaclass=_OledTypeEnumTypeWrapper):
|
||||||
"""
|
"""
|
||||||
@@ -1022,28 +978,20 @@ class Config(google.protobuf.message.Message):
|
|||||||
|
|
||||||
OLED_AUTO: Config.DisplayConfig.OledType.ValueType # 0
|
OLED_AUTO: Config.DisplayConfig.OledType.ValueType # 0
|
||||||
"""
|
"""
|
||||||
Default / Autodetect
|
Default / Auto
|
||||||
"""
|
"""
|
||||||
OLED_SSD1306: Config.DisplayConfig.OledType.ValueType # 1
|
OLED_SSD1306: Config.DisplayConfig.OledType.ValueType # 1
|
||||||
"""
|
"""
|
||||||
Default / Autodetect
|
Default / Auto
|
||||||
"""
|
"""
|
||||||
OLED_SH1106: Config.DisplayConfig.OledType.ValueType # 2
|
OLED_SH1106: Config.DisplayConfig.OledType.ValueType # 2
|
||||||
"""
|
"""
|
||||||
Default / Autodetect
|
Default / Auto
|
||||||
"""
|
"""
|
||||||
OLED_SH1107: Config.DisplayConfig.OledType.ValueType # 3
|
OLED_SH1107: Config.DisplayConfig.OledType.ValueType # 3
|
||||||
"""
|
"""
|
||||||
Can not be auto detected but set by proto. Used for 128x64 screens
|
|
||||||
"""
|
|
||||||
OLED_SH1107_128_128: Config.DisplayConfig.OledType.ValueType # 4
|
|
||||||
"""
|
|
||||||
Can not be auto detected but set by proto. Used for 128x128 screens
|
Can not be auto detected but set by proto. Used for 128x128 screens
|
||||||
"""
|
"""
|
||||||
OLED_SH1107_ROTATED: Config.DisplayConfig.OledType.ValueType # 5
|
|
||||||
"""
|
|
||||||
Can not be auto detected but set by proto. Used for 64x128 rotated screens
|
|
||||||
"""
|
|
||||||
|
|
||||||
class _DisplayMode:
|
class _DisplayMode:
|
||||||
ValueType = typing.NewType("ValueType", builtins.int)
|
ValueType = typing.NewType("ValueType", builtins.int)
|
||||||
@@ -1171,16 +1119,13 @@ class Config(google.protobuf.message.Message):
|
|||||||
WAKE_ON_TAP_OR_MOTION_FIELD_NUMBER: builtins.int
|
WAKE_ON_TAP_OR_MOTION_FIELD_NUMBER: builtins.int
|
||||||
COMPASS_ORIENTATION_FIELD_NUMBER: builtins.int
|
COMPASS_ORIENTATION_FIELD_NUMBER: builtins.int
|
||||||
USE_12H_CLOCK_FIELD_NUMBER: builtins.int
|
USE_12H_CLOCK_FIELD_NUMBER: builtins.int
|
||||||
USE_LONG_NODE_NAME_FIELD_NUMBER: builtins.int
|
|
||||||
ENABLE_MESSAGE_BUBBLES_FIELD_NUMBER: builtins.int
|
|
||||||
screen_on_secs: builtins.int
|
screen_on_secs: builtins.int
|
||||||
"""
|
"""
|
||||||
Number of seconds the screen stays on after pressing the user button or receiving a message
|
Number of seconds the screen stays on after pressing the user button or receiving a message
|
||||||
0 for default of one minute MAXUINT for always on
|
0 for default of one minute MAXUINT for always on
|
||||||
"""
|
"""
|
||||||
gps_format: global___Config.DisplayConfig.DeprecatedGpsCoordinateFormat.ValueType
|
gps_format: global___Config.DisplayConfig.GpsCoordinateFormat.ValueType
|
||||||
"""
|
"""
|
||||||
Deprecated in 2.7.4: Unused
|
|
||||||
How the GPS coordinates are formatted on the OLED screen.
|
How the GPS coordinates are formatted on the OLED screen.
|
||||||
"""
|
"""
|
||||||
auto_screen_carousel_secs: builtins.int
|
auto_screen_carousel_secs: builtins.int
|
||||||
@@ -1226,20 +1171,11 @@ class Config(google.protobuf.message.Message):
|
|||||||
If false (default), the device will display the time in 24-hour format on screen.
|
If false (default), the device will display the time in 24-hour format on screen.
|
||||||
If true, the device will display the time in 12-hour format on screen.
|
If true, the device will display the time in 12-hour format on screen.
|
||||||
"""
|
"""
|
||||||
use_long_node_name: builtins.bool
|
|
||||||
"""
|
|
||||||
If false (default), the device will use short names for various display screens.
|
|
||||||
If true, node names will show in long format
|
|
||||||
"""
|
|
||||||
enable_message_bubbles: builtins.bool
|
|
||||||
"""
|
|
||||||
If true, the device will display message bubbles on screen.
|
|
||||||
"""
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
screen_on_secs: builtins.int = ...,
|
screen_on_secs: builtins.int = ...,
|
||||||
gps_format: global___Config.DisplayConfig.DeprecatedGpsCoordinateFormat.ValueType = ...,
|
gps_format: global___Config.DisplayConfig.GpsCoordinateFormat.ValueType = ...,
|
||||||
auto_screen_carousel_secs: builtins.int = ...,
|
auto_screen_carousel_secs: builtins.int = ...,
|
||||||
compass_north_top: builtins.bool = ...,
|
compass_north_top: builtins.bool = ...,
|
||||||
flip_screen: builtins.bool = ...,
|
flip_screen: builtins.bool = ...,
|
||||||
@@ -1250,10 +1186,8 @@ class Config(google.protobuf.message.Message):
|
|||||||
wake_on_tap_or_motion: builtins.bool = ...,
|
wake_on_tap_or_motion: builtins.bool = ...,
|
||||||
compass_orientation: global___Config.DisplayConfig.CompassOrientation.ValueType = ...,
|
compass_orientation: global___Config.DisplayConfig.CompassOrientation.ValueType = ...,
|
||||||
use_12h_clock: builtins.bool = ...,
|
use_12h_clock: builtins.bool = ...,
|
||||||
use_long_node_name: builtins.bool = ...,
|
|
||||||
enable_message_bubbles: builtins.bool = ...,
|
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
def ClearField(self, field_name: typing.Literal["auto_screen_carousel_secs", b"auto_screen_carousel_secs", "compass_north_top", b"compass_north_top", "compass_orientation", b"compass_orientation", "displaymode", b"displaymode", "enable_message_bubbles", b"enable_message_bubbles", "flip_screen", b"flip_screen", "gps_format", b"gps_format", "heading_bold", b"heading_bold", "oled", b"oled", "screen_on_secs", b"screen_on_secs", "units", b"units", "use_12h_clock", b"use_12h_clock", "use_long_node_name", b"use_long_node_name", "wake_on_tap_or_motion", b"wake_on_tap_or_motion"]) -> None: ...
|
def ClearField(self, field_name: typing.Literal["auto_screen_carousel_secs", b"auto_screen_carousel_secs", "compass_north_top", b"compass_north_top", "compass_orientation", b"compass_orientation", "displaymode", b"displaymode", "flip_screen", b"flip_screen", "gps_format", b"gps_format", "heading_bold", b"heading_bold", "oled", b"oled", "screen_on_secs", b"screen_on_secs", "units", b"units", "use_12h_clock", b"use_12h_clock", "wake_on_tap_or_motion", b"wake_on_tap_or_motion"]) -> None: ...
|
||||||
|
|
||||||
@typing.final
|
@typing.final
|
||||||
class LoRaConfig(google.protobuf.message.Message):
|
class LoRaConfig(google.protobuf.message.Message):
|
||||||
@@ -1357,51 +1291,6 @@ class Config(google.protobuf.message.Message):
|
|||||||
"""
|
"""
|
||||||
Philippines 915mhz
|
Philippines 915mhz
|
||||||
"""
|
"""
|
||||||
ANZ_433: Config.LoRaConfig._RegionCode.ValueType # 22
|
|
||||||
"""
|
|
||||||
Australia / New Zealand 433MHz
|
|
||||||
"""
|
|
||||||
KZ_433: Config.LoRaConfig._RegionCode.ValueType # 23
|
|
||||||
"""
|
|
||||||
Kazakhstan 433MHz
|
|
||||||
"""
|
|
||||||
KZ_863: Config.LoRaConfig._RegionCode.ValueType # 24
|
|
||||||
"""
|
|
||||||
Kazakhstan 863MHz
|
|
||||||
"""
|
|
||||||
NP_865: Config.LoRaConfig._RegionCode.ValueType # 25
|
|
||||||
"""
|
|
||||||
Nepal 865MHz
|
|
||||||
"""
|
|
||||||
BR_902: Config.LoRaConfig._RegionCode.ValueType # 26
|
|
||||||
"""
|
|
||||||
Brazil 902MHz
|
|
||||||
"""
|
|
||||||
ITU1_2M: Config.LoRaConfig._RegionCode.ValueType # 27
|
|
||||||
"""
|
|
||||||
ITU Region 1 Amateur Radio 2m band (144-146 MHz)
|
|
||||||
"""
|
|
||||||
ITU2_2M: Config.LoRaConfig._RegionCode.ValueType # 28
|
|
||||||
"""
|
|
||||||
ITU Region 2 Amateur Radio 2m band (144-148 MHz)
|
|
||||||
"""
|
|
||||||
EU_866: Config.LoRaConfig._RegionCode.ValueType # 29
|
|
||||||
"""
|
|
||||||
EU 866MHz band (Band no. 47b of 2006/771/EC and subsequent amendments) for Non-specific short-range devices (SRD)
|
|
||||||
"""
|
|
||||||
EU_874: Config.LoRaConfig._RegionCode.ValueType # 30
|
|
||||||
"""
|
|
||||||
EU 874MHz and 917MHz bands (Band no. 1 and 4 of 2022/172/EC and subsequent amendments) for Non-specific short-range devices (SRD)
|
|
||||||
"""
|
|
||||||
EU_917: Config.LoRaConfig._RegionCode.ValueType # 31
|
|
||||||
EU_N_868: Config.LoRaConfig._RegionCode.ValueType # 32
|
|
||||||
"""
|
|
||||||
EU 868MHz band, with narrow presets
|
|
||||||
"""
|
|
||||||
ITU3_2M: Config.LoRaConfig._RegionCode.ValueType # 33
|
|
||||||
"""
|
|
||||||
ITU Region 3 Amateur Radio 2m band (144-148 MHz)
|
|
||||||
"""
|
|
||||||
|
|
||||||
class RegionCode(_RegionCode, metaclass=_RegionCodeEnumTypeWrapper): ...
|
class RegionCode(_RegionCode, metaclass=_RegionCodeEnumTypeWrapper): ...
|
||||||
UNSET: Config.LoRaConfig.RegionCode.ValueType # 0
|
UNSET: Config.LoRaConfig.RegionCode.ValueType # 0
|
||||||
@@ -1492,51 +1381,6 @@ class Config(google.protobuf.message.Message):
|
|||||||
"""
|
"""
|
||||||
Philippines 915mhz
|
Philippines 915mhz
|
||||||
"""
|
"""
|
||||||
ANZ_433: Config.LoRaConfig.RegionCode.ValueType # 22
|
|
||||||
"""
|
|
||||||
Australia / New Zealand 433MHz
|
|
||||||
"""
|
|
||||||
KZ_433: Config.LoRaConfig.RegionCode.ValueType # 23
|
|
||||||
"""
|
|
||||||
Kazakhstan 433MHz
|
|
||||||
"""
|
|
||||||
KZ_863: Config.LoRaConfig.RegionCode.ValueType # 24
|
|
||||||
"""
|
|
||||||
Kazakhstan 863MHz
|
|
||||||
"""
|
|
||||||
NP_865: Config.LoRaConfig.RegionCode.ValueType # 25
|
|
||||||
"""
|
|
||||||
Nepal 865MHz
|
|
||||||
"""
|
|
||||||
BR_902: Config.LoRaConfig.RegionCode.ValueType # 26
|
|
||||||
"""
|
|
||||||
Brazil 902MHz
|
|
||||||
"""
|
|
||||||
ITU1_2M: Config.LoRaConfig.RegionCode.ValueType # 27
|
|
||||||
"""
|
|
||||||
ITU Region 1 Amateur Radio 2m band (144-146 MHz)
|
|
||||||
"""
|
|
||||||
ITU2_2M: Config.LoRaConfig.RegionCode.ValueType # 28
|
|
||||||
"""
|
|
||||||
ITU Region 2 Amateur Radio 2m band (144-148 MHz)
|
|
||||||
"""
|
|
||||||
EU_866: Config.LoRaConfig.RegionCode.ValueType # 29
|
|
||||||
"""
|
|
||||||
EU 866MHz band (Band no. 47b of 2006/771/EC and subsequent amendments) for Non-specific short-range devices (SRD)
|
|
||||||
"""
|
|
||||||
EU_874: Config.LoRaConfig.RegionCode.ValueType # 30
|
|
||||||
"""
|
|
||||||
EU 874MHz and 917MHz bands (Band no. 1 and 4 of 2022/172/EC and subsequent amendments) for Non-specific short-range devices (SRD)
|
|
||||||
"""
|
|
||||||
EU_917: Config.LoRaConfig.RegionCode.ValueType # 31
|
|
||||||
EU_N_868: Config.LoRaConfig.RegionCode.ValueType # 32
|
|
||||||
"""
|
|
||||||
EU 868MHz band, with narrow presets
|
|
||||||
"""
|
|
||||||
ITU3_2M: Config.LoRaConfig.RegionCode.ValueType # 33
|
|
||||||
"""
|
|
||||||
ITU Region 3 Amateur Radio 2m band (144-148 MHz)
|
|
||||||
"""
|
|
||||||
|
|
||||||
class _ModemPreset:
|
class _ModemPreset:
|
||||||
ValueType = typing.NewType("ValueType", builtins.int)
|
ValueType = typing.NewType("ValueType", builtins.int)
|
||||||
@@ -1551,7 +1395,6 @@ class Config(google.protobuf.message.Message):
|
|||||||
LONG_SLOW: Config.LoRaConfig._ModemPreset.ValueType # 1
|
LONG_SLOW: Config.LoRaConfig._ModemPreset.ValueType # 1
|
||||||
"""
|
"""
|
||||||
Long Range - Slow
|
Long Range - Slow
|
||||||
Deprecated in 2.7: Unpopular slow preset.
|
|
||||||
"""
|
"""
|
||||||
VERY_LONG_SLOW: Config.LoRaConfig._ModemPreset.ValueType # 2
|
VERY_LONG_SLOW: Config.LoRaConfig._ModemPreset.ValueType # 2
|
||||||
"""
|
"""
|
||||||
@@ -1584,36 +1427,6 @@ class Config(google.protobuf.message.Message):
|
|||||||
This is the fastest preset and the only one with 500kHz bandwidth.
|
This is the fastest preset and the only one with 500kHz bandwidth.
|
||||||
It is not legal to use in all regions due to this wider bandwidth.
|
It is not legal to use in all regions due to this wider bandwidth.
|
||||||
"""
|
"""
|
||||||
LONG_TURBO: Config.LoRaConfig._ModemPreset.ValueType # 9
|
|
||||||
"""
|
|
||||||
Long Range - Turbo
|
|
||||||
This preset performs similarly to LongFast, but with 500Khz bandwidth.
|
|
||||||
"""
|
|
||||||
LITE_FAST: Config.LoRaConfig._ModemPreset.ValueType # 10
|
|
||||||
"""
|
|
||||||
Lite Fast
|
|
||||||
Medium range preset optimized for EU 866MHz SRD band with 125kHz bandwidth.
|
|
||||||
Comparable link budget to MEDIUM_FAST but compliant with Band no. 47b of 2006/771/EC.
|
|
||||||
"""
|
|
||||||
LITE_SLOW: Config.LoRaConfig._ModemPreset.ValueType # 11
|
|
||||||
"""
|
|
||||||
Lite Slow
|
|
||||||
Medium-to-moderate range preset optimized for EU 866MHz SRD band with 125kHz bandwidth.
|
|
||||||
Comparable link budget to LONG_FAST but compliant with Band no. 47b of 2006/771/EC.
|
|
||||||
"""
|
|
||||||
NARROW_FAST: Config.LoRaConfig._ModemPreset.ValueType # 12
|
|
||||||
"""
|
|
||||||
Narrow Fast
|
|
||||||
Medium-to-moderate range preset optimized for EU 868MHz band with 62.5kHz bandwidth.
|
|
||||||
Comparable link budget to SHORT_SLOW, but with half the data rate.
|
|
||||||
Intended to avoid interference with other devices.
|
|
||||||
"""
|
|
||||||
NARROW_SLOW: Config.LoRaConfig._ModemPreset.ValueType # 13
|
|
||||||
"""
|
|
||||||
Narrow Slow
|
|
||||||
Moderate range preset optimized for EU 868MHz band with 62.5kHz bandwidth.
|
|
||||||
Comparable link budget and data rate to LONG_FAST.
|
|
||||||
"""
|
|
||||||
|
|
||||||
class ModemPreset(_ModemPreset, metaclass=_ModemPresetEnumTypeWrapper):
|
class ModemPreset(_ModemPreset, metaclass=_ModemPresetEnumTypeWrapper):
|
||||||
"""
|
"""
|
||||||
@@ -1628,7 +1441,6 @@ class Config(google.protobuf.message.Message):
|
|||||||
LONG_SLOW: Config.LoRaConfig.ModemPreset.ValueType # 1
|
LONG_SLOW: Config.LoRaConfig.ModemPreset.ValueType # 1
|
||||||
"""
|
"""
|
||||||
Long Range - Slow
|
Long Range - Slow
|
||||||
Deprecated in 2.7: Unpopular slow preset.
|
|
||||||
"""
|
"""
|
||||||
VERY_LONG_SLOW: Config.LoRaConfig.ModemPreset.ValueType # 2
|
VERY_LONG_SLOW: Config.LoRaConfig.ModemPreset.ValueType # 2
|
||||||
"""
|
"""
|
||||||
@@ -1661,69 +1473,6 @@ class Config(google.protobuf.message.Message):
|
|||||||
This is the fastest preset and the only one with 500kHz bandwidth.
|
This is the fastest preset and the only one with 500kHz bandwidth.
|
||||||
It is not legal to use in all regions due to this wider bandwidth.
|
It is not legal to use in all regions due to this wider bandwidth.
|
||||||
"""
|
"""
|
||||||
LONG_TURBO: Config.LoRaConfig.ModemPreset.ValueType # 9
|
|
||||||
"""
|
|
||||||
Long Range - Turbo
|
|
||||||
This preset performs similarly to LongFast, but with 500Khz bandwidth.
|
|
||||||
"""
|
|
||||||
LITE_FAST: Config.LoRaConfig.ModemPreset.ValueType # 10
|
|
||||||
"""
|
|
||||||
Lite Fast
|
|
||||||
Medium range preset optimized for EU 866MHz SRD band with 125kHz bandwidth.
|
|
||||||
Comparable link budget to MEDIUM_FAST but compliant with Band no. 47b of 2006/771/EC.
|
|
||||||
"""
|
|
||||||
LITE_SLOW: Config.LoRaConfig.ModemPreset.ValueType # 11
|
|
||||||
"""
|
|
||||||
Lite Slow
|
|
||||||
Medium-to-moderate range preset optimized for EU 866MHz SRD band with 125kHz bandwidth.
|
|
||||||
Comparable link budget to LONG_FAST but compliant with Band no. 47b of 2006/771/EC.
|
|
||||||
"""
|
|
||||||
NARROW_FAST: Config.LoRaConfig.ModemPreset.ValueType # 12
|
|
||||||
"""
|
|
||||||
Narrow Fast
|
|
||||||
Medium-to-moderate range preset optimized for EU 868MHz band with 62.5kHz bandwidth.
|
|
||||||
Comparable link budget to SHORT_SLOW, but with half the data rate.
|
|
||||||
Intended to avoid interference with other devices.
|
|
||||||
"""
|
|
||||||
NARROW_SLOW: Config.LoRaConfig.ModemPreset.ValueType # 13
|
|
||||||
"""
|
|
||||||
Narrow Slow
|
|
||||||
Moderate range preset optimized for EU 868MHz band with 62.5kHz bandwidth.
|
|
||||||
Comparable link budget and data rate to LONG_FAST.
|
|
||||||
"""
|
|
||||||
|
|
||||||
class _FEM_LNA_Mode:
|
|
||||||
ValueType = typing.NewType("ValueType", builtins.int)
|
|
||||||
V: typing_extensions.TypeAlias = ValueType
|
|
||||||
|
|
||||||
class _FEM_LNA_ModeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[Config.LoRaConfig._FEM_LNA_Mode.ValueType], builtins.type):
|
|
||||||
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
|
|
||||||
DISABLED: Config.LoRaConfig._FEM_LNA_Mode.ValueType # 0
|
|
||||||
"""
|
|
||||||
FEM_LNA is present but disabled
|
|
||||||
"""
|
|
||||||
ENABLED: Config.LoRaConfig._FEM_LNA_Mode.ValueType # 1
|
|
||||||
"""
|
|
||||||
FEM_LNA is present and enabled
|
|
||||||
"""
|
|
||||||
NOT_PRESENT: Config.LoRaConfig._FEM_LNA_Mode.ValueType # 2
|
|
||||||
"""
|
|
||||||
FEM_LNA is not present on the device
|
|
||||||
"""
|
|
||||||
|
|
||||||
class FEM_LNA_Mode(_FEM_LNA_Mode, metaclass=_FEM_LNA_ModeEnumTypeWrapper): ...
|
|
||||||
DISABLED: Config.LoRaConfig.FEM_LNA_Mode.ValueType # 0
|
|
||||||
"""
|
|
||||||
FEM_LNA is present but disabled
|
|
||||||
"""
|
|
||||||
ENABLED: Config.LoRaConfig.FEM_LNA_Mode.ValueType # 1
|
|
||||||
"""
|
|
||||||
FEM_LNA is present and enabled
|
|
||||||
"""
|
|
||||||
NOT_PRESENT: Config.LoRaConfig.FEM_LNA_Mode.ValueType # 2
|
|
||||||
"""
|
|
||||||
FEM_LNA is not present on the device
|
|
||||||
"""
|
|
||||||
|
|
||||||
USE_PRESET_FIELD_NUMBER: builtins.int
|
USE_PRESET_FIELD_NUMBER: builtins.int
|
||||||
MODEM_PRESET_FIELD_NUMBER: builtins.int
|
MODEM_PRESET_FIELD_NUMBER: builtins.int
|
||||||
@@ -1743,8 +1492,6 @@ class Config(google.protobuf.message.Message):
|
|||||||
IGNORE_INCOMING_FIELD_NUMBER: builtins.int
|
IGNORE_INCOMING_FIELD_NUMBER: builtins.int
|
||||||
IGNORE_MQTT_FIELD_NUMBER: builtins.int
|
IGNORE_MQTT_FIELD_NUMBER: builtins.int
|
||||||
CONFIG_OK_TO_MQTT_FIELD_NUMBER: builtins.int
|
CONFIG_OK_TO_MQTT_FIELD_NUMBER: builtins.int
|
||||||
FEM_LNA_MODE_FIELD_NUMBER: builtins.int
|
|
||||||
SERIAL_HAL_ONLY_FIELD_NUMBER: builtins.int
|
|
||||||
use_preset: builtins.bool
|
use_preset: builtins.bool
|
||||||
"""
|
"""
|
||||||
When enabled, the `modem_preset` fields will be adhered to, else the `bandwidth`/`spread_factor`/`coding_rate`
|
When enabled, the `modem_preset` fields will be adhered to, else the `bandwidth`/`spread_factor`/`coding_rate`
|
||||||
@@ -1842,14 +1589,6 @@ class Config(google.protobuf.message.Message):
|
|||||||
"""
|
"""
|
||||||
Sets the ok_to_mqtt bit on outgoing packets
|
Sets the ok_to_mqtt bit on outgoing packets
|
||||||
"""
|
"""
|
||||||
fem_lna_mode: global___Config.LoRaConfig.FEM_LNA_Mode.ValueType
|
|
||||||
"""
|
|
||||||
Set where LORA FEM is enabled, disabled, or not present
|
|
||||||
"""
|
|
||||||
serial_hal_only: builtins.bool
|
|
||||||
"""
|
|
||||||
Don't use radiolib to initialize the radio, instead listen for a serialHal connection
|
|
||||||
"""
|
|
||||||
@property
|
@property
|
||||||
def ignore_incoming(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.int]:
|
def ignore_incoming(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.int]:
|
||||||
"""
|
"""
|
||||||
@@ -1879,10 +1618,8 @@ class Config(google.protobuf.message.Message):
|
|||||||
ignore_incoming: collections.abc.Iterable[builtins.int] | None = ...,
|
ignore_incoming: collections.abc.Iterable[builtins.int] | None = ...,
|
||||||
ignore_mqtt: builtins.bool = ...,
|
ignore_mqtt: builtins.bool = ...,
|
||||||
config_ok_to_mqtt: builtins.bool = ...,
|
config_ok_to_mqtt: builtins.bool = ...,
|
||||||
fem_lna_mode: global___Config.LoRaConfig.FEM_LNA_Mode.ValueType = ...,
|
|
||||||
serial_hal_only: builtins.bool = ...,
|
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
def ClearField(self, field_name: typing.Literal["bandwidth", b"bandwidth", "channel_num", b"channel_num", "coding_rate", b"coding_rate", "config_ok_to_mqtt", b"config_ok_to_mqtt", "fem_lna_mode", b"fem_lna_mode", "frequency_offset", b"frequency_offset", "hop_limit", b"hop_limit", "ignore_incoming", b"ignore_incoming", "ignore_mqtt", b"ignore_mqtt", "modem_preset", b"modem_preset", "override_duty_cycle", b"override_duty_cycle", "override_frequency", b"override_frequency", "pa_fan_disabled", b"pa_fan_disabled", "region", b"region", "serial_hal_only", b"serial_hal_only", "spread_factor", b"spread_factor", "sx126x_rx_boosted_gain", b"sx126x_rx_boosted_gain", "tx_enabled", b"tx_enabled", "tx_power", b"tx_power", "use_preset", b"use_preset"]) -> None: ...
|
def ClearField(self, field_name: typing.Literal["bandwidth", b"bandwidth", "channel_num", b"channel_num", "coding_rate", b"coding_rate", "config_ok_to_mqtt", b"config_ok_to_mqtt", "frequency_offset", b"frequency_offset", "hop_limit", b"hop_limit", "ignore_incoming", b"ignore_incoming", "ignore_mqtt", b"ignore_mqtt", "modem_preset", b"modem_preset", "override_duty_cycle", b"override_duty_cycle", "override_frequency", b"override_frequency", "pa_fan_disabled", b"pa_fan_disabled", "region", b"region", "spread_factor", b"spread_factor", "sx126x_rx_boosted_gain", b"sx126x_rx_boosted_gain", "tx_enabled", b"tx_enabled", "tx_power", b"tx_power", "use_preset", b"use_preset"]) -> None: ...
|
||||||
|
|
||||||
@typing.final
|
@typing.final
|
||||||
class BluetoothConfig(google.protobuf.message.Message):
|
class BluetoothConfig(google.protobuf.message.Message):
|
||||||
|
|||||||
31
meshtastic/protobuf/connection_status_pb2.py
generated
31
meshtastic/protobuf/connection_status_pb2.py
generated
@@ -11,29 +11,26 @@ from google.protobuf.internal import builder as _builder
|
|||||||
_sym_db = _symbol_database.Default()
|
_sym_db = _symbol_database.Default()
|
||||||
|
|
||||||
|
|
||||||
from meshtastic.protobuf import nanopb_pb2 as meshtastic_dot_protobuf_dot_nanopb__pb2
|
|
||||||
|
|
||||||
|
|
||||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n+meshtastic/protobuf/connection_status.proto\x12\x13meshtastic.protobuf\x1a meshtastic/protobuf/nanopb.proto\"\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\"w\n\x14WifiConnectionStatus\x12<\n\x06status\x18\x01 \x01(\x0b\x32,.meshtastic.protobuf.NetworkConnectionStatus\x12\x13\n\x04ssid\x18\x02 \x01(\tB\x05\x92?\x02\x08!\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\x66\n\x14org.meshtastic.protoB\x10\x43onnStatusProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
|
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n+meshtastic/protobuf/connection_status.proto\x12\x13meshtastic.protobuf\"\xd5\x02\n\x16\x44\x65viceConnectionStatus\x12<\n\x04wifi\x18\x01 \x01(\x0b\x32).meshtastic.protobuf.WifiConnectionStatusH\x00\x88\x01\x01\x12\x44\n\x08\x65thernet\x18\x02 \x01(\x0b\x32-.meshtastic.protobuf.EthernetConnectionStatusH\x01\x88\x01\x01\x12\x46\n\tbluetooth\x18\x03 \x01(\x0b\x32..meshtastic.protobuf.BluetoothConnectionStatusH\x02\x88\x01\x01\x12@\n\x06serial\x18\x04 \x01(\x0b\x32+.meshtastic.protobuf.SerialConnectionStatusH\x03\x88\x01\x01\x42\x07\n\x05_wifiB\x0b\n\t_ethernetB\x0c\n\n_bluetoothB\t\n\x07_serial\"p\n\x14WifiConnectionStatus\x12<\n\x06status\x18\x01 \x01(\x0b\x32,.meshtastic.protobuf.NetworkConnectionStatus\x12\x0c\n\x04ssid\x18\x02 \x01(\t\x12\x0c\n\x04rssi\x18\x03 \x01(\x05\"X\n\x18\x45thernetConnectionStatus\x12<\n\x06status\x18\x01 \x01(\x0b\x32,.meshtastic.protobuf.NetworkConnectionStatus\"{\n\x17NetworkConnectionStatus\x12\x12\n\nip_address\x18\x01 \x01(\x07\x12\x14\n\x0cis_connected\x18\x02 \x01(\x08\x12\x19\n\x11is_mqtt_connected\x18\x03 \x01(\x08\x12\x1b\n\x13is_syslog_connected\x18\x04 \x01(\x08\"L\n\x19\x42luetoothConnectionStatus\x12\x0b\n\x03pin\x18\x01 \x01(\r\x12\x0c\n\x04rssi\x18\x02 \x01(\x05\x12\x14\n\x0cis_connected\x18\x03 \x01(\x08\"<\n\x16SerialConnectionStatus\x12\x0c\n\x04\x62\x61ud\x18\x01 \x01(\r\x12\x14\n\x0cis_connected\x18\x02 \x01(\x08\x42\x65\n\x13\x63om.geeksville.meshB\x10\x43onnStatusProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
|
||||||
|
|
||||||
_globals = globals()
|
_globals = globals()
|
||||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.connection_status_pb2', _globals)
|
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.connection_status_pb2', _globals)
|
||||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||||
DESCRIPTOR._options = None
|
DESCRIPTOR._options = None
|
||||||
DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\020ConnStatusProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\020ConnStatusProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||||
_WIFICONNECTIONSTATUS.fields_by_name['ssid']._options = None
|
_globals['_DEVICECONNECTIONSTATUS']._serialized_start=69
|
||||||
_WIFICONNECTIONSTATUS.fields_by_name['ssid']._serialized_options = b'\222?\002\010!'
|
_globals['_DEVICECONNECTIONSTATUS']._serialized_end=410
|
||||||
_globals['_DEVICECONNECTIONSTATUS']._serialized_start=103
|
_globals['_WIFICONNECTIONSTATUS']._serialized_start=412
|
||||||
_globals['_DEVICECONNECTIONSTATUS']._serialized_end=444
|
_globals['_WIFICONNECTIONSTATUS']._serialized_end=524
|
||||||
_globals['_WIFICONNECTIONSTATUS']._serialized_start=446
|
_globals['_ETHERNETCONNECTIONSTATUS']._serialized_start=526
|
||||||
_globals['_WIFICONNECTIONSTATUS']._serialized_end=565
|
_globals['_ETHERNETCONNECTIONSTATUS']._serialized_end=614
|
||||||
_globals['_ETHERNETCONNECTIONSTATUS']._serialized_start=567
|
_globals['_NETWORKCONNECTIONSTATUS']._serialized_start=616
|
||||||
_globals['_ETHERNETCONNECTIONSTATUS']._serialized_end=655
|
_globals['_NETWORKCONNECTIONSTATUS']._serialized_end=739
|
||||||
_globals['_NETWORKCONNECTIONSTATUS']._serialized_start=657
|
_globals['_BLUETOOTHCONNECTIONSTATUS']._serialized_start=741
|
||||||
_globals['_NETWORKCONNECTIONSTATUS']._serialized_end=780
|
_globals['_BLUETOOTHCONNECTIONSTATUS']._serialized_end=817
|
||||||
_globals['_BLUETOOTHCONNECTIONSTATUS']._serialized_start=782
|
_globals['_SERIALCONNECTIONSTATUS']._serialized_start=819
|
||||||
_globals['_BLUETOOTHCONNECTIONSTATUS']._serialized_end=858
|
_globals['_SERIALCONNECTIONSTATUS']._serialized_end=879
|
||||||
_globals['_SERIALCONNECTIONSTATUS']._serialized_start=860
|
|
||||||
_globals['_SERIALCONNECTIONSTATUS']._serialized_end=920
|
|
||||||
# @@protoc_insertion_point(module_scope)
|
# @@protoc_insertion_point(module_scope)
|
||||||
|
|||||||
61
meshtastic/protobuf/device_ui_pb2.py
generated
61
meshtastic/protobuf/device_ui_pb2.py
generated
@@ -11,57 +11,28 @@ from google.protobuf.internal import builder as _builder
|
|||||||
_sym_db = _symbol_database.Default()
|
_sym_db = _symbol_database.Default()
|
||||||
|
|
||||||
|
|
||||||
from meshtastic.protobuf import nanopb_pb2 as meshtastic_dot_protobuf_dot_nanopb__pb2
|
|
||||||
|
|
||||||
|
|
||||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n#meshtastic/protobuf/device_ui.proto\x12\x13meshtastic.protobuf\x1a meshtastic/protobuf/nanopb.proto\"\xa9\x06\n\x0e\x44\x65viceUIConfig\x12\x0f\n\x07version\x18\x01 \x01(\r\x12 \n\x11screen_brightness\x18\x02 \x01(\rB\x05\x92?\x02\x38\x08\x12\x1d\n\x0escreen_timeout\x18\x03 \x01(\rB\x05\x92?\x02\x38\x10\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\x1b\n\x0cring_tone_id\x18\n \x01(\rB\x05\x92?\x02\x38\x08\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\x1f\n\x10\x63\x61libration_data\x18\x0e \x01(\x0c\x42\x05\x92?\x02\x08\x10\x12*\n\x08map_data\x18\x0f \x01(\x0b\x32\x18.meshtastic.protobuf.Map\x12=\n\x0c\x63ompass_mode\x18\x10 \x01(\x0e\x32 .meshtastic.protobuf.CompassModeB\x05\x92?\x02\x38\x08\x12\x18\n\x10screen_rgb_color\x18\x11 \x01(\r\x12\x1b\n\x13is_clockface_analog\x18\x12 \x01(\x08\x12R\n\ngps_format\x18\x13 \x01(\x0e\x32\x37.meshtastic.protobuf.DeviceUIConfig.GpsCoordinateFormatB\x05\x92?\x02\x38\x08\"V\n\x13GpsCoordinateFormat\x12\x07\n\x03\x44\x45\x43\x10\x00\x12\x07\n\x03\x44MS\x10\x01\x12\x07\n\x03UTM\x10\x02\x12\x08\n\x04MGRS\x10\x03\x12\x07\n\x03OLC\x10\x04\x12\x08\n\x04OSGR\x10\x05\x12\x07\n\x03MLS\x10\x06\"\xbc\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\x18\n\thops_away\x18\x04 \x01(\x05\x42\x05\x92?\x02\x38\x08\x12\x17\n\x0fposition_switch\x18\x05 \x01(\x08\x12\x18\n\tnode_name\x18\x06 \x01(\tB\x05\x92?\x02\x08\x10\x12\x16\n\x07\x63hannel\x18\x07 \x01(\x05\x42\x05\x92?\x02\x38\x08\"\x85\x01\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\x18\n\tnode_name\x18\x05 \x01(\tB\x05\x92?\x02\x08\x10\"D\n\x08GeoPoint\x12\x13\n\x04zoom\x18\x01 \x01(\x05\x42\x05\x92?\x02\x38\x08\x12\x10\n\x08latitude\x18\x02 \x01(\x05\x12\x11\n\tlongitude\x18\x03 \x01(\x05\"\\\n\x03Map\x12+\n\x04home\x18\x01 \x01(\x0b\x32\x1d.meshtastic.protobuf.GeoPoint\x12\x14\n\x05style\x18\x02 \x01(\tB\x05\x92?\x02\x08\x14\x12\x12\n\nfollow_gps\x18\x03 \x01(\x08*>\n\x0b\x43ompassMode\x12\x0b\n\x07\x44YNAMIC\x10\x00\x12\x0e\n\nFIXED_RING\x10\x01\x12\x12\n\x0e\x46REEZE_HEADING\x10\x02*%\n\x05Theme\x12\x08\n\x04\x44\x41RK\x10\x00\x12\t\n\x05LIGHT\x10\x01\x12\x07\n\x03RED\x10\x02*\xc0\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\r\n\tBULGARIAN\x10\x11\x12\t\n\x05\x43ZECH\x10\x12\x12\n\n\x06\x44\x41NISH\x10\x13\x12\x16\n\x12SIMPLIFIED_CHINESE\x10\x1e\x12\x17\n\x13TRADITIONAL_CHINESE\x10\x1f\x42\x64\n\x14org.meshtastic.protoB\x0e\x44\x65viceUIProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
|
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*\x8b\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\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')
|
||||||
|
|
||||||
_globals = globals()
|
_globals = globals()
|
||||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.device_ui_pb2', _globals)
|
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.device_ui_pb2', _globals)
|
||||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||||
DESCRIPTOR._options = None
|
DESCRIPTOR._options = None
|
||||||
DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\016DeviceUIProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\016DeviceUIProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||||
_DEVICEUICONFIG.fields_by_name['screen_brightness']._options = None
|
_globals['_THEME']._serialized_start=1002
|
||||||
_DEVICEUICONFIG.fields_by_name['screen_brightness']._serialized_options = b'\222?\0028\010'
|
_globals['_THEME']._serialized_end=1039
|
||||||
_DEVICEUICONFIG.fields_by_name['screen_timeout']._options = None
|
_globals['_LANGUAGE']._serialized_start=1042
|
||||||
_DEVICEUICONFIG.fields_by_name['screen_timeout']._serialized_options = b'\222?\0028\020'
|
_globals['_LANGUAGE']._serialized_end=1309
|
||||||
_DEVICEUICONFIG.fields_by_name['ring_tone_id']._options = None
|
_globals['_DEVICEUICONFIG']._serialized_start=61
|
||||||
_DEVICEUICONFIG.fields_by_name['ring_tone_id']._serialized_options = b'\222?\0028\010'
|
_globals['_DEVICEUICONFIG']._serialized_end=552
|
||||||
_DEVICEUICONFIG.fields_by_name['calibration_data']._options = None
|
_globals['_NODEFILTER']._serialized_start=555
|
||||||
_DEVICEUICONFIG.fields_by_name['calibration_data']._serialized_options = b'\222?\002\010\020'
|
_globals['_NODEFILTER']._serialized_end=722
|
||||||
_DEVICEUICONFIG.fields_by_name['compass_mode']._options = None
|
_globals['_NODEHIGHLIGHT']._serialized_start=724
|
||||||
_DEVICEUICONFIG.fields_by_name['compass_mode']._serialized_options = b'\222?\0028\010'
|
_globals['_NODEHIGHLIGHT']._serialized_end=850
|
||||||
_DEVICEUICONFIG.fields_by_name['gps_format']._options = None
|
_globals['_GEOPOINT']._serialized_start=852
|
||||||
_DEVICEUICONFIG.fields_by_name['gps_format']._serialized_options = b'\222?\0028\010'
|
_globals['_GEOPOINT']._serialized_end=913
|
||||||
_NODEFILTER.fields_by_name['hops_away']._options = None
|
_globals['_MAP']._serialized_start=915
|
||||||
_NODEFILTER.fields_by_name['hops_away']._serialized_options = b'\222?\0028\010'
|
_globals['_MAP']._serialized_end=1000
|
||||||
_NODEFILTER.fields_by_name['node_name']._options = None
|
|
||||||
_NODEFILTER.fields_by_name['node_name']._serialized_options = b'\222?\002\010\020'
|
|
||||||
_NODEFILTER.fields_by_name['channel']._options = None
|
|
||||||
_NODEFILTER.fields_by_name['channel']._serialized_options = b'\222?\0028\010'
|
|
||||||
_NODEHIGHLIGHT.fields_by_name['node_name']._options = None
|
|
||||||
_NODEHIGHLIGHT.fields_by_name['node_name']._serialized_options = b'\222?\002\010\020'
|
|
||||||
_GEOPOINT.fields_by_name['zoom']._options = None
|
|
||||||
_GEOPOINT.fields_by_name['zoom']._serialized_options = b'\222?\0028\010'
|
|
||||||
_MAP.fields_by_name['style']._options = None
|
|
||||||
_MAP.fields_by_name['style']._serialized_options = b'\222?\002\010\024'
|
|
||||||
_globals['_COMPASSMODE']._serialized_start=1397
|
|
||||||
_globals['_COMPASSMODE']._serialized_end=1459
|
|
||||||
_globals['_THEME']._serialized_start=1461
|
|
||||||
_globals['_THEME']._serialized_end=1498
|
|
||||||
_globals['_LANGUAGE']._serialized_start=1501
|
|
||||||
_globals['_LANGUAGE']._serialized_end=1821
|
|
||||||
_globals['_DEVICEUICONFIG']._serialized_start=95
|
|
||||||
_globals['_DEVICEUICONFIG']._serialized_end=904
|
|
||||||
_globals['_DEVICEUICONFIG_GPSCOORDINATEFORMAT']._serialized_start=818
|
|
||||||
_globals['_DEVICEUICONFIG_GPSCOORDINATEFORMAT']._serialized_end=904
|
|
||||||
_globals['_NODEFILTER']._serialized_start=907
|
|
||||||
_globals['_NODEFILTER']._serialized_end=1095
|
|
||||||
_globals['_NODEHIGHLIGHT']._serialized_start=1098
|
|
||||||
_globals['_NODEHIGHLIGHT']._serialized_end=1231
|
|
||||||
_globals['_GEOPOINT']._serialized_start=1233
|
|
||||||
_globals['_GEOPOINT']._serialized_end=1301
|
|
||||||
_globals['_MAP']._serialized_start=1303
|
|
||||||
_globals['_MAP']._serialized_end=1395
|
|
||||||
# @@protoc_insertion_point(module_scope)
|
# @@protoc_insertion_point(module_scope)
|
||||||
|
|||||||
180
meshtastic/protobuf/device_ui_pb2.pyi
generated
180
meshtastic/protobuf/device_ui_pb2.pyi
generated
@@ -17,41 +17,6 @@ else:
|
|||||||
|
|
||||||
DESCRIPTOR: google.protobuf.descriptor.FileDescriptor
|
DESCRIPTOR: google.protobuf.descriptor.FileDescriptor
|
||||||
|
|
||||||
class _CompassMode:
|
|
||||||
ValueType = typing.NewType("ValueType", builtins.int)
|
|
||||||
V: typing_extensions.TypeAlias = ValueType
|
|
||||||
|
|
||||||
class _CompassModeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_CompassMode.ValueType], builtins.type):
|
|
||||||
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
|
|
||||||
DYNAMIC: _CompassMode.ValueType # 0
|
|
||||||
"""
|
|
||||||
Compass with dynamic ring and heading
|
|
||||||
"""
|
|
||||||
FIXED_RING: _CompassMode.ValueType # 1
|
|
||||||
"""
|
|
||||||
Compass with fixed ring and heading
|
|
||||||
"""
|
|
||||||
FREEZE_HEADING: _CompassMode.ValueType # 2
|
|
||||||
"""
|
|
||||||
Compass with heading and freeze option
|
|
||||||
"""
|
|
||||||
|
|
||||||
class CompassMode(_CompassMode, metaclass=_CompassModeEnumTypeWrapper): ...
|
|
||||||
|
|
||||||
DYNAMIC: CompassMode.ValueType # 0
|
|
||||||
"""
|
|
||||||
Compass with dynamic ring and heading
|
|
||||||
"""
|
|
||||||
FIXED_RING: CompassMode.ValueType # 1
|
|
||||||
"""
|
|
||||||
Compass with fixed ring and heading
|
|
||||||
"""
|
|
||||||
FREEZE_HEADING: CompassMode.ValueType # 2
|
|
||||||
"""
|
|
||||||
Compass with heading and freeze option
|
|
||||||
"""
|
|
||||||
global___CompassMode = CompassMode
|
|
||||||
|
|
||||||
class _Theme:
|
class _Theme:
|
||||||
ValueType = typing.NewType("ValueType", builtins.int)
|
ValueType = typing.NewType("ValueType", builtins.int)
|
||||||
V: typing_extensions.TypeAlias = ValueType
|
V: typing_extensions.TypeAlias = ValueType
|
||||||
@@ -157,22 +122,6 @@ class _LanguageEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumT
|
|||||||
"""
|
"""
|
||||||
Slovenian
|
Slovenian
|
||||||
"""
|
"""
|
||||||
UKRAINIAN: _Language.ValueType # 16
|
|
||||||
"""
|
|
||||||
Ukrainian
|
|
||||||
"""
|
|
||||||
BULGARIAN: _Language.ValueType # 17
|
|
||||||
"""
|
|
||||||
Bulgarian
|
|
||||||
"""
|
|
||||||
CZECH: _Language.ValueType # 18
|
|
||||||
"""
|
|
||||||
Czech
|
|
||||||
"""
|
|
||||||
DANISH: _Language.ValueType # 19
|
|
||||||
"""
|
|
||||||
Danish
|
|
||||||
"""
|
|
||||||
SIMPLIFIED_CHINESE: _Language.ValueType # 30
|
SIMPLIFIED_CHINESE: _Language.ValueType # 30
|
||||||
"""
|
"""
|
||||||
Simplified Chinese (experimental)
|
Simplified Chinese (experimental)
|
||||||
@@ -251,22 +200,6 @@ SLOVENIAN: Language.ValueType # 15
|
|||||||
"""
|
"""
|
||||||
Slovenian
|
Slovenian
|
||||||
"""
|
"""
|
||||||
UKRAINIAN: Language.ValueType # 16
|
|
||||||
"""
|
|
||||||
Ukrainian
|
|
||||||
"""
|
|
||||||
BULGARIAN: Language.ValueType # 17
|
|
||||||
"""
|
|
||||||
Bulgarian
|
|
||||||
"""
|
|
||||||
CZECH: Language.ValueType # 18
|
|
||||||
"""
|
|
||||||
Czech
|
|
||||||
"""
|
|
||||||
DANISH: Language.ValueType # 19
|
|
||||||
"""
|
|
||||||
Danish
|
|
||||||
"""
|
|
||||||
SIMPLIFIED_CHINESE: Language.ValueType # 30
|
SIMPLIFIED_CHINESE: Language.ValueType # 30
|
||||||
"""
|
"""
|
||||||
Simplified Chinese (experimental)
|
Simplified Chinese (experimental)
|
||||||
@@ -285,91 +218,6 @@ class DeviceUIConfig(google.protobuf.message.Message):
|
|||||||
|
|
||||||
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
||||||
|
|
||||||
class _GpsCoordinateFormat:
|
|
||||||
ValueType = typing.NewType("ValueType", builtins.int)
|
|
||||||
V: typing_extensions.TypeAlias = ValueType
|
|
||||||
|
|
||||||
class _GpsCoordinateFormatEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[DeviceUIConfig._GpsCoordinateFormat.ValueType], builtins.type):
|
|
||||||
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
|
|
||||||
DEC: DeviceUIConfig._GpsCoordinateFormat.ValueType # 0
|
|
||||||
"""
|
|
||||||
GPS coordinates are displayed in the normal decimal degrees format:
|
|
||||||
DD.DDDDDD DDD.DDDDDD
|
|
||||||
"""
|
|
||||||
DMS: DeviceUIConfig._GpsCoordinateFormat.ValueType # 1
|
|
||||||
"""
|
|
||||||
GPS coordinates are displayed in the degrees minutes seconds format:
|
|
||||||
DD°MM'SS"C DDD°MM'SS"C, where C is the compass point representing the locations quadrant
|
|
||||||
"""
|
|
||||||
UTM: DeviceUIConfig._GpsCoordinateFormat.ValueType # 2
|
|
||||||
"""
|
|
||||||
Universal Transverse Mercator format:
|
|
||||||
ZZB EEEEEE NNNNNNN, where Z is zone, B is band, E is easting, N is northing
|
|
||||||
"""
|
|
||||||
MGRS: DeviceUIConfig._GpsCoordinateFormat.ValueType # 3
|
|
||||||
"""
|
|
||||||
Military Grid Reference System format:
|
|
||||||
ZZB CD EEEEE NNNNN, where Z is zone, B is band, C is the east 100k square, D is the north 100k square,
|
|
||||||
E is easting, N is northing
|
|
||||||
"""
|
|
||||||
OLC: DeviceUIConfig._GpsCoordinateFormat.ValueType # 4
|
|
||||||
"""
|
|
||||||
Open Location Code (aka Plus Codes).
|
|
||||||
"""
|
|
||||||
OSGR: DeviceUIConfig._GpsCoordinateFormat.ValueType # 5
|
|
||||||
"""
|
|
||||||
Ordnance Survey Grid Reference (the National Grid System of the UK).
|
|
||||||
Format: AB EEEEE NNNNN, where A is the east 100k square, B is the north 100k square,
|
|
||||||
E is the easting, N is the northing
|
|
||||||
"""
|
|
||||||
MLS: DeviceUIConfig._GpsCoordinateFormat.ValueType # 6
|
|
||||||
"""
|
|
||||||
Maidenhead Locator System
|
|
||||||
Described here: https://en.wikipedia.org/wiki/Maidenhead_Locator_System
|
|
||||||
"""
|
|
||||||
|
|
||||||
class GpsCoordinateFormat(_GpsCoordinateFormat, metaclass=_GpsCoordinateFormatEnumTypeWrapper):
|
|
||||||
"""
|
|
||||||
How the GPS coordinates are displayed on the OLED screen.
|
|
||||||
"""
|
|
||||||
|
|
||||||
DEC: DeviceUIConfig.GpsCoordinateFormat.ValueType # 0
|
|
||||||
"""
|
|
||||||
GPS coordinates are displayed in the normal decimal degrees format:
|
|
||||||
DD.DDDDDD DDD.DDDDDD
|
|
||||||
"""
|
|
||||||
DMS: DeviceUIConfig.GpsCoordinateFormat.ValueType # 1
|
|
||||||
"""
|
|
||||||
GPS coordinates are displayed in the degrees minutes seconds format:
|
|
||||||
DD°MM'SS"C DDD°MM'SS"C, where C is the compass point representing the locations quadrant
|
|
||||||
"""
|
|
||||||
UTM: DeviceUIConfig.GpsCoordinateFormat.ValueType # 2
|
|
||||||
"""
|
|
||||||
Universal Transverse Mercator format:
|
|
||||||
ZZB EEEEEE NNNNNNN, where Z is zone, B is band, E is easting, N is northing
|
|
||||||
"""
|
|
||||||
MGRS: DeviceUIConfig.GpsCoordinateFormat.ValueType # 3
|
|
||||||
"""
|
|
||||||
Military Grid Reference System format:
|
|
||||||
ZZB CD EEEEE NNNNN, where Z is zone, B is band, C is the east 100k square, D is the north 100k square,
|
|
||||||
E is easting, N is northing
|
|
||||||
"""
|
|
||||||
OLC: DeviceUIConfig.GpsCoordinateFormat.ValueType # 4
|
|
||||||
"""
|
|
||||||
Open Location Code (aka Plus Codes).
|
|
||||||
"""
|
|
||||||
OSGR: DeviceUIConfig.GpsCoordinateFormat.ValueType # 5
|
|
||||||
"""
|
|
||||||
Ordnance Survey Grid Reference (the National Grid System of the UK).
|
|
||||||
Format: AB EEEEE NNNNN, where A is the east 100k square, B is the north 100k square,
|
|
||||||
E is the easting, N is the northing
|
|
||||||
"""
|
|
||||||
MLS: DeviceUIConfig.GpsCoordinateFormat.ValueType # 6
|
|
||||||
"""
|
|
||||||
Maidenhead Locator System
|
|
||||||
Described here: https://en.wikipedia.org/wiki/Maidenhead_Locator_System
|
|
||||||
"""
|
|
||||||
|
|
||||||
VERSION_FIELD_NUMBER: builtins.int
|
VERSION_FIELD_NUMBER: builtins.int
|
||||||
SCREEN_BRIGHTNESS_FIELD_NUMBER: builtins.int
|
SCREEN_BRIGHTNESS_FIELD_NUMBER: builtins.int
|
||||||
SCREEN_TIMEOUT_FIELD_NUMBER: builtins.int
|
SCREEN_TIMEOUT_FIELD_NUMBER: builtins.int
|
||||||
@@ -385,10 +233,6 @@ class DeviceUIConfig(google.protobuf.message.Message):
|
|||||||
NODE_HIGHLIGHT_FIELD_NUMBER: builtins.int
|
NODE_HIGHLIGHT_FIELD_NUMBER: builtins.int
|
||||||
CALIBRATION_DATA_FIELD_NUMBER: builtins.int
|
CALIBRATION_DATA_FIELD_NUMBER: builtins.int
|
||||||
MAP_DATA_FIELD_NUMBER: builtins.int
|
MAP_DATA_FIELD_NUMBER: builtins.int
|
||||||
COMPASS_MODE_FIELD_NUMBER: builtins.int
|
|
||||||
SCREEN_RGB_COLOR_FIELD_NUMBER: builtins.int
|
|
||||||
IS_CLOCKFACE_ANALOG_FIELD_NUMBER: builtins.int
|
|
||||||
GPS_FORMAT_FIELD_NUMBER: builtins.int
|
|
||||||
version: builtins.int
|
version: builtins.int
|
||||||
"""
|
"""
|
||||||
A version integer used to invalidate saved files when we make incompatible changes.
|
A version integer used to invalidate saved files when we make incompatible changes.
|
||||||
@@ -425,24 +269,6 @@ class DeviceUIConfig(google.protobuf.message.Message):
|
|||||||
"""
|
"""
|
||||||
8 integers for screen calibration data
|
8 integers for screen calibration data
|
||||||
"""
|
"""
|
||||||
compass_mode: global___CompassMode.ValueType
|
|
||||||
"""
|
|
||||||
Compass mode
|
|
||||||
"""
|
|
||||||
screen_rgb_color: builtins.int
|
|
||||||
"""
|
|
||||||
RGB color for BaseUI
|
|
||||||
0xRRGGBB format, e.g. 0xFF0000 for red
|
|
||||||
"""
|
|
||||||
is_clockface_analog: builtins.bool
|
|
||||||
"""
|
|
||||||
Clockface analog style
|
|
||||||
true for analog clockface, false for digital clockface
|
|
||||||
"""
|
|
||||||
gps_format: global___DeviceUIConfig.GpsCoordinateFormat.ValueType
|
|
||||||
"""
|
|
||||||
How the GPS coordinates are formatted on the OLED screen.
|
|
||||||
"""
|
|
||||||
@property
|
@property
|
||||||
def node_filter(self) -> global___NodeFilter:
|
def node_filter(self) -> global___NodeFilter:
|
||||||
"""
|
"""
|
||||||
@@ -479,13 +305,9 @@ class DeviceUIConfig(google.protobuf.message.Message):
|
|||||||
node_highlight: global___NodeHighlight | None = ...,
|
node_highlight: global___NodeHighlight | None = ...,
|
||||||
calibration_data: builtins.bytes = ...,
|
calibration_data: builtins.bytes = ...,
|
||||||
map_data: global___Map | None = ...,
|
map_data: global___Map | None = ...,
|
||||||
compass_mode: global___CompassMode.ValueType = ...,
|
|
||||||
screen_rgb_color: builtins.int = ...,
|
|
||||||
is_clockface_analog: builtins.bool = ...,
|
|
||||||
gps_format: global___DeviceUIConfig.GpsCoordinateFormat.ValueType = ...,
|
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
def HasField(self, field_name: typing.Literal["map_data", b"map_data", "node_filter", b"node_filter", "node_highlight", b"node_highlight"]) -> builtins.bool: ...
|
def HasField(self, field_name: typing.Literal["map_data", b"map_data", "node_filter", b"node_filter", "node_highlight", b"node_highlight"]) -> builtins.bool: ...
|
||||||
def ClearField(self, field_name: typing.Literal["alert_enabled", b"alert_enabled", "banner_enabled", b"banner_enabled", "calibration_data", b"calibration_data", "compass_mode", b"compass_mode", "gps_format", b"gps_format", "is_clockface_analog", b"is_clockface_analog", "language", b"language", "map_data", b"map_data", "node_filter", b"node_filter", "node_highlight", b"node_highlight", "pin_code", b"pin_code", "ring_tone_id", b"ring_tone_id", "screen_brightness", b"screen_brightness", "screen_lock", b"screen_lock", "screen_rgb_color", b"screen_rgb_color", "screen_timeout", b"screen_timeout", "settings_lock", b"settings_lock", "theme", b"theme", "version", b"version"]) -> None: ...
|
def ClearField(self, field_name: typing.Literal["alert_enabled", b"alert_enabled", "banner_enabled", b"banner_enabled", "calibration_data", b"calibration_data", "language", b"language", "map_data", b"map_data", "node_filter", b"node_filter", "node_highlight", b"node_highlight", "pin_code", b"pin_code", "ring_tone_id", b"ring_tone_id", "screen_brightness", b"screen_brightness", "screen_lock", b"screen_lock", "screen_timeout", b"screen_timeout", "settings_lock", b"settings_lock", "theme", b"theme", "version", b"version"]) -> None: ...
|
||||||
|
|
||||||
global___DeviceUIConfig = DeviceUIConfig
|
global___DeviceUIConfig = DeviceUIConfig
|
||||||
|
|
||||||
|
|||||||
58
meshtastic/protobuf/deviceonly_pb2.py
generated
58
meshtastic/protobuf/deviceonly_pb2.py
generated
@@ -12,59 +12,41 @@ _sym_db = _symbol_database.Default()
|
|||||||
|
|
||||||
|
|
||||||
from meshtastic.protobuf import channel_pb2 as meshtastic_dot_protobuf_dot_channel__pb2
|
from meshtastic.protobuf import channel_pb2 as meshtastic_dot_protobuf_dot_channel__pb2
|
||||||
from meshtastic.protobuf import config_pb2 as meshtastic_dot_protobuf_dot_config__pb2
|
|
||||||
from meshtastic.protobuf import localonly_pb2 as meshtastic_dot_protobuf_dot_localonly__pb2
|
|
||||||
from meshtastic.protobuf import mesh_pb2 as meshtastic_dot_protobuf_dot_mesh__pb2
|
from meshtastic.protobuf import mesh_pb2 as meshtastic_dot_protobuf_dot_mesh__pb2
|
||||||
from meshtastic.protobuf import telemetry_pb2 as meshtastic_dot_protobuf_dot_telemetry__pb2
|
from meshtastic.protobuf import telemetry_pb2 as meshtastic_dot_protobuf_dot_telemetry__pb2
|
||||||
from meshtastic.protobuf import nanopb_pb2 as meshtastic_dot_protobuf_dot_nanopb__pb2
|
from meshtastic.protobuf import config_pb2 as meshtastic_dot_protobuf_dot_config__pb2
|
||||||
|
from meshtastic.protobuf import localonly_pb2 as meshtastic_dot_protobuf_dot_localonly__pb2
|
||||||
|
import nanopb_pb2 as nanopb__pb2
|
||||||
|
|
||||||
|
|
||||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n$meshtastic/protobuf/deviceonly.proto\x12\x13meshtastic.protobuf\x1a!meshtastic/protobuf/channel.proto\x1a meshtastic/protobuf/config.proto\x1a#meshtastic/protobuf/localonly.proto\x1a\x1emeshtastic/protobuf/mesh.proto\x1a#meshtastic/protobuf/telemetry.proto\x1a meshtastic/protobuf/nanopb.proto\"\x99\x01\n\x0cPositionLite\x12\x12\n\nlatitude_i\x18\x01 \x01(\x0f\x12\x13\n\x0blongitude_i\x18\x02 \x01(\x0f\x12\x10\n\x08\x61ltitude\x18\x03 \x01(\x05\x12\x0c\n\x04time\x18\x04 \x01(\x07\x12@\n\x0flocation_source\x18\x05 \x01(\x0e\x32\'.meshtastic.protobuf.Position.LocSource\"\xb0\x02\n\x08UserLite\x12\x1a\n\x07macaddr\x18\x01 \x01(\x0c\x42\t\x18\x01\x92?\x04\x08\x06x\x01\x12\x18\n\tlong_name\x18\x02 \x01(\tB\x05\x92?\x02\x08(\x12\x19\n\nshort_name\x18\x03 \x01(\tB\x05\x92?\x02\x08\x05\x12\x34\n\x08hw_model\x18\x04 \x01(\x0e\x32\".meshtastic.protobuf.HardwareModel\x12\x13\n\x0bis_licensed\x18\x05 \x01(\x08\x12;\n\x04role\x18\x06 \x01(\x0e\x32-.meshtastic.protobuf.Config.DeviceConfig.Role\x12\x19\n\npublic_key\x18\x07 \x01(\x0c\x42\x05\x92?\x02\x08 \x12\x1c\n\x0fis_unmessagable\x18\t \x01(\x08H\x00\x88\x01\x01\x42\x12\n\x10_is_unmessagable\"\x85\x03\n\x0cNodeInfoLite\x12\x0b\n\x03num\x18\x01 \x01(\r\x12+\n\x04user\x18\x02 \x01(\x0b\x32\x1d.meshtastic.protobuf.UserLite\x12\x33\n\x08position\x18\x03 \x01(\x0b\x32!.meshtastic.protobuf.PositionLite\x12\x0b\n\x03snr\x18\x04 \x01(\x02\x12\x12\n\nlast_heard\x18\x05 \x01(\x07\x12:\n\x0e\x64\x65vice_metrics\x18\x06 \x01(\x0b\x32\".meshtastic.protobuf.DeviceMetrics\x12\x16\n\x07\x63hannel\x18\x07 \x01(\rB\x05\x92?\x02\x38\x08\x12\x10\n\x08via_mqtt\x18\x08 \x01(\x08\x12\x1d\n\thops_away\x18\t \x01(\rB\x05\x92?\x02\x38\x08H\x00\x88\x01\x01\x12\x13\n\x0bis_favorite\x18\n \x01(\x08\x12\x12\n\nis_ignored\x18\x0b \x01(\x08\x12\x17\n\x08next_hop\x18\x0c \x01(\rB\x05\x92?\x02\x38\x08\x12\x10\n\x08\x62itfield\x18\r \x01(\rB\x0c\n\n_hops_away\"\xaf\x03\n\x0b\x44\x65viceState\x12\x30\n\x07my_node\x18\x02 \x01(\x0b\x32\x1f.meshtastic.protobuf.MyNodeInfo\x12(\n\x05owner\x18\x03 \x01(\x0b\x32\x19.meshtastic.protobuf.User\x12=\n\rreceive_queue\x18\x05 \x03(\x0b\x32\x1f.meshtastic.protobuf.MeshPacketB\x05\x92?\x02\x10\x01\x12\x0f\n\x07version\x18\x08 \x01(\r\x12\x38\n\x0frx_text_message\x18\x07 \x01(\x0b\x32\x1f.meshtastic.protobuf.MeshPacket\x12\x13\n\x07no_save\x18\t \x01(\x08\x42\x02\x18\x01\x12\x19\n\rdid_gps_reset\x18\x0b \x01(\x08\x42\x02\x18\x01\x12\x34\n\x0brx_waypoint\x18\x0c \x01(\x0b\x32\x1f.meshtastic.protobuf.MeshPacket\x12T\n\x19node_remote_hardware_pins\x18\r \x03(\x0b\x32*.meshtastic.protobuf.NodeRemoteHardwarePinB\x05\x92?\x02\x10\x0c\"}\n\x0cNodeDatabase\x12\x0f\n\x07version\x18\x01 \x01(\r\x12\\\n\x05nodes\x18\x02 \x03(\x0b\x32!.meshtastic.protobuf.NodeInfoLiteB*\x92?\'\x92\x01$std::vector<meshtastic_NodeInfoLite>\"U\n\x0b\x43hannelFile\x12\x35\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x1c.meshtastic.protobuf.ChannelB\x05\x92?\x02\x10\x08\x12\x0f\n\x07version\x18\x02 \x01(\r\"\x86\x02\n\x11\x42\x61\x63kupPreferences\x12\x0f\n\x07version\x18\x01 \x01(\r\x12\x11\n\ttimestamp\x18\x02 \x01(\x07\x12\x30\n\x06\x63onfig\x18\x03 \x01(\x0b\x32 .meshtastic.protobuf.LocalConfig\x12=\n\rmodule_config\x18\x04 \x01(\x0b\x32&.meshtastic.protobuf.LocalModuleConfig\x12\x32\n\x08\x63hannels\x18\x05 \x01(\x0b\x32 .meshtastic.protobuf.ChannelFile\x12(\n\x05owner\x18\x06 \x01(\x0b\x32\x19.meshtastic.protobuf.UserBn\n\x14org.meshtastic.protoB\nDeviceOnlyZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x92?\x0b\xc2\x01\x08<vector>b\x06proto3')
|
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n$meshtastic/protobuf/deviceonly.proto\x12\x13meshtastic.protobuf\x1a!meshtastic/protobuf/channel.proto\x1a\x1emeshtastic/protobuf/mesh.proto\x1a#meshtastic/protobuf/telemetry.proto\x1a meshtastic/protobuf/config.proto\x1a#meshtastic/protobuf/localonly.proto\x1a\x0cnanopb.proto\"\x99\x01\n\x0cPositionLite\x12\x12\n\nlatitude_i\x18\x01 \x01(\x0f\x12\x13\n\x0blongitude_i\x18\x02 \x01(\x0f\x12\x10\n\x08\x61ltitude\x18\x03 \x01(\x05\x12\x0c\n\x04time\x18\x04 \x01(\x07\x12@\n\x0flocation_source\x18\x05 \x01(\x0e\x32\'.meshtastic.protobuf.Position.LocSource\"\xe2\x01\n\x08UserLite\x12\x13\n\x07macaddr\x18\x01 \x01(\x0c\x42\x02\x18\x01\x12\x11\n\tlong_name\x18\x02 \x01(\t\x12\x12\n\nshort_name\x18\x03 \x01(\t\x12\x34\n\x08hw_model\x18\x04 \x01(\x0e\x32\".meshtastic.protobuf.HardwareModel\x12\x13\n\x0bis_licensed\x18\x05 \x01(\x08\x12;\n\x04role\x18\x06 \x01(\x0e\x32-.meshtastic.protobuf.Config.DeviceConfig.Role\x12\x12\n\npublic_key\x18\x07 \x01(\x0c\"\xde\x02\n\x0cNodeInfoLite\x12\x0b\n\x03num\x18\x01 \x01(\r\x12+\n\x04user\x18\x02 \x01(\x0b\x32\x1d.meshtastic.protobuf.UserLite\x12\x33\n\x08position\x18\x03 \x01(\x0b\x32!.meshtastic.protobuf.PositionLite\x12\x0b\n\x03snr\x18\x04 \x01(\x02\x12\x12\n\nlast_heard\x18\x05 \x01(\x07\x12:\n\x0e\x64\x65vice_metrics\x18\x06 \x01(\x0b\x32\".meshtastic.protobuf.DeviceMetrics\x12\x0f\n\x07\x63hannel\x18\x07 \x01(\r\x12\x10\n\x08via_mqtt\x18\x08 \x01(\x08\x12\x16\n\thops_away\x18\t \x01(\rH\x00\x88\x01\x01\x12\x13\n\x0bis_favorite\x18\n \x01(\x08\x12\x12\n\nis_ignored\x18\x0b \x01(\x08\x12\x10\n\x08next_hop\x18\x0c \x01(\rB\x0c\n\n_hops_away\"\xa1\x03\n\x0b\x44\x65viceState\x12\x30\n\x07my_node\x18\x02 \x01(\x0b\x32\x1f.meshtastic.protobuf.MyNodeInfo\x12(\n\x05owner\x18\x03 \x01(\x0b\x32\x19.meshtastic.protobuf.User\x12\x36\n\rreceive_queue\x18\x05 \x03(\x0b\x32\x1f.meshtastic.protobuf.MeshPacket\x12\x0f\n\x07version\x18\x08 \x01(\r\x12\x38\n\x0frx_text_message\x18\x07 \x01(\x0b\x32\x1f.meshtastic.protobuf.MeshPacket\x12\x13\n\x07no_save\x18\t \x01(\x08\x42\x02\x18\x01\x12\x19\n\rdid_gps_reset\x18\x0b \x01(\x08\x42\x02\x18\x01\x12\x34\n\x0brx_waypoint\x18\x0c \x01(\x0b\x32\x1f.meshtastic.protobuf.MeshPacket\x12M\n\x19node_remote_hardware_pins\x18\r \x03(\x0b\x32*.meshtastic.protobuf.NodeRemoteHardwarePin\"}\n\x0cNodeDatabase\x12\x0f\n\x07version\x18\x01 \x01(\r\x12\\\n\x05nodes\x18\x02 \x03(\x0b\x32!.meshtastic.protobuf.NodeInfoLiteB*\x92?\'\x92\x01$std::vector<meshtastic_NodeInfoLite>\"N\n\x0b\x43hannelFile\x12.\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x1c.meshtastic.protobuf.Channel\x12\x0f\n\x07version\x18\x02 \x01(\r\"\x86\x02\n\x11\x42\x61\x63kupPreferences\x12\x0f\n\x07version\x18\x01 \x01(\r\x12\x11\n\ttimestamp\x18\x02 \x01(\x07\x12\x30\n\x06\x63onfig\x18\x03 \x01(\x0b\x32 .meshtastic.protobuf.LocalConfig\x12=\n\rmodule_config\x18\x04 \x01(\x0b\x32&.meshtastic.protobuf.LocalModuleConfig\x12\x32\n\x08\x63hannels\x18\x05 \x01(\x0b\x32 .meshtastic.protobuf.ChannelFile\x12(\n\x05owner\x18\x06 \x01(\x0b\x32\x19.meshtastic.protobuf.UserBm\n\x13\x63om.geeksville.meshB\nDeviceOnlyZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x92?\x0b\xc2\x01\x08<vector>b\x06proto3')
|
||||||
|
|
||||||
_globals = globals()
|
_globals = globals()
|
||||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.deviceonly_pb2', _globals)
|
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.deviceonly_pb2', _globals)
|
||||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||||
DESCRIPTOR._options = None
|
DESCRIPTOR._options = None
|
||||||
DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\nDeviceOnlyZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000\222?\013\302\001\010<vector>'
|
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\nDeviceOnlyZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000\222?\013\302\001\010<vector>'
|
||||||
_USERLITE.fields_by_name['macaddr']._options = None
|
_USERLITE.fields_by_name['macaddr']._options = None
|
||||||
_USERLITE.fields_by_name['macaddr']._serialized_options = b'\030\001\222?\004\010\006x\001'
|
_USERLITE.fields_by_name['macaddr']._serialized_options = b'\030\001'
|
||||||
_USERLITE.fields_by_name['long_name']._options = None
|
|
||||||
_USERLITE.fields_by_name['long_name']._serialized_options = b'\222?\002\010('
|
|
||||||
_USERLITE.fields_by_name['short_name']._options = None
|
|
||||||
_USERLITE.fields_by_name['short_name']._serialized_options = b'\222?\002\010\005'
|
|
||||||
_USERLITE.fields_by_name['public_key']._options = None
|
|
||||||
_USERLITE.fields_by_name['public_key']._serialized_options = b'\222?\002\010 '
|
|
||||||
_NODEINFOLITE.fields_by_name['channel']._options = None
|
|
||||||
_NODEINFOLITE.fields_by_name['channel']._serialized_options = b'\222?\0028\010'
|
|
||||||
_NODEINFOLITE.fields_by_name['hops_away']._options = None
|
|
||||||
_NODEINFOLITE.fields_by_name['hops_away']._serialized_options = b'\222?\0028\010'
|
|
||||||
_NODEINFOLITE.fields_by_name['next_hop']._options = None
|
|
||||||
_NODEINFOLITE.fields_by_name['next_hop']._serialized_options = b'\222?\0028\010'
|
|
||||||
_DEVICESTATE.fields_by_name['receive_queue']._options = None
|
|
||||||
_DEVICESTATE.fields_by_name['receive_queue']._serialized_options = b'\222?\002\020\001'
|
|
||||||
_DEVICESTATE.fields_by_name['no_save']._options = None
|
_DEVICESTATE.fields_by_name['no_save']._options = None
|
||||||
_DEVICESTATE.fields_by_name['no_save']._serialized_options = b'\030\001'
|
_DEVICESTATE.fields_by_name['no_save']._serialized_options = b'\030\001'
|
||||||
_DEVICESTATE.fields_by_name['did_gps_reset']._options = None
|
_DEVICESTATE.fields_by_name['did_gps_reset']._options = None
|
||||||
_DEVICESTATE.fields_by_name['did_gps_reset']._serialized_options = b'\030\001'
|
_DEVICESTATE.fields_by_name['did_gps_reset']._serialized_options = b'\030\001'
|
||||||
_DEVICESTATE.fields_by_name['node_remote_hardware_pins']._options = None
|
|
||||||
_DEVICESTATE.fields_by_name['node_remote_hardware_pins']._serialized_options = b'\222?\002\020\014'
|
|
||||||
_NODEDATABASE.fields_by_name['nodes']._options = None
|
_NODEDATABASE.fields_by_name['nodes']._options = None
|
||||||
_NODEDATABASE.fields_by_name['nodes']._serialized_options = b'\222?\'\222\001$std::vector<meshtastic_NodeInfoLite>'
|
_NODEDATABASE.fields_by_name['nodes']._serialized_options = b'\222?\'\222\001$std::vector<meshtastic_NodeInfoLite>'
|
||||||
_CHANNELFILE.fields_by_name['channels']._options = None
|
_globals['_POSITIONLITE']._serialized_start=251
|
||||||
_CHANNELFILE.fields_by_name['channels']._serialized_options = b'\222?\002\020\010'
|
_globals['_POSITIONLITE']._serialized_end=404
|
||||||
_globals['_POSITIONLITE']._serialized_start=271
|
_globals['_USERLITE']._serialized_start=407
|
||||||
_globals['_POSITIONLITE']._serialized_end=424
|
_globals['_USERLITE']._serialized_end=633
|
||||||
_globals['_USERLITE']._serialized_start=427
|
_globals['_NODEINFOLITE']._serialized_start=636
|
||||||
_globals['_USERLITE']._serialized_end=731
|
_globals['_NODEINFOLITE']._serialized_end=986
|
||||||
_globals['_NODEINFOLITE']._serialized_start=734
|
_globals['_DEVICESTATE']._serialized_start=989
|
||||||
_globals['_NODEINFOLITE']._serialized_end=1123
|
_globals['_DEVICESTATE']._serialized_end=1406
|
||||||
_globals['_DEVICESTATE']._serialized_start=1126
|
_globals['_NODEDATABASE']._serialized_start=1408
|
||||||
_globals['_DEVICESTATE']._serialized_end=1557
|
_globals['_NODEDATABASE']._serialized_end=1533
|
||||||
_globals['_NODEDATABASE']._serialized_start=1559
|
_globals['_CHANNELFILE']._serialized_start=1535
|
||||||
_globals['_NODEDATABASE']._serialized_end=1684
|
_globals['_CHANNELFILE']._serialized_end=1613
|
||||||
_globals['_CHANNELFILE']._serialized_start=1686
|
_globals['_BACKUPPREFERENCES']._serialized_start=1616
|
||||||
_globals['_CHANNELFILE']._serialized_end=1771
|
_globals['_BACKUPPREFERENCES']._serialized_end=1878
|
||||||
_globals['_BACKUPPREFERENCES']._serialized_start=1774
|
|
||||||
_globals['_BACKUPPREFERENCES']._serialized_end=2036
|
|
||||||
# @@protoc_insertion_point(module_scope)
|
# @@protoc_insertion_point(module_scope)
|
||||||
|
|||||||
20
meshtastic/protobuf/deviceonly_pb2.pyi
generated
20
meshtastic/protobuf/deviceonly_pb2.pyi
generated
@@ -79,7 +79,6 @@ class UserLite(google.protobuf.message.Message):
|
|||||||
IS_LICENSED_FIELD_NUMBER: builtins.int
|
IS_LICENSED_FIELD_NUMBER: builtins.int
|
||||||
ROLE_FIELD_NUMBER: builtins.int
|
ROLE_FIELD_NUMBER: builtins.int
|
||||||
PUBLIC_KEY_FIELD_NUMBER: builtins.int
|
PUBLIC_KEY_FIELD_NUMBER: builtins.int
|
||||||
IS_UNMESSAGABLE_FIELD_NUMBER: builtins.int
|
|
||||||
macaddr: builtins.bytes
|
macaddr: builtins.bytes
|
||||||
"""
|
"""
|
||||||
This is the addr of the radio.
|
This is the addr of the radio.
|
||||||
@@ -115,10 +114,6 @@ class UserLite(google.protobuf.message.Message):
|
|||||||
The public key of the user's device.
|
The public key of the user's device.
|
||||||
This is sent out to other nodes on the mesh to allow them to compute a shared secret key.
|
This is sent out to other nodes on the mesh to allow them to compute a shared secret key.
|
||||||
"""
|
"""
|
||||||
is_unmessagable: builtins.bool
|
|
||||||
"""
|
|
||||||
Whether or not the node can be messaged
|
|
||||||
"""
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
@@ -129,11 +124,8 @@ class UserLite(google.protobuf.message.Message):
|
|||||||
is_licensed: builtins.bool = ...,
|
is_licensed: builtins.bool = ...,
|
||||||
role: meshtastic.protobuf.config_pb2.Config.DeviceConfig.Role.ValueType = ...,
|
role: meshtastic.protobuf.config_pb2.Config.DeviceConfig.Role.ValueType = ...,
|
||||||
public_key: builtins.bytes = ...,
|
public_key: builtins.bytes = ...,
|
||||||
is_unmessagable: builtins.bool | None = ...,
|
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
def HasField(self, field_name: typing.Literal["_is_unmessagable", b"_is_unmessagable", "is_unmessagable", b"is_unmessagable"]) -> builtins.bool: ...
|
def ClearField(self, field_name: typing.Literal["hw_model", b"hw_model", "is_licensed", b"is_licensed", "long_name", b"long_name", "macaddr", b"macaddr", "public_key", b"public_key", "role", b"role", "short_name", b"short_name"]) -> None: ...
|
||||||
def ClearField(self, field_name: typing.Literal["_is_unmessagable", b"_is_unmessagable", "hw_model", b"hw_model", "is_licensed", b"is_licensed", "is_unmessagable", b"is_unmessagable", "long_name", b"long_name", "macaddr", b"macaddr", "public_key", b"public_key", "role", b"role", "short_name", b"short_name"]) -> None: ...
|
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_is_unmessagable", b"_is_unmessagable"]) -> typing.Literal["is_unmessagable"] | None: ...
|
|
||||||
|
|
||||||
global___UserLite = UserLite
|
global___UserLite = UserLite
|
||||||
|
|
||||||
@@ -153,7 +145,6 @@ class NodeInfoLite(google.protobuf.message.Message):
|
|||||||
IS_FAVORITE_FIELD_NUMBER: builtins.int
|
IS_FAVORITE_FIELD_NUMBER: builtins.int
|
||||||
IS_IGNORED_FIELD_NUMBER: builtins.int
|
IS_IGNORED_FIELD_NUMBER: builtins.int
|
||||||
NEXT_HOP_FIELD_NUMBER: builtins.int
|
NEXT_HOP_FIELD_NUMBER: builtins.int
|
||||||
BITFIELD_FIELD_NUMBER: builtins.int
|
|
||||||
num: builtins.int
|
num: builtins.int
|
||||||
"""
|
"""
|
||||||
The node number
|
The node number
|
||||||
@@ -193,12 +184,6 @@ class NodeInfoLite(google.protobuf.message.Message):
|
|||||||
"""
|
"""
|
||||||
Last byte of the node number of the node that should be used as the next hop to reach this node.
|
Last byte of the node number of the node that should be used as the next hop to reach this node.
|
||||||
"""
|
"""
|
||||||
bitfield: builtins.int
|
|
||||||
"""
|
|
||||||
Bitfield for storing booleans.
|
|
||||||
LSB 0 is_key_manually_verified
|
|
||||||
LSB 1 is_muted
|
|
||||||
"""
|
|
||||||
@property
|
@property
|
||||||
def user(self) -> global___UserLite:
|
def user(self) -> global___UserLite:
|
||||||
"""
|
"""
|
||||||
@@ -233,10 +218,9 @@ class NodeInfoLite(google.protobuf.message.Message):
|
|||||||
is_favorite: builtins.bool = ...,
|
is_favorite: builtins.bool = ...,
|
||||||
is_ignored: builtins.bool = ...,
|
is_ignored: builtins.bool = ...,
|
||||||
next_hop: builtins.int = ...,
|
next_hop: builtins.int = ...,
|
||||||
bitfield: builtins.int = ...,
|
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
def HasField(self, field_name: typing.Literal["_hops_away", b"_hops_away", "device_metrics", b"device_metrics", "hops_away", b"hops_away", "position", b"position", "user", b"user"]) -> builtins.bool: ...
|
def HasField(self, field_name: typing.Literal["_hops_away", b"_hops_away", "device_metrics", b"device_metrics", "hops_away", b"hops_away", "position", b"position", "user", b"user"]) -> builtins.bool: ...
|
||||||
def ClearField(self, field_name: typing.Literal["_hops_away", b"_hops_away", "bitfield", b"bitfield", "channel", b"channel", "device_metrics", b"device_metrics", "hops_away", b"hops_away", "is_favorite", b"is_favorite", "is_ignored", b"is_ignored", "last_heard", b"last_heard", "next_hop", b"next_hop", "num", b"num", "position", b"position", "snr", b"snr", "user", b"user", "via_mqtt", b"via_mqtt"]) -> None: ...
|
def ClearField(self, field_name: typing.Literal["_hops_away", b"_hops_away", "channel", b"channel", "device_metrics", b"device_metrics", "hops_away", b"hops_away", "is_favorite", b"is_favorite", "is_ignored", b"is_ignored", "last_heard", b"last_heard", "next_hop", b"next_hop", "num", b"num", "position", b"position", "snr", b"snr", "user", b"user", "via_mqtt", b"via_mqtt"]) -> None: ...
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_hops_away", b"_hops_away"]) -> typing.Literal["hops_away"] | None: ...
|
def WhichOneof(self, oneof_group: typing.Literal["_hops_away", b"_hops_away"]) -> typing.Literal["hops_away"] | None: ...
|
||||||
|
|
||||||
global___NodeInfoLite = NodeInfoLite
|
global___NodeInfoLite = NodeInfoLite
|
||||||
|
|||||||
33
meshtastic/protobuf/interdevice_pb2.py
generated
33
meshtastic/protobuf/interdevice_pb2.py
generated
@@ -1,33 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
||||||
# source: meshtastic/protobuf/interdevice.proto
|
|
||||||
"""Generated protocol buffer code."""
|
|
||||||
from google.protobuf import descriptor as _descriptor
|
|
||||||
from google.protobuf import descriptor_pool as _descriptor_pool
|
|
||||||
from google.protobuf import symbol_database as _symbol_database
|
|
||||||
from google.protobuf.internal import builder as _builder
|
|
||||||
# @@protoc_insertion_point(imports)
|
|
||||||
|
|
||||||
_sym_db = _symbol_database.Default()
|
|
||||||
|
|
||||||
|
|
||||||
from meshtastic.protobuf import nanopb_pb2 as meshtastic_dot_protobuf_dot_nanopb__pb2
|
|
||||||
|
|
||||||
|
|
||||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n%meshtastic/protobuf/interdevice.proto\x12\x13meshtastic.protobuf\x1a meshtastic/protobuf/nanopb.proto\"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\"g\n\x12InterdeviceMessage\x12\x16\n\x04nmea\x18\x01 \x01(\tB\x06\x92?\x03\x08\x80\x08H\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\x42g\n\x14org.meshtastic.protoB\x11InterdeviceProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
|
|
||||||
|
|
||||||
_globals = globals()
|
|
||||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
|
||||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.interdevice_pb2', _globals)
|
|
||||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
|
||||||
DESCRIPTOR._options = None
|
|
||||||
DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\021InterdeviceProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
|
||||||
_INTERDEVICEMESSAGE.fields_by_name['nmea']._options = None
|
|
||||||
_INTERDEVICEMESSAGE.fields_by_name['nmea']._serialized_options = b'\222?\003\010\200\010'
|
|
||||||
_globals['_MESSAGETYPE']._serialized_start=319
|
|
||||||
_globals['_MESSAGETYPE']._serialized_end=532
|
|
||||||
_globals['_SENSORDATA']._serialized_start=96
|
|
||||||
_globals['_SENSORDATA']._serialized_end=211
|
|
||||||
_globals['_INTERDEVICEMESSAGE']._serialized_start=213
|
|
||||||
_globals['_INTERDEVICEMESSAGE']._serialized_end=316
|
|
||||||
# @@protoc_insertion_point(module_scope)
|
|
||||||
105
meshtastic/protobuf/interdevice_pb2.pyi
generated
105
meshtastic/protobuf/interdevice_pb2.pyi
generated
@@ -1,105 +0,0 @@
|
|||||||
"""
|
|
||||||
@generated by mypy-protobuf. Do not edit manually!
|
|
||||||
isort:skip_file
|
|
||||||
"""
|
|
||||||
|
|
||||||
import builtins
|
|
||||||
import google.protobuf.descriptor
|
|
||||||
import google.protobuf.internal.enum_type_wrapper
|
|
||||||
import google.protobuf.message
|
|
||||||
import sys
|
|
||||||
import typing
|
|
||||||
|
|
||||||
if sys.version_info >= (3, 10):
|
|
||||||
import typing as typing_extensions
|
|
||||||
else:
|
|
||||||
import typing_extensions
|
|
||||||
|
|
||||||
DESCRIPTOR: google.protobuf.descriptor.FileDescriptor
|
|
||||||
|
|
||||||
class _MessageType:
|
|
||||||
ValueType = typing.NewType("ValueType", builtins.int)
|
|
||||||
V: typing_extensions.TypeAlias = ValueType
|
|
||||||
|
|
||||||
class _MessageTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_MessageType.ValueType], builtins.type):
|
|
||||||
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
|
|
||||||
ACK: _MessageType.ValueType # 0
|
|
||||||
COLLECT_INTERVAL: _MessageType.ValueType # 160
|
|
||||||
"""in ms"""
|
|
||||||
BEEP_ON: _MessageType.ValueType # 161
|
|
||||||
"""duration ms"""
|
|
||||||
BEEP_OFF: _MessageType.ValueType # 162
|
|
||||||
"""cancel prematurely"""
|
|
||||||
SHUTDOWN: _MessageType.ValueType # 163
|
|
||||||
POWER_ON: _MessageType.ValueType # 164
|
|
||||||
SCD41_TEMP: _MessageType.ValueType # 176
|
|
||||||
SCD41_HUMIDITY: _MessageType.ValueType # 177
|
|
||||||
SCD41_CO2: _MessageType.ValueType # 178
|
|
||||||
AHT20_TEMP: _MessageType.ValueType # 179
|
|
||||||
AHT20_HUMIDITY: _MessageType.ValueType # 180
|
|
||||||
TVOC_INDEX: _MessageType.ValueType # 181
|
|
||||||
|
|
||||||
class MessageType(_MessageType, metaclass=_MessageTypeEnumTypeWrapper):
|
|
||||||
"""encapsulate up to 1k of NMEA string data"""
|
|
||||||
|
|
||||||
ACK: MessageType.ValueType # 0
|
|
||||||
COLLECT_INTERVAL: MessageType.ValueType # 160
|
|
||||||
"""in ms"""
|
|
||||||
BEEP_ON: MessageType.ValueType # 161
|
|
||||||
"""duration ms"""
|
|
||||||
BEEP_OFF: MessageType.ValueType # 162
|
|
||||||
"""cancel prematurely"""
|
|
||||||
SHUTDOWN: MessageType.ValueType # 163
|
|
||||||
POWER_ON: MessageType.ValueType # 164
|
|
||||||
SCD41_TEMP: MessageType.ValueType # 176
|
|
||||||
SCD41_HUMIDITY: MessageType.ValueType # 177
|
|
||||||
SCD41_CO2: MessageType.ValueType # 178
|
|
||||||
AHT20_TEMP: MessageType.ValueType # 179
|
|
||||||
AHT20_HUMIDITY: MessageType.ValueType # 180
|
|
||||||
TVOC_INDEX: MessageType.ValueType # 181
|
|
||||||
global___MessageType = MessageType
|
|
||||||
|
|
||||||
@typing.final
|
|
||||||
class SensorData(google.protobuf.message.Message):
|
|
||||||
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
|
||||||
|
|
||||||
TYPE_FIELD_NUMBER: builtins.int
|
|
||||||
FLOAT_VALUE_FIELD_NUMBER: builtins.int
|
|
||||||
UINT32_VALUE_FIELD_NUMBER: builtins.int
|
|
||||||
type: global___MessageType.ValueType
|
|
||||||
"""The message type"""
|
|
||||||
float_value: builtins.float
|
|
||||||
uint32_value: builtins.int
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
*,
|
|
||||||
type: global___MessageType.ValueType = ...,
|
|
||||||
float_value: builtins.float = ...,
|
|
||||||
uint32_value: builtins.int = ...,
|
|
||||||
) -> None: ...
|
|
||||||
def HasField(self, field_name: typing.Literal["data", b"data", "float_value", b"float_value", "uint32_value", b"uint32_value"]) -> builtins.bool: ...
|
|
||||||
def ClearField(self, field_name: typing.Literal["data", b"data", "float_value", b"float_value", "type", b"type", "uint32_value", b"uint32_value"]) -> None: ...
|
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["data", b"data"]) -> typing.Literal["float_value", "uint32_value"] | None: ...
|
|
||||||
|
|
||||||
global___SensorData = SensorData
|
|
||||||
|
|
||||||
@typing.final
|
|
||||||
class InterdeviceMessage(google.protobuf.message.Message):
|
|
||||||
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
|
||||||
|
|
||||||
NMEA_FIELD_NUMBER: builtins.int
|
|
||||||
SENSOR_FIELD_NUMBER: builtins.int
|
|
||||||
nmea: builtins.str
|
|
||||||
@property
|
|
||||||
def sensor(self) -> global___SensorData: ...
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
*,
|
|
||||||
nmea: builtins.str = ...,
|
|
||||||
sensor: global___SensorData | None = ...,
|
|
||||||
) -> None: ...
|
|
||||||
def HasField(self, field_name: typing.Literal["data", b"data", "nmea", b"nmea", "sensor", b"sensor"]) -> builtins.bool: ...
|
|
||||||
def ClearField(self, field_name: typing.Literal["data", b"data", "nmea", b"nmea", "sensor", b"sensor"]) -> None: ...
|
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["data", b"data"]) -> typing.Literal["nmea", "sensor"] | None: ...
|
|
||||||
|
|
||||||
global___InterdeviceMessage = InterdeviceMessage
|
|
||||||
6
meshtastic/protobuf/localonly_pb2.py
generated
6
meshtastic/protobuf/localonly_pb2.py
generated
@@ -15,16 +15,16 @@ from meshtastic.protobuf import config_pb2 as meshtastic_dot_protobuf_dot_config
|
|||||||
from meshtastic.protobuf import module_config_pb2 as meshtastic_dot_protobuf_dot_module__config__pb2
|
from meshtastic.protobuf import module_config_pb2 as meshtastic_dot_protobuf_dot_module__config__pb2
|
||||||
|
|
||||||
|
|
||||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n#meshtastic/protobuf/localonly.proto\x12\x13meshtastic.protobuf\x1a meshtastic/protobuf/config.proto\x1a\'meshtastic/protobuf/module_config.proto\"\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\"\xcf\t\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\x12L\n\rstatusmessage\x18\x0f \x01(\x0b\x32\x35.meshtastic.protobuf.ModuleConfig.StatusMessageConfig\x12U\n\x12traffic_management\x18\x10 \x01(\x0b\x32\x39.meshtastic.protobuf.ModuleConfig.TrafficManagementConfig\x12\x38\n\x03tak\x18\x11 \x01(\x0b\x32+.meshtastic.protobuf.ModuleConfig.TAKConfig\x12\x0f\n\x07version\x18\x08 \x01(\rBe\n\x14org.meshtastic.protoB\x0fLocalOnlyProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
|
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')
|
||||||
|
|
||||||
_globals = globals()
|
_globals = globals()
|
||||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.localonly_pb2', _globals)
|
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.localonly_pb2', _globals)
|
||||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||||
DESCRIPTOR._options = None
|
DESCRIPTOR._options = None
|
||||||
DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\017LocalOnlyProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\017LocalOnlyProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||||
_globals['_LOCALCONFIG']._serialized_start=136
|
_globals['_LOCALCONFIG']._serialized_start=136
|
||||||
_globals['_LOCALCONFIG']._serialized_end=642
|
_globals['_LOCALCONFIG']._serialized_end=642
|
||||||
_globals['_LOCALMODULECONFIG']._serialized_start=645
|
_globals['_LOCALMODULECONFIG']._serialized_start=645
|
||||||
_globals['_LOCALMODULECONFIG']._serialized_end=1876
|
_globals['_LOCALMODULECONFIG']._serialized_end=1653
|
||||||
# @@protoc_insertion_point(module_scope)
|
# @@protoc_insertion_point(module_scope)
|
||||||
|
|||||||
28
meshtastic/protobuf/localonly_pb2.pyi
generated
28
meshtastic/protobuf/localonly_pb2.pyi
generated
@@ -119,9 +119,6 @@ class LocalModuleConfig(google.protobuf.message.Message):
|
|||||||
AMBIENT_LIGHTING_FIELD_NUMBER: builtins.int
|
AMBIENT_LIGHTING_FIELD_NUMBER: builtins.int
|
||||||
DETECTION_SENSOR_FIELD_NUMBER: builtins.int
|
DETECTION_SENSOR_FIELD_NUMBER: builtins.int
|
||||||
PAXCOUNTER_FIELD_NUMBER: builtins.int
|
PAXCOUNTER_FIELD_NUMBER: builtins.int
|
||||||
STATUSMESSAGE_FIELD_NUMBER: builtins.int
|
|
||||||
TRAFFIC_MANAGEMENT_FIELD_NUMBER: builtins.int
|
|
||||||
TAK_FIELD_NUMBER: builtins.int
|
|
||||||
VERSION_FIELD_NUMBER: builtins.int
|
VERSION_FIELD_NUMBER: builtins.int
|
||||||
version: builtins.int
|
version: builtins.int
|
||||||
"""
|
"""
|
||||||
@@ -207,24 +204,6 @@ class LocalModuleConfig(google.protobuf.message.Message):
|
|||||||
Paxcounter Config
|
Paxcounter Config
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@property
|
|
||||||
def statusmessage(self) -> meshtastic.protobuf.module_config_pb2.ModuleConfig.StatusMessageConfig:
|
|
||||||
"""
|
|
||||||
StatusMessage Config
|
|
||||||
"""
|
|
||||||
|
|
||||||
@property
|
|
||||||
def traffic_management(self) -> meshtastic.protobuf.module_config_pb2.ModuleConfig.TrafficManagementConfig:
|
|
||||||
"""
|
|
||||||
The part of the config that is specific to the Traffic Management module
|
|
||||||
"""
|
|
||||||
|
|
||||||
@property
|
|
||||||
def tak(self) -> meshtastic.protobuf.module_config_pb2.ModuleConfig.TAKConfig:
|
|
||||||
"""
|
|
||||||
TAK Config
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
@@ -241,12 +220,9 @@ class LocalModuleConfig(google.protobuf.message.Message):
|
|||||||
ambient_lighting: meshtastic.protobuf.module_config_pb2.ModuleConfig.AmbientLightingConfig | None = ...,
|
ambient_lighting: meshtastic.protobuf.module_config_pb2.ModuleConfig.AmbientLightingConfig | None = ...,
|
||||||
detection_sensor: meshtastic.protobuf.module_config_pb2.ModuleConfig.DetectionSensorConfig | None = ...,
|
detection_sensor: meshtastic.protobuf.module_config_pb2.ModuleConfig.DetectionSensorConfig | None = ...,
|
||||||
paxcounter: meshtastic.protobuf.module_config_pb2.ModuleConfig.PaxcounterConfig | None = ...,
|
paxcounter: meshtastic.protobuf.module_config_pb2.ModuleConfig.PaxcounterConfig | None = ...,
|
||||||
statusmessage: meshtastic.protobuf.module_config_pb2.ModuleConfig.StatusMessageConfig | None = ...,
|
|
||||||
traffic_management: meshtastic.protobuf.module_config_pb2.ModuleConfig.TrafficManagementConfig | None = ...,
|
|
||||||
tak: meshtastic.protobuf.module_config_pb2.ModuleConfig.TAKConfig | None = ...,
|
|
||||||
version: builtins.int = ...,
|
version: builtins.int = ...,
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
def HasField(self, field_name: typing.Literal["ambient_lighting", b"ambient_lighting", "audio", b"audio", "canned_message", b"canned_message", "detection_sensor", b"detection_sensor", "external_notification", b"external_notification", "mqtt", b"mqtt", "neighbor_info", b"neighbor_info", "paxcounter", b"paxcounter", "range_test", b"range_test", "remote_hardware", b"remote_hardware", "serial", b"serial", "statusmessage", b"statusmessage", "store_forward", b"store_forward", "tak", b"tak", "telemetry", b"telemetry", "traffic_management", b"traffic_management"]) -> builtins.bool: ...
|
def HasField(self, field_name: typing.Literal["ambient_lighting", b"ambient_lighting", "audio", b"audio", "canned_message", b"canned_message", "detection_sensor", b"detection_sensor", "external_notification", b"external_notification", "mqtt", b"mqtt", "neighbor_info", b"neighbor_info", "paxcounter", b"paxcounter", "range_test", b"range_test", "remote_hardware", b"remote_hardware", "serial", b"serial", "store_forward", b"store_forward", "telemetry", b"telemetry"]) -> builtins.bool: ...
|
||||||
def ClearField(self, field_name: typing.Literal["ambient_lighting", b"ambient_lighting", "audio", b"audio", "canned_message", b"canned_message", "detection_sensor", b"detection_sensor", "external_notification", b"external_notification", "mqtt", b"mqtt", "neighbor_info", b"neighbor_info", "paxcounter", b"paxcounter", "range_test", b"range_test", "remote_hardware", b"remote_hardware", "serial", b"serial", "statusmessage", b"statusmessage", "store_forward", b"store_forward", "tak", b"tak", "telemetry", b"telemetry", "traffic_management", b"traffic_management", "version", b"version"]) -> None: ...
|
def ClearField(self, field_name: typing.Literal["ambient_lighting", b"ambient_lighting", "audio", b"audio", "canned_message", b"canned_message", "detection_sensor", b"detection_sensor", "external_notification", b"external_notification", "mqtt", b"mqtt", "neighbor_info", b"neighbor_info", "paxcounter", b"paxcounter", "range_test", b"range_test", "remote_hardware", b"remote_hardware", "serial", b"serial", "store_forward", b"store_forward", "telemetry", b"telemetry", "version", b"version"]) -> None: ...
|
||||||
|
|
||||||
global___LocalModuleConfig = LocalModuleConfig
|
global___LocalModuleConfig = LocalModuleConfig
|
||||||
|
|||||||
293
meshtastic/protobuf/mesh_pb2.py
generated
293
meshtastic/protobuf/mesh_pb2.py
generated
File diff suppressed because one or more lines are too long
1228
meshtastic/protobuf/mesh_pb2.pyi
generated
1228
meshtastic/protobuf/mesh_pb2.pyi
generated
File diff suppressed because it is too large
Load Diff
152
meshtastic/protobuf/module_config_pb2.py
generated
152
meshtastic/protobuf/module_config_pb2.py
generated
File diff suppressed because one or more lines are too long
240
meshtastic/protobuf/module_config_pb2.pyi
generated
240
meshtastic/protobuf/module_config_pb2.pyi
generated
@@ -9,7 +9,6 @@ import google.protobuf.descriptor
|
|||||||
import google.protobuf.internal.containers
|
import google.protobuf.internal.containers
|
||||||
import google.protobuf.internal.enum_type_wrapper
|
import google.protobuf.internal.enum_type_wrapper
|
||||||
import google.protobuf.message
|
import google.protobuf.message
|
||||||
import meshtastic.protobuf.atak_pb2
|
|
||||||
import sys
|
import sys
|
||||||
import typing
|
import typing
|
||||||
|
|
||||||
@@ -113,7 +112,7 @@ class ModuleConfig(google.protobuf.message.Message):
|
|||||||
"""
|
"""
|
||||||
json_enabled: builtins.bool
|
json_enabled: builtins.bool
|
||||||
"""
|
"""
|
||||||
Deprecated: JSON packet support on MQTT was removed, and this field is ignored.
|
Whether to send / consume json packets on MQTT
|
||||||
"""
|
"""
|
||||||
tls_enabled: builtins.bool
|
tls_enabled: builtins.bool
|
||||||
"""
|
"""
|
||||||
@@ -166,7 +165,6 @@ class ModuleConfig(google.protobuf.message.Message):
|
|||||||
|
|
||||||
PUBLISH_INTERVAL_SECS_FIELD_NUMBER: builtins.int
|
PUBLISH_INTERVAL_SECS_FIELD_NUMBER: builtins.int
|
||||||
POSITION_PRECISION_FIELD_NUMBER: builtins.int
|
POSITION_PRECISION_FIELD_NUMBER: builtins.int
|
||||||
SHOULD_REPORT_LOCATION_FIELD_NUMBER: builtins.int
|
|
||||||
publish_interval_secs: builtins.int
|
publish_interval_secs: builtins.int
|
||||||
"""
|
"""
|
||||||
How often we should report our info to the map (in seconds)
|
How often we should report our info to the map (in seconds)
|
||||||
@@ -175,18 +173,13 @@ class ModuleConfig(google.protobuf.message.Message):
|
|||||||
"""
|
"""
|
||||||
Bits of precision for the location sent (default of 32 is full precision).
|
Bits of precision for the location sent (default of 32 is full precision).
|
||||||
"""
|
"""
|
||||||
should_report_location: builtins.bool
|
|
||||||
"""
|
|
||||||
Whether we have opted-in to report our location to the map
|
|
||||||
"""
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
publish_interval_secs: builtins.int = ...,
|
publish_interval_secs: builtins.int = ...,
|
||||||
position_precision: builtins.int = ...,
|
position_precision: builtins.int = ...,
|
||||||
should_report_location: builtins.bool = ...,
|
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
def ClearField(self, field_name: typing.Literal["position_precision", b"position_precision", "publish_interval_secs", b"publish_interval_secs", "should_report_location", b"should_report_location"]) -> None: ...
|
def ClearField(self, field_name: typing.Literal["position_precision", b"position_precision", "publish_interval_secs", b"publish_interval_secs"]) -> None: ...
|
||||||
|
|
||||||
@typing.final
|
@typing.final
|
||||||
class RemoteHardwareConfig(google.protobuf.message.Message):
|
class RemoteHardwareConfig(google.protobuf.message.Message):
|
||||||
@@ -493,105 +486,6 @@ class ModuleConfig(google.protobuf.message.Message):
|
|||||||
) -> None: ...
|
) -> None: ...
|
||||||
def ClearField(self, field_name: typing.Literal["ble_threshold", b"ble_threshold", "enabled", b"enabled", "paxcounter_update_interval", b"paxcounter_update_interval", "wifi_threshold", b"wifi_threshold"]) -> None: ...
|
def ClearField(self, field_name: typing.Literal["ble_threshold", b"ble_threshold", "enabled", b"enabled", "paxcounter_update_interval", b"paxcounter_update_interval", "wifi_threshold", b"wifi_threshold"]) -> None: ...
|
||||||
|
|
||||||
@typing.final
|
|
||||||
class TrafficManagementConfig(google.protobuf.message.Message):
|
|
||||||
"""
|
|
||||||
Config for the Traffic Management module.
|
|
||||||
Provides packet inspection and traffic shaping to help reduce channel utilization
|
|
||||||
"""
|
|
||||||
|
|
||||||
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
|
||||||
|
|
||||||
ENABLED_FIELD_NUMBER: builtins.int
|
|
||||||
POSITION_DEDUP_ENABLED_FIELD_NUMBER: builtins.int
|
|
||||||
POSITION_PRECISION_BITS_FIELD_NUMBER: builtins.int
|
|
||||||
POSITION_MIN_INTERVAL_SECS_FIELD_NUMBER: builtins.int
|
|
||||||
NODEINFO_DIRECT_RESPONSE_FIELD_NUMBER: builtins.int
|
|
||||||
NODEINFO_DIRECT_RESPONSE_MAX_HOPS_FIELD_NUMBER: builtins.int
|
|
||||||
RATE_LIMIT_ENABLED_FIELD_NUMBER: builtins.int
|
|
||||||
RATE_LIMIT_WINDOW_SECS_FIELD_NUMBER: builtins.int
|
|
||||||
RATE_LIMIT_MAX_PACKETS_FIELD_NUMBER: builtins.int
|
|
||||||
DROP_UNKNOWN_ENABLED_FIELD_NUMBER: builtins.int
|
|
||||||
UNKNOWN_PACKET_THRESHOLD_FIELD_NUMBER: builtins.int
|
|
||||||
EXHAUST_HOP_TELEMETRY_FIELD_NUMBER: builtins.int
|
|
||||||
EXHAUST_HOP_POSITION_FIELD_NUMBER: builtins.int
|
|
||||||
ROUTER_PRESERVE_HOPS_FIELD_NUMBER: builtins.int
|
|
||||||
enabled: builtins.bool
|
|
||||||
"""
|
|
||||||
Master enable for traffic management module
|
|
||||||
"""
|
|
||||||
position_dedup_enabled: builtins.bool
|
|
||||||
"""
|
|
||||||
Enable position deduplication to drop redundant position broadcasts
|
|
||||||
"""
|
|
||||||
position_precision_bits: builtins.int
|
|
||||||
"""
|
|
||||||
Number of bits of precision for position deduplication (0-32)
|
|
||||||
"""
|
|
||||||
position_min_interval_secs: builtins.int
|
|
||||||
"""
|
|
||||||
Minimum interval in seconds between position updates from the same node
|
|
||||||
"""
|
|
||||||
nodeinfo_direct_response: builtins.bool
|
|
||||||
"""
|
|
||||||
Enable direct response to NodeInfo requests from local cache
|
|
||||||
"""
|
|
||||||
nodeinfo_direct_response_max_hops: builtins.int
|
|
||||||
"""
|
|
||||||
Minimum hop distance from requestor before responding to NodeInfo requests
|
|
||||||
"""
|
|
||||||
rate_limit_enabled: builtins.bool
|
|
||||||
"""
|
|
||||||
Enable per-node rate limiting to throttle chatty nodes
|
|
||||||
"""
|
|
||||||
rate_limit_window_secs: builtins.int
|
|
||||||
"""
|
|
||||||
Time window in seconds for rate limiting calculations
|
|
||||||
"""
|
|
||||||
rate_limit_max_packets: builtins.int
|
|
||||||
"""
|
|
||||||
Maximum packets allowed per node within the rate limit window
|
|
||||||
"""
|
|
||||||
drop_unknown_enabled: builtins.bool
|
|
||||||
"""
|
|
||||||
Enable dropping of unknown/undecryptable packets per rate_limit_window_secs
|
|
||||||
"""
|
|
||||||
unknown_packet_threshold: builtins.int
|
|
||||||
"""
|
|
||||||
Number of unknown packets before dropping from a node
|
|
||||||
"""
|
|
||||||
exhaust_hop_telemetry: builtins.bool
|
|
||||||
"""
|
|
||||||
Set hop_limit to 0 for relayed telemetry broadcasts (own packets unaffected)
|
|
||||||
"""
|
|
||||||
exhaust_hop_position: builtins.bool
|
|
||||||
"""
|
|
||||||
Set hop_limit to 0 for relayed position broadcasts (own packets unaffected)
|
|
||||||
"""
|
|
||||||
router_preserve_hops: builtins.bool
|
|
||||||
"""
|
|
||||||
Preserve hop_limit for router-to-router traffic
|
|
||||||
"""
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
*,
|
|
||||||
enabled: builtins.bool = ...,
|
|
||||||
position_dedup_enabled: builtins.bool = ...,
|
|
||||||
position_precision_bits: builtins.int = ...,
|
|
||||||
position_min_interval_secs: builtins.int = ...,
|
|
||||||
nodeinfo_direct_response: builtins.bool = ...,
|
|
||||||
nodeinfo_direct_response_max_hops: builtins.int = ...,
|
|
||||||
rate_limit_enabled: builtins.bool = ...,
|
|
||||||
rate_limit_window_secs: builtins.int = ...,
|
|
||||||
rate_limit_max_packets: builtins.int = ...,
|
|
||||||
drop_unknown_enabled: builtins.bool = ...,
|
|
||||||
unknown_packet_threshold: builtins.int = ...,
|
|
||||||
exhaust_hop_telemetry: builtins.bool = ...,
|
|
||||||
exhaust_hop_position: builtins.bool = ...,
|
|
||||||
router_preserve_hops: builtins.bool = ...,
|
|
||||||
) -> None: ...
|
|
||||||
def ClearField(self, field_name: typing.Literal["drop_unknown_enabled", b"drop_unknown_enabled", "enabled", b"enabled", "exhaust_hop_position", b"exhaust_hop_position", "exhaust_hop_telemetry", b"exhaust_hop_telemetry", "nodeinfo_direct_response", b"nodeinfo_direct_response", "nodeinfo_direct_response_max_hops", b"nodeinfo_direct_response_max_hops", "position_dedup_enabled", b"position_dedup_enabled", "position_min_interval_secs", b"position_min_interval_secs", "position_precision_bits", b"position_precision_bits", "rate_limit_enabled", b"rate_limit_enabled", "rate_limit_max_packets", b"rate_limit_max_packets", "rate_limit_window_secs", b"rate_limit_window_secs", "router_preserve_hops", b"router_preserve_hops", "unknown_packet_threshold", b"unknown_packet_threshold"]) -> None: ...
|
|
||||||
|
|
||||||
@typing.final
|
@typing.final
|
||||||
class SerialConfig(google.protobuf.message.Message):
|
class SerialConfig(google.protobuf.message.Message):
|
||||||
"""
|
"""
|
||||||
@@ -660,20 +554,6 @@ class ModuleConfig(google.protobuf.message.Message):
|
|||||||
"""NMEA messages specifically tailored for CalTopo"""
|
"""NMEA messages specifically tailored for CalTopo"""
|
||||||
WS85: ModuleConfig.SerialConfig._Serial_Mode.ValueType # 6
|
WS85: ModuleConfig.SerialConfig._Serial_Mode.ValueType # 6
|
||||||
"""Ecowitt WS85 weather station"""
|
"""Ecowitt WS85 weather station"""
|
||||||
VE_DIRECT: ModuleConfig.SerialConfig._Serial_Mode.ValueType # 7
|
|
||||||
"""VE.Direct is a serial protocol used by Victron Energy products
|
|
||||||
https://beta.ivc.no/wiki/index.php/Victron_VE_Direct_DIY_Cable
|
|
||||||
"""
|
|
||||||
MS_CONFIG: ModuleConfig.SerialConfig._Serial_Mode.ValueType # 8
|
|
||||||
"""Used to configure and view some parameters of MeshSolar.
|
|
||||||
https://heltec.org/project/meshsolar/
|
|
||||||
"""
|
|
||||||
LOG: ModuleConfig.SerialConfig._Serial_Mode.ValueType # 9
|
|
||||||
"""Logs mesh traffic to the serial pins, ideal for logging via openLog or similar.
|
|
||||||
includes other packets
|
|
||||||
"""
|
|
||||||
LOGTEXT: ModuleConfig.SerialConfig._Serial_Mode.ValueType # 10
|
|
||||||
"""only text (channel & DM)"""
|
|
||||||
|
|
||||||
class Serial_Mode(_Serial_Mode, metaclass=_Serial_ModeEnumTypeWrapper):
|
class Serial_Mode(_Serial_Mode, metaclass=_Serial_ModeEnumTypeWrapper):
|
||||||
"""
|
"""
|
||||||
@@ -689,20 +569,6 @@ class ModuleConfig(google.protobuf.message.Message):
|
|||||||
"""NMEA messages specifically tailored for CalTopo"""
|
"""NMEA messages specifically tailored for CalTopo"""
|
||||||
WS85: ModuleConfig.SerialConfig.Serial_Mode.ValueType # 6
|
WS85: ModuleConfig.SerialConfig.Serial_Mode.ValueType # 6
|
||||||
"""Ecowitt WS85 weather station"""
|
"""Ecowitt WS85 weather station"""
|
||||||
VE_DIRECT: ModuleConfig.SerialConfig.Serial_Mode.ValueType # 7
|
|
||||||
"""VE.Direct is a serial protocol used by Victron Energy products
|
|
||||||
https://beta.ivc.no/wiki/index.php/Victron_VE_Direct_DIY_Cable
|
|
||||||
"""
|
|
||||||
MS_CONFIG: ModuleConfig.SerialConfig.Serial_Mode.ValueType # 8
|
|
||||||
"""Used to configure and view some parameters of MeshSolar.
|
|
||||||
https://heltec.org/project/meshsolar/
|
|
||||||
"""
|
|
||||||
LOG: ModuleConfig.SerialConfig.Serial_Mode.ValueType # 9
|
|
||||||
"""Logs mesh traffic to the serial pins, ideal for logging via openLog or similar.
|
|
||||||
includes other packets
|
|
||||||
"""
|
|
||||||
LOGTEXT: ModuleConfig.SerialConfig.Serial_Mode.ValueType # 10
|
|
||||||
"""only text (channel & DM)"""
|
|
||||||
|
|
||||||
ENABLED_FIELD_NUMBER: builtins.int
|
ENABLED_FIELD_NUMBER: builtins.int
|
||||||
ECHO_FIELD_NUMBER: builtins.int
|
ECHO_FIELD_NUMBER: builtins.int
|
||||||
@@ -936,7 +802,6 @@ class ModuleConfig(google.protobuf.message.Message):
|
|||||||
ENABLED_FIELD_NUMBER: builtins.int
|
ENABLED_FIELD_NUMBER: builtins.int
|
||||||
SENDER_FIELD_NUMBER: builtins.int
|
SENDER_FIELD_NUMBER: builtins.int
|
||||||
SAVE_FIELD_NUMBER: builtins.int
|
SAVE_FIELD_NUMBER: builtins.int
|
||||||
CLEAR_ON_REBOOT_FIELD_NUMBER: builtins.int
|
|
||||||
enabled: builtins.bool
|
enabled: builtins.bool
|
||||||
"""
|
"""
|
||||||
Enable the Range Test Module
|
Enable the Range Test Module
|
||||||
@@ -950,20 +815,14 @@ class ModuleConfig(google.protobuf.message.Message):
|
|||||||
Bool value indicating that this node should save a RangeTest.csv file.
|
Bool value indicating that this node should save a RangeTest.csv file.
|
||||||
ESP32 Only
|
ESP32 Only
|
||||||
"""
|
"""
|
||||||
clear_on_reboot: builtins.bool
|
|
||||||
"""
|
|
||||||
Bool indicating that the node should cleanup / destroy it's RangeTest.csv file.
|
|
||||||
ESP32 Only
|
|
||||||
"""
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
enabled: builtins.bool = ...,
|
enabled: builtins.bool = ...,
|
||||||
sender: builtins.int = ...,
|
sender: builtins.int = ...,
|
||||||
save: builtins.bool = ...,
|
save: builtins.bool = ...,
|
||||||
clear_on_reboot: builtins.bool = ...,
|
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
def ClearField(self, field_name: typing.Literal["clear_on_reboot", b"clear_on_reboot", "enabled", b"enabled", "save", b"save", "sender", b"sender"]) -> None: ...
|
def ClearField(self, field_name: typing.Literal["enabled", b"enabled", "save", b"save", "sender", b"sender"]) -> None: ...
|
||||||
|
|
||||||
@typing.final
|
@typing.final
|
||||||
class TelemetryConfig(google.protobuf.message.Message):
|
class TelemetryConfig(google.protobuf.message.Message):
|
||||||
@@ -986,8 +845,6 @@ class ModuleConfig(google.protobuf.message.Message):
|
|||||||
HEALTH_MEASUREMENT_ENABLED_FIELD_NUMBER: builtins.int
|
HEALTH_MEASUREMENT_ENABLED_FIELD_NUMBER: builtins.int
|
||||||
HEALTH_UPDATE_INTERVAL_FIELD_NUMBER: builtins.int
|
HEALTH_UPDATE_INTERVAL_FIELD_NUMBER: builtins.int
|
||||||
HEALTH_SCREEN_ENABLED_FIELD_NUMBER: builtins.int
|
HEALTH_SCREEN_ENABLED_FIELD_NUMBER: builtins.int
|
||||||
DEVICE_TELEMETRY_ENABLED_FIELD_NUMBER: builtins.int
|
|
||||||
AIR_QUALITY_SCREEN_ENABLED_FIELD_NUMBER: builtins.int
|
|
||||||
device_update_interval: builtins.int
|
device_update_interval: builtins.int
|
||||||
"""
|
"""
|
||||||
Interval in seconds of how often we should try to send our
|
Interval in seconds of how often we should try to send our
|
||||||
@@ -1048,15 +905,6 @@ class ModuleConfig(google.protobuf.message.Message):
|
|||||||
"""
|
"""
|
||||||
Enable/Disable the health telemetry module on-device display
|
Enable/Disable the health telemetry module on-device display
|
||||||
"""
|
"""
|
||||||
device_telemetry_enabled: builtins.bool
|
|
||||||
"""
|
|
||||||
Enable/Disable the device telemetry module to send metrics to the mesh
|
|
||||||
Note: We will still send telemtry to the connected phone / client every minute over the API
|
|
||||||
"""
|
|
||||||
air_quality_screen_enabled: builtins.bool
|
|
||||||
"""
|
|
||||||
Enable/Disable the air quality telemetry measurement module on-device display
|
|
||||||
"""
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
@@ -1073,10 +921,8 @@ class ModuleConfig(google.protobuf.message.Message):
|
|||||||
health_measurement_enabled: builtins.bool = ...,
|
health_measurement_enabled: builtins.bool = ...,
|
||||||
health_update_interval: builtins.int = ...,
|
health_update_interval: builtins.int = ...,
|
||||||
health_screen_enabled: builtins.bool = ...,
|
health_screen_enabled: builtins.bool = ...,
|
||||||
device_telemetry_enabled: builtins.bool = ...,
|
|
||||||
air_quality_screen_enabled: builtins.bool = ...,
|
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
def ClearField(self, field_name: typing.Literal["air_quality_enabled", b"air_quality_enabled", "air_quality_interval", b"air_quality_interval", "air_quality_screen_enabled", b"air_quality_screen_enabled", "device_telemetry_enabled", b"device_telemetry_enabled", "device_update_interval", b"device_update_interval", "environment_display_fahrenheit", b"environment_display_fahrenheit", "environment_measurement_enabled", b"environment_measurement_enabled", "environment_screen_enabled", b"environment_screen_enabled", "environment_update_interval", b"environment_update_interval", "health_measurement_enabled", b"health_measurement_enabled", "health_screen_enabled", b"health_screen_enabled", "health_update_interval", b"health_update_interval", "power_measurement_enabled", b"power_measurement_enabled", "power_screen_enabled", b"power_screen_enabled", "power_update_interval", b"power_update_interval"]) -> None: ...
|
def ClearField(self, field_name: typing.Literal["air_quality_enabled", b"air_quality_enabled", "air_quality_interval", b"air_quality_interval", "device_update_interval", b"device_update_interval", "environment_display_fahrenheit", b"environment_display_fahrenheit", "environment_measurement_enabled", b"environment_measurement_enabled", "environment_screen_enabled", b"environment_screen_enabled", "environment_update_interval", b"environment_update_interval", "health_measurement_enabled", b"health_measurement_enabled", "health_screen_enabled", b"health_screen_enabled", "health_update_interval", b"health_update_interval", "power_measurement_enabled", b"power_measurement_enabled", "power_screen_enabled", b"power_screen_enabled", "power_update_interval", b"power_update_interval"]) -> None: ...
|
||||||
|
|
||||||
@typing.final
|
@typing.final
|
||||||
class CannedMessageConfig(google.protobuf.message.Message):
|
class CannedMessageConfig(google.protobuf.message.Message):
|
||||||
@@ -1282,54 +1128,6 @@ class ModuleConfig(google.protobuf.message.Message):
|
|||||||
) -> None: ...
|
) -> None: ...
|
||||||
def ClearField(self, field_name: typing.Literal["blue", b"blue", "current", b"current", "green", b"green", "led_state", b"led_state", "red", b"red"]) -> None: ...
|
def ClearField(self, field_name: typing.Literal["blue", b"blue", "current", b"current", "green", b"green", "led_state", b"led_state", "red", b"red"]) -> None: ...
|
||||||
|
|
||||||
@typing.final
|
|
||||||
class StatusMessageConfig(google.protobuf.message.Message):
|
|
||||||
"""
|
|
||||||
StatusMessage config - Allows setting a status message for a node to periodically rebroadcast
|
|
||||||
"""
|
|
||||||
|
|
||||||
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
|
||||||
|
|
||||||
NODE_STATUS_FIELD_NUMBER: builtins.int
|
|
||||||
node_status: builtins.str
|
|
||||||
"""
|
|
||||||
The actual status string
|
|
||||||
"""
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
*,
|
|
||||||
node_status: builtins.str = ...,
|
|
||||||
) -> None: ...
|
|
||||||
def ClearField(self, field_name: typing.Literal["node_status", b"node_status"]) -> None: ...
|
|
||||||
|
|
||||||
@typing.final
|
|
||||||
class TAKConfig(google.protobuf.message.Message):
|
|
||||||
"""
|
|
||||||
TAK team/role configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
|
||||||
|
|
||||||
TEAM_FIELD_NUMBER: builtins.int
|
|
||||||
ROLE_FIELD_NUMBER: builtins.int
|
|
||||||
team: meshtastic.protobuf.atak_pb2.Team.ValueType
|
|
||||||
"""
|
|
||||||
Team color.
|
|
||||||
Default Unspecifed_Color -> firmware uses Cyan
|
|
||||||
"""
|
|
||||||
role: meshtastic.protobuf.atak_pb2.MemberRole.ValueType
|
|
||||||
"""
|
|
||||||
Member role.
|
|
||||||
Default Unspecifed -> firmware uses TeamMember
|
|
||||||
"""
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
*,
|
|
||||||
team: meshtastic.protobuf.atak_pb2.Team.ValueType = ...,
|
|
||||||
role: meshtastic.protobuf.atak_pb2.MemberRole.ValueType = ...,
|
|
||||||
) -> None: ...
|
|
||||||
def ClearField(self, field_name: typing.Literal["role", b"role", "team", b"team"]) -> None: ...
|
|
||||||
|
|
||||||
MQTT_FIELD_NUMBER: builtins.int
|
MQTT_FIELD_NUMBER: builtins.int
|
||||||
SERIAL_FIELD_NUMBER: builtins.int
|
SERIAL_FIELD_NUMBER: builtins.int
|
||||||
EXTERNAL_NOTIFICATION_FIELD_NUMBER: builtins.int
|
EXTERNAL_NOTIFICATION_FIELD_NUMBER: builtins.int
|
||||||
@@ -1343,9 +1141,6 @@ class ModuleConfig(google.protobuf.message.Message):
|
|||||||
AMBIENT_LIGHTING_FIELD_NUMBER: builtins.int
|
AMBIENT_LIGHTING_FIELD_NUMBER: builtins.int
|
||||||
DETECTION_SENSOR_FIELD_NUMBER: builtins.int
|
DETECTION_SENSOR_FIELD_NUMBER: builtins.int
|
||||||
PAXCOUNTER_FIELD_NUMBER: builtins.int
|
PAXCOUNTER_FIELD_NUMBER: builtins.int
|
||||||
STATUSMESSAGE_FIELD_NUMBER: builtins.int
|
|
||||||
TRAFFIC_MANAGEMENT_FIELD_NUMBER: builtins.int
|
|
||||||
TAK_FIELD_NUMBER: builtins.int
|
|
||||||
@property
|
@property
|
||||||
def mqtt(self) -> global___ModuleConfig.MQTTConfig:
|
def mqtt(self) -> global___ModuleConfig.MQTTConfig:
|
||||||
"""
|
"""
|
||||||
@@ -1424,24 +1219,6 @@ class ModuleConfig(google.protobuf.message.Message):
|
|||||||
TODO: REPLACE
|
TODO: REPLACE
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@property
|
|
||||||
def statusmessage(self) -> global___ModuleConfig.StatusMessageConfig:
|
|
||||||
"""
|
|
||||||
TODO: REPLACE
|
|
||||||
"""
|
|
||||||
|
|
||||||
@property
|
|
||||||
def traffic_management(self) -> global___ModuleConfig.TrafficManagementConfig:
|
|
||||||
"""
|
|
||||||
Traffic management module config for mesh network optimization
|
|
||||||
"""
|
|
||||||
|
|
||||||
@property
|
|
||||||
def tak(self) -> global___ModuleConfig.TAKConfig:
|
|
||||||
"""
|
|
||||||
TAK team/role configuration for TAK_TRACKER
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
@@ -1458,13 +1235,10 @@ class ModuleConfig(google.protobuf.message.Message):
|
|||||||
ambient_lighting: global___ModuleConfig.AmbientLightingConfig | None = ...,
|
ambient_lighting: global___ModuleConfig.AmbientLightingConfig | None = ...,
|
||||||
detection_sensor: global___ModuleConfig.DetectionSensorConfig | None = ...,
|
detection_sensor: global___ModuleConfig.DetectionSensorConfig | None = ...,
|
||||||
paxcounter: global___ModuleConfig.PaxcounterConfig | None = ...,
|
paxcounter: global___ModuleConfig.PaxcounterConfig | None = ...,
|
||||||
statusmessage: global___ModuleConfig.StatusMessageConfig | None = ...,
|
|
||||||
traffic_management: global___ModuleConfig.TrafficManagementConfig | None = ...,
|
|
||||||
tak: global___ModuleConfig.TAKConfig | None = ...,
|
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
def HasField(self, field_name: typing.Literal["ambient_lighting", b"ambient_lighting", "audio", b"audio", "canned_message", b"canned_message", "detection_sensor", b"detection_sensor", "external_notification", b"external_notification", "mqtt", b"mqtt", "neighbor_info", b"neighbor_info", "paxcounter", b"paxcounter", "payload_variant", b"payload_variant", "range_test", b"range_test", "remote_hardware", b"remote_hardware", "serial", b"serial", "statusmessage", b"statusmessage", "store_forward", b"store_forward", "tak", b"tak", "telemetry", b"telemetry", "traffic_management", b"traffic_management"]) -> builtins.bool: ...
|
def HasField(self, field_name: typing.Literal["ambient_lighting", b"ambient_lighting", "audio", b"audio", "canned_message", b"canned_message", "detection_sensor", b"detection_sensor", "external_notification", b"external_notification", "mqtt", b"mqtt", "neighbor_info", b"neighbor_info", "paxcounter", b"paxcounter", "payload_variant", b"payload_variant", "range_test", b"range_test", "remote_hardware", b"remote_hardware", "serial", b"serial", "store_forward", b"store_forward", "telemetry", b"telemetry"]) -> builtins.bool: ...
|
||||||
def ClearField(self, field_name: typing.Literal["ambient_lighting", b"ambient_lighting", "audio", b"audio", "canned_message", b"canned_message", "detection_sensor", b"detection_sensor", "external_notification", b"external_notification", "mqtt", b"mqtt", "neighbor_info", b"neighbor_info", "paxcounter", b"paxcounter", "payload_variant", b"payload_variant", "range_test", b"range_test", "remote_hardware", b"remote_hardware", "serial", b"serial", "statusmessage", b"statusmessage", "store_forward", b"store_forward", "tak", b"tak", "telemetry", b"telemetry", "traffic_management", b"traffic_management"]) -> None: ...
|
def ClearField(self, field_name: typing.Literal["ambient_lighting", b"ambient_lighting", "audio", b"audio", "canned_message", b"canned_message", "detection_sensor", b"detection_sensor", "external_notification", b"external_notification", "mqtt", b"mqtt", "neighbor_info", b"neighbor_info", "paxcounter", b"paxcounter", "payload_variant", b"payload_variant", "range_test", b"range_test", "remote_hardware", b"remote_hardware", "serial", b"serial", "store_forward", b"store_forward", "telemetry", b"telemetry"]) -> None: ...
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["payload_variant", b"payload_variant"]) -> typing.Literal["mqtt", "serial", "external_notification", "store_forward", "range_test", "telemetry", "canned_message", "audio", "remote_hardware", "neighbor_info", "ambient_lighting", "detection_sensor", "paxcounter", "statusmessage", "traffic_management", "tak"] | None: ...
|
def WhichOneof(self, oneof_group: typing.Literal["payload_variant", b"payload_variant"]) -> typing.Literal["mqtt", "serial", "external_notification", "store_forward", "range_test", "telemetry", "canned_message", "audio", "remote_hardware", "neighbor_info", "ambient_lighting", "detection_sensor", "paxcounter"] | None: ...
|
||||||
|
|
||||||
global___ModuleConfig = ModuleConfig
|
global___ModuleConfig = ModuleConfig
|
||||||
|
|
||||||
|
|||||||
21
meshtastic/protobuf/mqtt_pb2.py
generated
21
meshtastic/protobuf/mqtt_pb2.py
generated
@@ -13,27 +13,18 @@ _sym_db = _symbol_database.Default()
|
|||||||
|
|
||||||
from meshtastic.protobuf import config_pb2 as meshtastic_dot_protobuf_dot_config__pb2
|
from meshtastic.protobuf import config_pb2 as meshtastic_dot_protobuf_dot_config__pb2
|
||||||
from meshtastic.protobuf import mesh_pb2 as meshtastic_dot_protobuf_dot_mesh__pb2
|
from meshtastic.protobuf import mesh_pb2 as meshtastic_dot_protobuf_dot_mesh__pb2
|
||||||
from meshtastic.protobuf import nanopb_pb2 as meshtastic_dot_protobuf_dot_nanopb__pb2
|
|
||||||
|
|
||||||
|
|
||||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1emeshtastic/protobuf/mqtt.proto\x12\x13meshtastic.protobuf\x1a meshtastic/protobuf/config.proto\x1a\x1emeshtastic/protobuf/mesh.proto\x1a meshtastic/protobuf/nanopb.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\"\x9f\x04\n\tMapReport\x12\x18\n\tlong_name\x18\x01 \x01(\tB\x05\x92?\x02\x08(\x12\x19\n\nshort_name\x18\x02 \x01(\tB\x05\x92?\x02\x08\x05\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\x1f\n\x10\x66irmware_version\x18\x05 \x01(\tB\x05\x92?\x02\x08\x12\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%\n\x16num_online_local_nodes\x18\r \x01(\rB\x05\x92?\x02\x38\x10\x12!\n\x19has_opted_report_location\x18\x0e \x01(\x08\x42`\n\x14org.meshtastic.protoB\nMQTTProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
|
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1emeshtastic/protobuf/mqtt.proto\x12\x13meshtastic.protobuf\x1a meshtastic/protobuf/config.proto\x1a\x1emeshtastic/protobuf/mesh.proto\"j\n\x0fServiceEnvelope\x12/\n\x06packet\x18\x01 \x01(\x0b\x32\x1f.meshtastic.protobuf.MeshPacket\x12\x12\n\nchannel_id\x18\x02 \x01(\t\x12\x12\n\ngateway_id\x18\x03 \x01(\t\"\xe0\x03\n\tMapReport\x12\x11\n\tlong_name\x18\x01 \x01(\t\x12\x12\n\nshort_name\x18\x02 \x01(\t\x12;\n\x04role\x18\x03 \x01(\x0e\x32-.meshtastic.protobuf.Config.DeviceConfig.Role\x12\x34\n\x08hw_model\x18\x04 \x01(\x0e\x32\".meshtastic.protobuf.HardwareModel\x12\x18\n\x10\x66irmware_version\x18\x05 \x01(\t\x12\x41\n\x06region\x18\x06 \x01(\x0e\x32\x31.meshtastic.protobuf.Config.LoRaConfig.RegionCode\x12H\n\x0cmodem_preset\x18\x07 \x01(\x0e\x32\x32.meshtastic.protobuf.Config.LoRaConfig.ModemPreset\x12\x1b\n\x13has_default_channel\x18\x08 \x01(\x08\x12\x12\n\nlatitude_i\x18\t \x01(\x0f\x12\x13\n\x0blongitude_i\x18\n \x01(\x0f\x12\x10\n\x08\x61ltitude\x18\x0b \x01(\x05\x12\x1a\n\x12position_precision\x18\x0c \x01(\r\x12\x1e\n\x16num_online_local_nodes\x18\r \x01(\rB_\n\x13\x63om.geeksville.meshB\nMQTTProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
|
||||||
|
|
||||||
_globals = globals()
|
_globals = globals()
|
||||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.mqtt_pb2', _globals)
|
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.mqtt_pb2', _globals)
|
||||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||||
DESCRIPTOR._options = None
|
DESCRIPTOR._options = None
|
||||||
DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\nMQTTProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\nMQTTProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||||
_MAPREPORT.fields_by_name['long_name']._options = None
|
_globals['_SERVICEENVELOPE']._serialized_start=121
|
||||||
_MAPREPORT.fields_by_name['long_name']._serialized_options = b'\222?\002\010('
|
_globals['_SERVICEENVELOPE']._serialized_end=227
|
||||||
_MAPREPORT.fields_by_name['short_name']._options = None
|
_globals['_MAPREPORT']._serialized_start=230
|
||||||
_MAPREPORT.fields_by_name['short_name']._serialized_options = b'\222?\002\010\005'
|
_globals['_MAPREPORT']._serialized_end=710
|
||||||
_MAPREPORT.fields_by_name['firmware_version']._options = None
|
|
||||||
_MAPREPORT.fields_by_name['firmware_version']._serialized_options = b'\222?\002\010\022'
|
|
||||||
_MAPREPORT.fields_by_name['num_online_local_nodes']._options = None
|
|
||||||
_MAPREPORT.fields_by_name['num_online_local_nodes']._serialized_options = b'\222?\0028\020'
|
|
||||||
_globals['_SERVICEENVELOPE']._serialized_start=155
|
|
||||||
_globals['_SERVICEENVELOPE']._serialized_end=261
|
|
||||||
_globals['_MAPREPORT']._serialized_start=264
|
|
||||||
_globals['_MAPREPORT']._serialized_end=807
|
|
||||||
# @@protoc_insertion_point(module_scope)
|
# @@protoc_insertion_point(module_scope)
|
||||||
|
|||||||
9
meshtastic/protobuf/mqtt_pb2.pyi
generated
9
meshtastic/protobuf/mqtt_pb2.pyi
generated
@@ -72,7 +72,6 @@ class MapReport(google.protobuf.message.Message):
|
|||||||
ALTITUDE_FIELD_NUMBER: builtins.int
|
ALTITUDE_FIELD_NUMBER: builtins.int
|
||||||
POSITION_PRECISION_FIELD_NUMBER: builtins.int
|
POSITION_PRECISION_FIELD_NUMBER: builtins.int
|
||||||
NUM_ONLINE_LOCAL_NODES_FIELD_NUMBER: builtins.int
|
NUM_ONLINE_LOCAL_NODES_FIELD_NUMBER: builtins.int
|
||||||
HAS_OPTED_REPORT_LOCATION_FIELD_NUMBER: builtins.int
|
|
||||||
long_name: builtins.str
|
long_name: builtins.str
|
||||||
"""
|
"""
|
||||||
A full name for this user, i.e. "Kevin Hester"
|
A full name for this user, i.e. "Kevin Hester"
|
||||||
@@ -127,11 +126,6 @@ class MapReport(google.protobuf.message.Message):
|
|||||||
"""
|
"""
|
||||||
Number of online nodes (heard in the last 2 hours) this node has in its list that were received locally (not via MQTT)
|
Number of online nodes (heard in the last 2 hours) this node has in its list that were received locally (not via MQTT)
|
||||||
"""
|
"""
|
||||||
has_opted_report_location: builtins.bool
|
|
||||||
"""
|
|
||||||
User has opted in to share their location (map report) with the mqtt server
|
|
||||||
Controlled by map_report.should_report_location
|
|
||||||
"""
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
@@ -148,8 +142,7 @@ class MapReport(google.protobuf.message.Message):
|
|||||||
altitude: builtins.int = ...,
|
altitude: builtins.int = ...,
|
||||||
position_precision: builtins.int = ...,
|
position_precision: builtins.int = ...,
|
||||||
num_online_local_nodes: builtins.int = ...,
|
num_online_local_nodes: builtins.int = ...,
|
||||||
has_opted_report_location: builtins.bool = ...,
|
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
def ClearField(self, field_name: typing.Literal["altitude", b"altitude", "firmware_version", b"firmware_version", "has_default_channel", b"has_default_channel", "has_opted_report_location", b"has_opted_report_location", "hw_model", b"hw_model", "latitude_i", b"latitude_i", "long_name", b"long_name", "longitude_i", b"longitude_i", "modem_preset", b"modem_preset", "num_online_local_nodes", b"num_online_local_nodes", "position_precision", b"position_precision", "region", b"region", "role", b"role", "short_name", b"short_name"]) -> None: ...
|
def ClearField(self, field_name: typing.Literal["altitude", b"altitude", "firmware_version", b"firmware_version", "has_default_channel", b"has_default_channel", "hw_model", b"hw_model", "latitude_i", b"latitude_i", "long_name", b"long_name", "longitude_i", b"longitude_i", "modem_preset", b"modem_preset", "num_online_local_nodes", b"num_online_local_nodes", "position_precision", b"position_precision", "region", b"region", "role", b"role", "short_name", b"short_name"]) -> None: ...
|
||||||
|
|
||||||
global___MapReport = MapReport
|
global___MapReport = MapReport
|
||||||
|
|||||||
35
meshtastic/protobuf/nanopb_pb2.py
generated
35
meshtastic/protobuf/nanopb_pb2.py
generated
@@ -1,35 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
||||||
# source: meshtastic/protobuf/nanopb.proto
|
|
||||||
"""Generated protocol buffer code."""
|
|
||||||
from google.protobuf import descriptor as _descriptor
|
|
||||||
from google.protobuf import descriptor_pool as _descriptor_pool
|
|
||||||
from google.protobuf import symbol_database as _symbol_database
|
|
||||||
from google.protobuf.internal import builder as _builder
|
|
||||||
# @@protoc_insertion_point(imports)
|
|
||||||
|
|
||||||
_sym_db = _symbol_database.Default()
|
|
||||||
|
|
||||||
|
|
||||||
from google.protobuf import descriptor_pb2 as google_dot_protobuf_dot_descriptor__pb2
|
|
||||||
|
|
||||||
|
|
||||||
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')
|
|
||||||
|
|
||||||
_globals = globals()
|
|
||||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
|
||||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.nanopb_pb2', _globals)
|
|
||||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
|
||||||
DESCRIPTOR._options = None
|
|
||||||
DESCRIPTOR._serialized_options = b'\n\030fi.kapsi.koti.jpa.nanopbZ\"github.com/meshtastic/go/generated'
|
|
||||||
_globals['_FIELDTYPE']._serialized_start=1005
|
|
||||||
_globals['_FIELDTYPE']._serialized_end=1110
|
|
||||||
_globals['_INTSIZE']._serialized_start=1112
|
|
||||||
_globals['_INTSIZE']._serialized_end=1180
|
|
||||||
_globals['_TYPENAMEMANGLING']._serialized_start=1182
|
|
||||||
_globals['_TYPENAMEMANGLING']._serialized_end=1272
|
|
||||||
_globals['_DESCRIPTORSIZE']._serialized_start=1274
|
|
||||||
_globals['_DESCRIPTORSIZE']._serialized_end=1343
|
|
||||||
_globals['_NANOPBOPTIONS']._serialized_start=71
|
|
||||||
_globals['_NANOPBOPTIONS']._serialized_end=1003
|
|
||||||
# @@protoc_insertion_point(module_scope)
|
|
||||||
324
meshtastic/protobuf/nanopb_pb2.pyi
generated
324
meshtastic/protobuf/nanopb_pb2.pyi
generated
@@ -1,324 +0,0 @@
|
|||||||
"""
|
|
||||||
@generated by mypy-protobuf. Do not edit manually!
|
|
||||||
isort:skip_file
|
|
||||||
Custom options for defining:
|
|
||||||
- Maximum size of string/bytes
|
|
||||||
- Maximum number of elements in array
|
|
||||||
|
|
||||||
These are used by nanopb to generate statically allocable structures
|
|
||||||
for memory-limited environments.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import builtins
|
|
||||||
import collections.abc
|
|
||||||
import google.protobuf.descriptor
|
|
||||||
import google.protobuf.descriptor_pb2
|
|
||||||
import google.protobuf.internal.containers
|
|
||||||
import google.protobuf.internal.enum_type_wrapper
|
|
||||||
import google.protobuf.internal.extension_dict
|
|
||||||
import google.protobuf.message
|
|
||||||
import sys
|
|
||||||
import typing
|
|
||||||
|
|
||||||
if sys.version_info >= (3, 10):
|
|
||||||
import typing as typing_extensions
|
|
||||||
else:
|
|
||||||
import typing_extensions
|
|
||||||
|
|
||||||
DESCRIPTOR: google.protobuf.descriptor.FileDescriptor
|
|
||||||
|
|
||||||
class _FieldType:
|
|
||||||
ValueType = typing.NewType("ValueType", builtins.int)
|
|
||||||
V: typing_extensions.TypeAlias = ValueType
|
|
||||||
|
|
||||||
class _FieldTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_FieldType.ValueType], builtins.type):
|
|
||||||
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
|
|
||||||
FT_DEFAULT: _FieldType.ValueType # 0
|
|
||||||
"""Automatically decide field type, generate static field if possible."""
|
|
||||||
FT_CALLBACK: _FieldType.ValueType # 1
|
|
||||||
"""Always generate a callback field."""
|
|
||||||
FT_POINTER: _FieldType.ValueType # 4
|
|
||||||
"""Always generate a dynamically allocated field."""
|
|
||||||
FT_STATIC: _FieldType.ValueType # 2
|
|
||||||
"""Generate a static field or raise an exception if not possible."""
|
|
||||||
FT_IGNORE: _FieldType.ValueType # 3
|
|
||||||
"""Ignore the field completely."""
|
|
||||||
FT_INLINE: _FieldType.ValueType # 5
|
|
||||||
"""Legacy option, use the separate 'fixed_length' option instead"""
|
|
||||||
|
|
||||||
class FieldType(_FieldType, metaclass=_FieldTypeEnumTypeWrapper): ...
|
|
||||||
|
|
||||||
FT_DEFAULT: FieldType.ValueType # 0
|
|
||||||
"""Automatically decide field type, generate static field if possible."""
|
|
||||||
FT_CALLBACK: FieldType.ValueType # 1
|
|
||||||
"""Always generate a callback field."""
|
|
||||||
FT_POINTER: FieldType.ValueType # 4
|
|
||||||
"""Always generate a dynamically allocated field."""
|
|
||||||
FT_STATIC: FieldType.ValueType # 2
|
|
||||||
"""Generate a static field or raise an exception if not possible."""
|
|
||||||
FT_IGNORE: FieldType.ValueType # 3
|
|
||||||
"""Ignore the field completely."""
|
|
||||||
FT_INLINE: FieldType.ValueType # 5
|
|
||||||
"""Legacy option, use the separate 'fixed_length' option instead"""
|
|
||||||
global___FieldType = FieldType
|
|
||||||
|
|
||||||
class _IntSize:
|
|
||||||
ValueType = typing.NewType("ValueType", builtins.int)
|
|
||||||
V: typing_extensions.TypeAlias = ValueType
|
|
||||||
|
|
||||||
class _IntSizeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_IntSize.ValueType], builtins.type):
|
|
||||||
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
|
|
||||||
IS_DEFAULT: _IntSize.ValueType # 0
|
|
||||||
"""Default, 32/64bit based on type in .proto"""
|
|
||||||
IS_8: _IntSize.ValueType # 8
|
|
||||||
IS_16: _IntSize.ValueType # 16
|
|
||||||
IS_32: _IntSize.ValueType # 32
|
|
||||||
IS_64: _IntSize.ValueType # 64
|
|
||||||
|
|
||||||
class IntSize(_IntSize, metaclass=_IntSizeEnumTypeWrapper): ...
|
|
||||||
|
|
||||||
IS_DEFAULT: IntSize.ValueType # 0
|
|
||||||
"""Default, 32/64bit based on type in .proto"""
|
|
||||||
IS_8: IntSize.ValueType # 8
|
|
||||||
IS_16: IntSize.ValueType # 16
|
|
||||||
IS_32: IntSize.ValueType # 32
|
|
||||||
IS_64: IntSize.ValueType # 64
|
|
||||||
global___IntSize = IntSize
|
|
||||||
|
|
||||||
class _TypenameMangling:
|
|
||||||
ValueType = typing.NewType("ValueType", builtins.int)
|
|
||||||
V: typing_extensions.TypeAlias = ValueType
|
|
||||||
|
|
||||||
class _TypenameManglingEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_TypenameMangling.ValueType], builtins.type):
|
|
||||||
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
|
|
||||||
M_NONE: _TypenameMangling.ValueType # 0
|
|
||||||
"""Default, no typename mangling"""
|
|
||||||
M_STRIP_PACKAGE: _TypenameMangling.ValueType # 1
|
|
||||||
"""Strip current package name"""
|
|
||||||
M_FLATTEN: _TypenameMangling.ValueType # 2
|
|
||||||
"""Only use last path component"""
|
|
||||||
M_PACKAGE_INITIALS: _TypenameMangling.ValueType # 3
|
|
||||||
"""Replace the package name by the initials"""
|
|
||||||
|
|
||||||
class TypenameMangling(_TypenameMangling, metaclass=_TypenameManglingEnumTypeWrapper): ...
|
|
||||||
|
|
||||||
M_NONE: TypenameMangling.ValueType # 0
|
|
||||||
"""Default, no typename mangling"""
|
|
||||||
M_STRIP_PACKAGE: TypenameMangling.ValueType # 1
|
|
||||||
"""Strip current package name"""
|
|
||||||
M_FLATTEN: TypenameMangling.ValueType # 2
|
|
||||||
"""Only use last path component"""
|
|
||||||
M_PACKAGE_INITIALS: TypenameMangling.ValueType # 3
|
|
||||||
"""Replace the package name by the initials"""
|
|
||||||
global___TypenameMangling = TypenameMangling
|
|
||||||
|
|
||||||
class _DescriptorSize:
|
|
||||||
ValueType = typing.NewType("ValueType", builtins.int)
|
|
||||||
V: typing_extensions.TypeAlias = ValueType
|
|
||||||
|
|
||||||
class _DescriptorSizeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_DescriptorSize.ValueType], builtins.type):
|
|
||||||
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
|
|
||||||
DS_AUTO: _DescriptorSize.ValueType # 0
|
|
||||||
"""Select minimal size based on field type"""
|
|
||||||
DS_1: _DescriptorSize.ValueType # 1
|
|
||||||
"""1 word; up to 15 byte fields, no arrays"""
|
|
||||||
DS_2: _DescriptorSize.ValueType # 2
|
|
||||||
"""2 words; up to 4095 byte fields, 4095 entry arrays"""
|
|
||||||
DS_4: _DescriptorSize.ValueType # 4
|
|
||||||
"""4 words; up to 2^32-1 byte fields, 2^16-1 entry arrays"""
|
|
||||||
DS_8: _DescriptorSize.ValueType # 8
|
|
||||||
"""8 words; up to 2^32-1 entry arrays"""
|
|
||||||
|
|
||||||
class DescriptorSize(_DescriptorSize, metaclass=_DescriptorSizeEnumTypeWrapper): ...
|
|
||||||
|
|
||||||
DS_AUTO: DescriptorSize.ValueType # 0
|
|
||||||
"""Select minimal size based on field type"""
|
|
||||||
DS_1: DescriptorSize.ValueType # 1
|
|
||||||
"""1 word; up to 15 byte fields, no arrays"""
|
|
||||||
DS_2: DescriptorSize.ValueType # 2
|
|
||||||
"""2 words; up to 4095 byte fields, 4095 entry arrays"""
|
|
||||||
DS_4: DescriptorSize.ValueType # 4
|
|
||||||
"""4 words; up to 2^32-1 byte fields, 2^16-1 entry arrays"""
|
|
||||||
DS_8: DescriptorSize.ValueType # 8
|
|
||||||
"""8 words; up to 2^32-1 entry arrays"""
|
|
||||||
global___DescriptorSize = DescriptorSize
|
|
||||||
|
|
||||||
@typing.final
|
|
||||||
class NanoPBOptions(google.protobuf.message.Message):
|
|
||||||
"""This is the inner options message, which basically defines options for
|
|
||||||
a field. When it is used in message or file scope, it applies to all
|
|
||||||
fields.
|
|
||||||
"""
|
|
||||||
|
|
||||||
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
|
||||||
|
|
||||||
MAX_SIZE_FIELD_NUMBER: builtins.int
|
|
||||||
MAX_LENGTH_FIELD_NUMBER: builtins.int
|
|
||||||
MAX_COUNT_FIELD_NUMBER: builtins.int
|
|
||||||
INT_SIZE_FIELD_NUMBER: builtins.int
|
|
||||||
TYPE_FIELD_NUMBER: builtins.int
|
|
||||||
LONG_NAMES_FIELD_NUMBER: builtins.int
|
|
||||||
PACKED_STRUCT_FIELD_NUMBER: builtins.int
|
|
||||||
PACKED_ENUM_FIELD_NUMBER: builtins.int
|
|
||||||
SKIP_MESSAGE_FIELD_NUMBER: builtins.int
|
|
||||||
NO_UNIONS_FIELD_NUMBER: builtins.int
|
|
||||||
MSGID_FIELD_NUMBER: builtins.int
|
|
||||||
ANONYMOUS_ONEOF_FIELD_NUMBER: builtins.int
|
|
||||||
PROTO3_FIELD_NUMBER: builtins.int
|
|
||||||
PROTO3_SINGULAR_MSGS_FIELD_NUMBER: builtins.int
|
|
||||||
ENUM_TO_STRING_FIELD_NUMBER: builtins.int
|
|
||||||
FIXED_LENGTH_FIELD_NUMBER: builtins.int
|
|
||||||
FIXED_COUNT_FIELD_NUMBER: builtins.int
|
|
||||||
SUBMSG_CALLBACK_FIELD_NUMBER: builtins.int
|
|
||||||
MANGLE_NAMES_FIELD_NUMBER: builtins.int
|
|
||||||
CALLBACK_DATATYPE_FIELD_NUMBER: builtins.int
|
|
||||||
CALLBACK_FUNCTION_FIELD_NUMBER: builtins.int
|
|
||||||
DESCRIPTORSIZE_FIELD_NUMBER: builtins.int
|
|
||||||
DEFAULT_HAS_FIELD_NUMBER: builtins.int
|
|
||||||
INCLUDE_FIELD_NUMBER: builtins.int
|
|
||||||
EXCLUDE_FIELD_NUMBER: builtins.int
|
|
||||||
PACKAGE_FIELD_NUMBER: builtins.int
|
|
||||||
TYPE_OVERRIDE_FIELD_NUMBER: builtins.int
|
|
||||||
SORT_BY_TAG_FIELD_NUMBER: builtins.int
|
|
||||||
FALLBACK_TYPE_FIELD_NUMBER: builtins.int
|
|
||||||
max_size: builtins.int
|
|
||||||
"""Allocated size for 'bytes' and 'string' fields.
|
|
||||||
For string fields, this should include the space for null terminator.
|
|
||||||
"""
|
|
||||||
max_length: builtins.int
|
|
||||||
"""Maximum length for 'string' fields. Setting this is equivalent
|
|
||||||
to setting max_size to a value of length+1.
|
|
||||||
"""
|
|
||||||
max_count: builtins.int
|
|
||||||
"""Allocated number of entries in arrays ('repeated' fields)"""
|
|
||||||
int_size: global___IntSize.ValueType
|
|
||||||
"""Size of integer fields. Can save some memory if you don't need
|
|
||||||
full 32 bits for the value.
|
|
||||||
"""
|
|
||||||
type: global___FieldType.ValueType
|
|
||||||
"""Force type of field (callback or static allocation)"""
|
|
||||||
long_names: builtins.bool
|
|
||||||
"""Use long names for enums, i.e. EnumName_EnumValue."""
|
|
||||||
packed_struct: builtins.bool
|
|
||||||
"""Add 'packed' attribute to generated structs.
|
|
||||||
Note: this cannot be used on CPUs that break on unaligned
|
|
||||||
accesses to variables.
|
|
||||||
"""
|
|
||||||
packed_enum: builtins.bool
|
|
||||||
"""Add 'packed' attribute to generated enums."""
|
|
||||||
skip_message: builtins.bool
|
|
||||||
"""Skip this message"""
|
|
||||||
no_unions: builtins.bool
|
|
||||||
"""Generate oneof fields as normal optional fields instead of union."""
|
|
||||||
msgid: builtins.int
|
|
||||||
"""integer type tag for a message"""
|
|
||||||
anonymous_oneof: builtins.bool
|
|
||||||
"""decode oneof as anonymous union"""
|
|
||||||
proto3: builtins.bool
|
|
||||||
"""Proto3 singular field does not generate a "has_" flag"""
|
|
||||||
proto3_singular_msgs: builtins.bool
|
|
||||||
"""Force proto3 messages to have no "has_" flag.
|
|
||||||
This was default behavior until nanopb-0.4.0.
|
|
||||||
"""
|
|
||||||
enum_to_string: builtins.bool
|
|
||||||
"""Generate an enum->string mapping function (can take up lots of space)."""
|
|
||||||
fixed_length: builtins.bool
|
|
||||||
"""Generate bytes arrays with fixed length"""
|
|
||||||
fixed_count: builtins.bool
|
|
||||||
"""Generate repeated field with fixed count"""
|
|
||||||
submsg_callback: builtins.bool
|
|
||||||
"""Generate message-level callback that is called before decoding submessages.
|
|
||||||
This can be used to set callback fields for submsgs inside oneofs.
|
|
||||||
"""
|
|
||||||
mangle_names: global___TypenameMangling.ValueType
|
|
||||||
"""Shorten or remove package names from type names.
|
|
||||||
This option applies only on the file level.
|
|
||||||
"""
|
|
||||||
callback_datatype: builtins.str
|
|
||||||
"""Data type for storage associated with callback fields."""
|
|
||||||
callback_function: builtins.str
|
|
||||||
"""Callback function used for encoding and decoding.
|
|
||||||
Prior to nanopb-0.4.0, the callback was specified in per-field pb_callback_t
|
|
||||||
structure. This is still supported, but does not work inside e.g. oneof or pointer
|
|
||||||
fields. Instead, a new method allows specifying a per-message callback that
|
|
||||||
will be called for all callback fields in a message type.
|
|
||||||
"""
|
|
||||||
descriptorsize: global___DescriptorSize.ValueType
|
|
||||||
"""Select the size of field descriptors. This option has to be defined
|
|
||||||
for the whole message, not per-field. Usually automatic selection is
|
|
||||||
ok, but if it results in compilation errors you can increase the field
|
|
||||||
size here.
|
|
||||||
"""
|
|
||||||
default_has: builtins.bool
|
|
||||||
"""Set default value for has_ fields."""
|
|
||||||
package: builtins.str
|
|
||||||
"""Package name that applies only for nanopb."""
|
|
||||||
type_override: google.protobuf.descriptor_pb2.FieldDescriptorProto.Type.ValueType
|
|
||||||
"""Override type of the field in generated C code. Only to be used with related field types"""
|
|
||||||
sort_by_tag: builtins.bool
|
|
||||||
"""Due to historical reasons, nanopb orders fields in structs by their tag number
|
|
||||||
instead of the order in .proto. Set this to false to keep the .proto order.
|
|
||||||
The default value will probably change to false in nanopb-0.5.0.
|
|
||||||
"""
|
|
||||||
fallback_type: global___FieldType.ValueType
|
|
||||||
"""Set the FT_DEFAULT field conversion strategy.
|
|
||||||
A field that can become a static member of a c struct (e.g. int, bool, etc)
|
|
||||||
will be a a static field.
|
|
||||||
Fields with dynamic length are converted to either a pointer or a callback.
|
|
||||||
"""
|
|
||||||
@property
|
|
||||||
def include(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]:
|
|
||||||
"""Extra files to include in generated `.pb.h`"""
|
|
||||||
|
|
||||||
@property
|
|
||||||
def exclude(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]:
|
|
||||||
"""Automatic includes to exclude from generated `.pb.h`
|
|
||||||
Same as nanopb_generator.py command line flag -x.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
*,
|
|
||||||
max_size: builtins.int | None = ...,
|
|
||||||
max_length: builtins.int | None = ...,
|
|
||||||
max_count: builtins.int | None = ...,
|
|
||||||
int_size: global___IntSize.ValueType | None = ...,
|
|
||||||
type: global___FieldType.ValueType | None = ...,
|
|
||||||
long_names: builtins.bool | None = ...,
|
|
||||||
packed_struct: builtins.bool | None = ...,
|
|
||||||
packed_enum: builtins.bool | None = ...,
|
|
||||||
skip_message: builtins.bool | None = ...,
|
|
||||||
no_unions: builtins.bool | None = ...,
|
|
||||||
msgid: builtins.int | None = ...,
|
|
||||||
anonymous_oneof: builtins.bool | None = ...,
|
|
||||||
proto3: builtins.bool | None = ...,
|
|
||||||
proto3_singular_msgs: builtins.bool | None = ...,
|
|
||||||
enum_to_string: builtins.bool | None = ...,
|
|
||||||
fixed_length: builtins.bool | None = ...,
|
|
||||||
fixed_count: builtins.bool | None = ...,
|
|
||||||
submsg_callback: builtins.bool | None = ...,
|
|
||||||
mangle_names: global___TypenameMangling.ValueType | None = ...,
|
|
||||||
callback_datatype: builtins.str | None = ...,
|
|
||||||
callback_function: builtins.str | None = ...,
|
|
||||||
descriptorsize: global___DescriptorSize.ValueType | None = ...,
|
|
||||||
default_has: builtins.bool | None = ...,
|
|
||||||
include: collections.abc.Iterable[builtins.str] | None = ...,
|
|
||||||
exclude: collections.abc.Iterable[builtins.str] | None = ...,
|
|
||||||
package: builtins.str | None = ...,
|
|
||||||
type_override: google.protobuf.descriptor_pb2.FieldDescriptorProto.Type.ValueType | None = ...,
|
|
||||||
sort_by_tag: builtins.bool | None = ...,
|
|
||||||
fallback_type: global___FieldType.ValueType | None = ...,
|
|
||||||
) -> None: ...
|
|
||||||
def HasField(self, field_name: typing.Literal["anonymous_oneof", b"anonymous_oneof", "callback_datatype", b"callback_datatype", "callback_function", b"callback_function", "default_has", b"default_has", "descriptorsize", b"descriptorsize", "enum_to_string", b"enum_to_string", "fallback_type", b"fallback_type", "fixed_count", b"fixed_count", "fixed_length", b"fixed_length", "int_size", b"int_size", "long_names", b"long_names", "mangle_names", b"mangle_names", "max_count", b"max_count", "max_length", b"max_length", "max_size", b"max_size", "msgid", b"msgid", "no_unions", b"no_unions", "package", b"package", "packed_enum", b"packed_enum", "packed_struct", b"packed_struct", "proto3", b"proto3", "proto3_singular_msgs", b"proto3_singular_msgs", "skip_message", b"skip_message", "sort_by_tag", b"sort_by_tag", "submsg_callback", b"submsg_callback", "type", b"type", "type_override", b"type_override"]) -> builtins.bool: ...
|
|
||||||
def ClearField(self, field_name: typing.Literal["anonymous_oneof", b"anonymous_oneof", "callback_datatype", b"callback_datatype", "callback_function", b"callback_function", "default_has", b"default_has", "descriptorsize", b"descriptorsize", "enum_to_string", b"enum_to_string", "exclude", b"exclude", "fallback_type", b"fallback_type", "fixed_count", b"fixed_count", "fixed_length", b"fixed_length", "include", b"include", "int_size", b"int_size", "long_names", b"long_names", "mangle_names", b"mangle_names", "max_count", b"max_count", "max_length", b"max_length", "max_size", b"max_size", "msgid", b"msgid", "no_unions", b"no_unions", "package", b"package", "packed_enum", b"packed_enum", "packed_struct", b"packed_struct", "proto3", b"proto3", "proto3_singular_msgs", b"proto3_singular_msgs", "skip_message", b"skip_message", "sort_by_tag", b"sort_by_tag", "submsg_callback", b"submsg_callback", "type", b"type", "type_override", b"type_override"]) -> None: ...
|
|
||||||
|
|
||||||
global___NanoPBOptions = NanoPBOptions
|
|
||||||
|
|
||||||
NANOPB_FILEOPT_FIELD_NUMBER: builtins.int
|
|
||||||
NANOPB_MSGOPT_FIELD_NUMBER: builtins.int
|
|
||||||
NANOPB_ENUMOPT_FIELD_NUMBER: builtins.int
|
|
||||||
NANOPB_FIELD_NUMBER: builtins.int
|
|
||||||
nanopb_fileopt: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[google.protobuf.descriptor_pb2.FileOptions, global___NanoPBOptions]
|
|
||||||
nanopb_msgopt: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[google.protobuf.descriptor_pb2.MessageOptions, global___NanoPBOptions]
|
|
||||||
nanopb_enumopt: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[google.protobuf.descriptor_pb2.EnumOptions, global___NanoPBOptions]
|
|
||||||
nanopb: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[google.protobuf.descriptor_pb2.FieldOptions, global___NanoPBOptions]
|
|
||||||
4
meshtastic/protobuf/paxcount_pb2.py
generated
4
meshtastic/protobuf/paxcount_pb2.py
generated
@@ -13,14 +13,14 @@ _sym_db = _symbol_database.Default()
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\"meshtastic/protobuf/paxcount.proto\x12\x13meshtastic.protobuf\"5\n\x08Paxcount\x12\x0c\n\x04wifi\x18\x01 \x01(\r\x12\x0b\n\x03\x62le\x18\x02 \x01(\r\x12\x0e\n\x06uptime\x18\x03 \x01(\rBd\n\x14org.meshtastic.protoB\x0ePaxcountProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
|
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\"meshtastic/protobuf/paxcount.proto\x12\x13meshtastic.protobuf\"5\n\x08Paxcount\x12\x0c\n\x04wifi\x18\x01 \x01(\r\x12\x0b\n\x03\x62le\x18\x02 \x01(\r\x12\x0e\n\x06uptime\x18\x03 \x01(\rBc\n\x13\x63om.geeksville.meshB\x0ePaxcountProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
|
||||||
|
|
||||||
_globals = globals()
|
_globals = globals()
|
||||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.paxcount_pb2', _globals)
|
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.paxcount_pb2', _globals)
|
||||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||||
DESCRIPTOR._options = None
|
DESCRIPTOR._options = None
|
||||||
DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\016PaxcountProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\016PaxcountProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||||
_globals['_PAXCOUNT']._serialized_start=59
|
_globals['_PAXCOUNT']._serialized_start=59
|
||||||
_globals['_PAXCOUNT']._serialized_end=112
|
_globals['_PAXCOUNT']._serialized_end=112
|
||||||
# @@protoc_insertion_point(module_scope)
|
# @@protoc_insertion_point(module_scope)
|
||||||
|
|||||||
6
meshtastic/protobuf/portnums_pb2.py
generated
6
meshtastic/protobuf/portnums_pb2.py
generated
@@ -13,14 +13,14 @@ _sym_db = _symbol_database.Default()
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\"meshtastic/protobuf/portnums.proto\x12\x13meshtastic.protobuf*\xfd\x05\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\x18\n\x14KEY_VERIFICATION_APP\x10\x0c\x12\x14\n\x10REMOTE_SHELL_APP\x10\r\x12\r\n\tREPLY_APP\x10 \x12\x11\n\rIP_TUNNEL_APP\x10!\x12\x12\n\x0ePAXCOUNTER_APP\x10\"\x12\x1e\n\x1aSTORE_FORWARD_PLUSPLUS_APP\x10#\x12\x13\n\x0fNODE_STATUS_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\x12\n\x0eLORAWAN_BRIDGE\x10K\x12\x18\n\x14RETICULUM_TUNNEL_APP\x10L\x12\x0f\n\x0b\x43\x41YENNE_APP\x10M\x12\x12\n\x0e\x41TAK_PLUGIN_V2\x10N\x12\x12\n\x0eGROUPALARM_APP\x10p\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\x14org.meshtastic.protoB\x08PortnumsZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
|
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\"meshtastic/protobuf/portnums.proto\x12\x13meshtastic.protobuf*\xb1\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\x10\n\x0bPRIVATE_APP\x10\x80\x02\x12\x13\n\x0e\x41TAK_FORWARDER\x10\x81\x02\x12\x08\n\x03MAX\x10\xff\x03\x42]\n\x13\x63om.geeksville.meshB\x08PortnumsZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
|
||||||
|
|
||||||
_globals = globals()
|
_globals = globals()
|
||||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.portnums_pb2', _globals)
|
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.portnums_pb2', _globals)
|
||||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||||
DESCRIPTOR._options = None
|
DESCRIPTOR._options = None
|
||||||
DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\010PortnumsZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\010PortnumsZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||||
_globals['_PORTNUM']._serialized_start=60
|
_globals['_PORTNUM']._serialized_start=60
|
||||||
_globals['_PORTNUM']._serialized_end=825
|
_globals['_PORTNUM']._serialized_end=621
|
||||||
# @@protoc_insertion_point(module_scope)
|
# @@protoc_insertion_point(module_scope)
|
||||||
|
|||||||
100
meshtastic/protobuf/portnums_pb2.pyi
generated
100
meshtastic/protobuf/portnums_pb2.pyi
generated
@@ -97,14 +97,6 @@ class _PortNumEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTy
|
|||||||
"""
|
"""
|
||||||
Same as Text Message but used for critical alerts.
|
Same as Text Message but used for critical alerts.
|
||||||
"""
|
"""
|
||||||
KEY_VERIFICATION_APP: _PortNum.ValueType # 12
|
|
||||||
"""
|
|
||||||
Module/port for handling key verification requests.
|
|
||||||
"""
|
|
||||||
REMOTE_SHELL_APP: _PortNum.ValueType # 13
|
|
||||||
"""
|
|
||||||
Module/port for handling primitive remote shell access.
|
|
||||||
"""
|
|
||||||
REPLY_APP: _PortNum.ValueType # 32
|
REPLY_APP: _PortNum.ValueType # 32
|
||||||
"""
|
"""
|
||||||
Provides a 'ping' service that replies to any packet it receives.
|
Provides a 'ping' service that replies to any packet it receives.
|
||||||
@@ -121,20 +113,6 @@ class _PortNumEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTy
|
|||||||
Paxcounter lib included in the firmware
|
Paxcounter lib included in the firmware
|
||||||
ENCODING: protobuf
|
ENCODING: protobuf
|
||||||
"""
|
"""
|
||||||
STORE_FORWARD_PLUSPLUS_APP: _PortNum.ValueType # 35
|
|
||||||
"""
|
|
||||||
Store and Forward++ module included in the firmware
|
|
||||||
ENCODING: protobuf
|
|
||||||
This module is specifically for Native Linux nodes, and provides a Git-style
|
|
||||||
chain of messages.
|
|
||||||
"""
|
|
||||||
NODE_STATUS_APP: _PortNum.ValueType # 36
|
|
||||||
"""
|
|
||||||
Node Status module
|
|
||||||
ENCODING: protobuf
|
|
||||||
This module allows setting an extra string of status for a node.
|
|
||||||
Broadcasts on change and on a timer, possibly once a day.
|
|
||||||
"""
|
|
||||||
SERIAL_APP: _PortNum.ValueType # 64
|
SERIAL_APP: _PortNum.ValueType # 64
|
||||||
"""
|
"""
|
||||||
Provides a hardware serial interface to send and receive from the Meshtastic network.
|
Provides a hardware serial interface to send and receive from the Meshtastic network.
|
||||||
@@ -201,34 +179,6 @@ class _PortNumEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTy
|
|||||||
"""
|
"""
|
||||||
PowerStress based monitoring support (for automated power consumption testing)
|
PowerStress based monitoring support (for automated power consumption testing)
|
||||||
"""
|
"""
|
||||||
LORAWAN_BRIDGE: _PortNum.ValueType # 75
|
|
||||||
"""
|
|
||||||
LoraWAN Payload Transport
|
|
||||||
ENCODING: compact binary LoRaWAN uplink (10-byte RF metadata + PHY payload) - see LoRaWANBridgeModule
|
|
||||||
"""
|
|
||||||
RETICULUM_TUNNEL_APP: _PortNum.ValueType # 76
|
|
||||||
"""
|
|
||||||
Reticulum Network Stack Tunnel App
|
|
||||||
ENCODING: Fragmented RNS Packet. Handled by Meshtastic RNS interface
|
|
||||||
"""
|
|
||||||
CAYENNE_APP: _PortNum.ValueType # 77
|
|
||||||
"""
|
|
||||||
App for transporting Cayenne Low Power Payload, popular for LoRaWAN sensor nodes. Offers ability to send
|
|
||||||
arbitrary telemetry over meshtastic that is not covered by telemetry.proto
|
|
||||||
ENCODING: CayenneLLP
|
|
||||||
"""
|
|
||||||
ATAK_PLUGIN_V2: _PortNum.ValueType # 78
|
|
||||||
"""
|
|
||||||
ATAK Plugin V2
|
|
||||||
Portnum for payloads from the official Meshtastic ATAK plugin using
|
|
||||||
TAKPacketV2 with zstd dictionary compression.
|
|
||||||
"""
|
|
||||||
GROUPALARM_APP: _PortNum.ValueType # 112
|
|
||||||
"""
|
|
||||||
GroupAlarm integration
|
|
||||||
Used for transporting GroupAlarm-related messages between Meshtastic nodes
|
|
||||||
and companion applications/services.
|
|
||||||
"""
|
|
||||||
PRIVATE_APP: _PortNum.ValueType # 256
|
PRIVATE_APP: _PortNum.ValueType # 256
|
||||||
"""
|
"""
|
||||||
Private applications should use portnums >= 256.
|
Private applications should use portnums >= 256.
|
||||||
@@ -336,14 +286,6 @@ ALERT_APP: PortNum.ValueType # 11
|
|||||||
"""
|
"""
|
||||||
Same as Text Message but used for critical alerts.
|
Same as Text Message but used for critical alerts.
|
||||||
"""
|
"""
|
||||||
KEY_VERIFICATION_APP: PortNum.ValueType # 12
|
|
||||||
"""
|
|
||||||
Module/port for handling key verification requests.
|
|
||||||
"""
|
|
||||||
REMOTE_SHELL_APP: PortNum.ValueType # 13
|
|
||||||
"""
|
|
||||||
Module/port for handling primitive remote shell access.
|
|
||||||
"""
|
|
||||||
REPLY_APP: PortNum.ValueType # 32
|
REPLY_APP: PortNum.ValueType # 32
|
||||||
"""
|
"""
|
||||||
Provides a 'ping' service that replies to any packet it receives.
|
Provides a 'ping' service that replies to any packet it receives.
|
||||||
@@ -360,20 +302,6 @@ PAXCOUNTER_APP: PortNum.ValueType # 34
|
|||||||
Paxcounter lib included in the firmware
|
Paxcounter lib included in the firmware
|
||||||
ENCODING: protobuf
|
ENCODING: protobuf
|
||||||
"""
|
"""
|
||||||
STORE_FORWARD_PLUSPLUS_APP: PortNum.ValueType # 35
|
|
||||||
"""
|
|
||||||
Store and Forward++ module included in the firmware
|
|
||||||
ENCODING: protobuf
|
|
||||||
This module is specifically for Native Linux nodes, and provides a Git-style
|
|
||||||
chain of messages.
|
|
||||||
"""
|
|
||||||
NODE_STATUS_APP: PortNum.ValueType # 36
|
|
||||||
"""
|
|
||||||
Node Status module
|
|
||||||
ENCODING: protobuf
|
|
||||||
This module allows setting an extra string of status for a node.
|
|
||||||
Broadcasts on change and on a timer, possibly once a day.
|
|
||||||
"""
|
|
||||||
SERIAL_APP: PortNum.ValueType # 64
|
SERIAL_APP: PortNum.ValueType # 64
|
||||||
"""
|
"""
|
||||||
Provides a hardware serial interface to send and receive from the Meshtastic network.
|
Provides a hardware serial interface to send and receive from the Meshtastic network.
|
||||||
@@ -440,34 +368,6 @@ POWERSTRESS_APP: PortNum.ValueType # 74
|
|||||||
"""
|
"""
|
||||||
PowerStress based monitoring support (for automated power consumption testing)
|
PowerStress based monitoring support (for automated power consumption testing)
|
||||||
"""
|
"""
|
||||||
LORAWAN_BRIDGE: PortNum.ValueType # 75
|
|
||||||
"""
|
|
||||||
LoraWAN Payload Transport
|
|
||||||
ENCODING: compact binary LoRaWAN uplink (10-byte RF metadata + PHY payload) - see LoRaWANBridgeModule
|
|
||||||
"""
|
|
||||||
RETICULUM_TUNNEL_APP: PortNum.ValueType # 76
|
|
||||||
"""
|
|
||||||
Reticulum Network Stack Tunnel App
|
|
||||||
ENCODING: Fragmented RNS Packet. Handled by Meshtastic RNS interface
|
|
||||||
"""
|
|
||||||
CAYENNE_APP: PortNum.ValueType # 77
|
|
||||||
"""
|
|
||||||
App for transporting Cayenne Low Power Payload, popular for LoRaWAN sensor nodes. Offers ability to send
|
|
||||||
arbitrary telemetry over meshtastic that is not covered by telemetry.proto
|
|
||||||
ENCODING: CayenneLLP
|
|
||||||
"""
|
|
||||||
ATAK_PLUGIN_V2: PortNum.ValueType # 78
|
|
||||||
"""
|
|
||||||
ATAK Plugin V2
|
|
||||||
Portnum for payloads from the official Meshtastic ATAK plugin using
|
|
||||||
TAKPacketV2 with zstd dictionary compression.
|
|
||||||
"""
|
|
||||||
GROUPALARM_APP: PortNum.ValueType # 112
|
|
||||||
"""
|
|
||||||
GroupAlarm integration
|
|
||||||
Used for transporting GroupAlarm-related messages between Meshtastic nodes
|
|
||||||
and companion applications/services.
|
|
||||||
"""
|
|
||||||
PRIVATE_APP: PortNum.ValueType # 256
|
PRIVATE_APP: PortNum.ValueType # 256
|
||||||
"""
|
"""
|
||||||
Private applications should use portnums >= 256.
|
Private applications should use portnums >= 256.
|
||||||
|
|||||||
4
meshtastic/protobuf/powermon_pb2.py
generated
4
meshtastic/protobuf/powermon_pb2.py
generated
@@ -13,14 +13,14 @@ _sym_db = _symbol_database.Default()
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\"meshtastic/protobuf/powermon.proto\x12\x13meshtastic.protobuf\"\xe0\x01\n\x08PowerMon\"\xd3\x01\n\x05State\x12\x08\n\x04None\x10\x00\x12\x11\n\rCPU_DeepSleep\x10\x01\x12\x12\n\x0e\x43PU_LightSleep\x10\x02\x12\x0c\n\x08Vext1_On\x10\x04\x12\r\n\tLora_RXOn\x10\x08\x12\r\n\tLora_TXOn\x10\x10\x12\x11\n\rLora_RXActive\x10 \x12\t\n\x05\x42T_On\x10@\x12\x0b\n\x06LED_On\x10\x80\x01\x12\x0e\n\tScreen_On\x10\x80\x02\x12\x13\n\x0eScreen_Drawing\x10\x80\x04\x12\x0c\n\x07Wifi_On\x10\x80\x08\x12\x0f\n\nGPS_Active\x10\x80\x10\"\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\x10qBd\n\x14org.meshtastic.protoB\x0ePowerMonProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
|
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')
|
||||||
|
|
||||||
_globals = globals()
|
_globals = globals()
|
||||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.powermon_pb2', _globals)
|
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.powermon_pb2', _globals)
|
||||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||||
DESCRIPTOR._options = None
|
DESCRIPTOR._options = None
|
||||||
DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\016PowerMonProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\016PowerMonProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||||
_globals['_POWERMON']._serialized_start=60
|
_globals['_POWERMON']._serialized_start=60
|
||||||
_globals['_POWERMON']._serialized_end=284
|
_globals['_POWERMON']._serialized_end=284
|
||||||
_globals['_POWERMON_STATE']._serialized_start=73
|
_globals['_POWERMON_STATE']._serialized_start=73
|
||||||
|
|||||||
4
meshtastic/protobuf/remote_hardware_pb2.py
generated
4
meshtastic/protobuf/remote_hardware_pb2.py
generated
@@ -13,14 +13,14 @@ _sym_db = _symbol_database.Default()
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n)meshtastic/protobuf/remote_hardware.proto\x12\x13meshtastic.protobuf\"\xdf\x01\n\x0fHardwareMessage\x12\x37\n\x04type\x18\x01 \x01(\x0e\x32).meshtastic.protobuf.HardwareMessage.Type\x12\x11\n\tgpio_mask\x18\x02 \x01(\x04\x12\x12\n\ngpio_value\x18\x03 \x01(\x04\"l\n\x04Type\x12\t\n\x05UNSET\x10\x00\x12\x0f\n\x0bWRITE_GPIOS\x10\x01\x12\x0f\n\x0bWATCH_GPIOS\x10\x02\x12\x11\n\rGPIOS_CHANGED\x10\x03\x12\x0e\n\nREAD_GPIOS\x10\x04\x12\x14\n\x10READ_GPIOS_REPLY\x10\x05\x42\x64\n\x14org.meshtastic.protoB\x0eRemoteHardwareZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
|
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n)meshtastic/protobuf/remote_hardware.proto\x12\x13meshtastic.protobuf\"\xdf\x01\n\x0fHardwareMessage\x12\x37\n\x04type\x18\x01 \x01(\x0e\x32).meshtastic.protobuf.HardwareMessage.Type\x12\x11\n\tgpio_mask\x18\x02 \x01(\x04\x12\x12\n\ngpio_value\x18\x03 \x01(\x04\"l\n\x04Type\x12\t\n\x05UNSET\x10\x00\x12\x0f\n\x0bWRITE_GPIOS\x10\x01\x12\x0f\n\x0bWATCH_GPIOS\x10\x02\x12\x11\n\rGPIOS_CHANGED\x10\x03\x12\x0e\n\nREAD_GPIOS\x10\x04\x12\x14\n\x10READ_GPIOS_REPLY\x10\x05\x42\x63\n\x13\x63om.geeksville.meshB\x0eRemoteHardwareZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
|
||||||
|
|
||||||
_globals = globals()
|
_globals = globals()
|
||||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.remote_hardware_pb2', _globals)
|
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.remote_hardware_pb2', _globals)
|
||||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||||
DESCRIPTOR._options = None
|
DESCRIPTOR._options = None
|
||||||
DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\016RemoteHardwareZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\016RemoteHardwareZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||||
_globals['_HARDWAREMESSAGE']._serialized_start=67
|
_globals['_HARDWAREMESSAGE']._serialized_start=67
|
||||||
_globals['_HARDWAREMESSAGE']._serialized_end=290
|
_globals['_HARDWAREMESSAGE']._serialized_end=290
|
||||||
_globals['_HARDWAREMESSAGE_TYPE']._serialized_start=182
|
_globals['_HARDWAREMESSAGE_TYPE']._serialized_start=182
|
||||||
|
|||||||
11
meshtastic/protobuf/rtttl_pb2.py
generated
11
meshtastic/protobuf/rtttl_pb2.py
generated
@@ -11,19 +11,16 @@ from google.protobuf.internal import builder as _builder
|
|||||||
_sym_db = _symbol_database.Default()
|
_sym_db = _symbol_database.Default()
|
||||||
|
|
||||||
|
|
||||||
from meshtastic.protobuf import nanopb_pb2 as meshtastic_dot_protobuf_dot_nanopb__pb2
|
|
||||||
|
|
||||||
|
|
||||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1fmeshtastic/protobuf/rtttl.proto\x12\x13meshtastic.protobuf\x1a meshtastic/protobuf/nanopb.proto\"\'\n\x0bRTTTLConfig\x12\x18\n\x08ringtone\x18\x01 \x01(\tB\x06\x92?\x03\x08\xe7\x01\x42g\n\x14org.meshtastic.protoB\x11RTTTLConfigProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
|
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1fmeshtastic/protobuf/rtttl.proto\x12\x13meshtastic.protobuf\"\x1f\n\x0bRTTTLConfig\x12\x10\n\x08ringtone\x18\x01 \x01(\tBf\n\x13\x63om.geeksville.meshB\x11RTTTLConfigProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
|
||||||
|
|
||||||
_globals = globals()
|
_globals = globals()
|
||||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.rtttl_pb2', _globals)
|
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.rtttl_pb2', _globals)
|
||||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||||
DESCRIPTOR._options = None
|
DESCRIPTOR._options = None
|
||||||
DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\021RTTTLConfigProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\021RTTTLConfigProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||||
_RTTTLCONFIG.fields_by_name['ringtone']._options = None
|
_globals['_RTTTLCONFIG']._serialized_start=56
|
||||||
_RTTTLCONFIG.fields_by_name['ringtone']._serialized_options = b'\222?\003\010\347\001'
|
_globals['_RTTTLCONFIG']._serialized_end=87
|
||||||
_globals['_RTTTLCONFIG']._serialized_start=90
|
|
||||||
_globals['_RTTTLCONFIG']._serialized_end=129
|
|
||||||
# @@protoc_insertion_point(module_scope)
|
# @@protoc_insertion_point(module_scope)
|
||||||
|
|||||||
39
meshtastic/protobuf/serial_hal_pb2.py
generated
39
meshtastic/protobuf/serial_hal_pb2.py
generated
@@ -1,39 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
||||||
# source: meshtastic/protobuf/serial_hal.proto
|
|
||||||
"""Generated protocol buffer code."""
|
|
||||||
from google.protobuf import descriptor as _descriptor
|
|
||||||
from google.protobuf import descriptor_pool as _descriptor_pool
|
|
||||||
from google.protobuf import symbol_database as _symbol_database
|
|
||||||
from google.protobuf.internal import builder as _builder
|
|
||||||
# @@protoc_insertion_point(imports)
|
|
||||||
|
|
||||||
_sym_db = _symbol_database.Default()
|
|
||||||
|
|
||||||
|
|
||||||
from meshtastic.protobuf import nanopb_pb2 as meshtastic_dot_protobuf_dot_nanopb__pb2
|
|
||||||
|
|
||||||
|
|
||||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n$meshtastic/protobuf/serial_hal.proto\x12\x13meshtastic.protobuf\x1a meshtastic/protobuf/nanopb.proto\"\xb3\x02\n\x10SerialHalCommand\x12\x16\n\x0etransaction_id\x18\x01 \x01(\r\x12\x38\n\x04type\x18\x02 \x01(\x0e\x32*.meshtastic.protobuf.SerialHalCommand.Type\x12\x0b\n\x03pin\x18\x03 \x01(\r\x12\r\n\x05value\x18\x04 \x01(\r\x12\x0c\n\x04mode\x18\x05 \x01(\r\x12\x14\n\x04\x64\x61ta\x18\x06 \x01(\x0c\x42\x06\x92?\x03\x08\x80\x04\"\x8c\x01\n\x04Type\x12\t\n\x05UNSET\x10\x00\x12\x0c\n\x08PIN_MODE\x10\x01\x12\x11\n\rDIGITAL_WRITE\x10\x02\x12\x10\n\x0c\x44IGITAL_READ\x10\x03\x12\x14\n\x10\x41TTACH_INTERRUPT\x10\x04\x12\x14\n\x10\x44\x45TACH_INTERRUPT\x10\x05\x12\x10\n\x0cSPI_TRANSFER\x10\x06\x12\x08\n\x04NOOP\x10\x07\"\xe4\x01\n\x11SerialHalResponse\x12\x16\n\x0etransaction_id\x18\x01 \x01(\r\x12=\n\x06result\x18\x02 \x01(\x0e\x32-.meshtastic.protobuf.SerialHalResponse.Result\x12\r\n\x05value\x18\x03 \x01(\r\x12\x14\n\x04\x64\x61ta\x18\x04 \x01(\x0c\x42\x06\x92?\x03\x08\x80\x04\x12\x14\n\x05\x65rror\x18\x05 \x01(\tB\x05\x92?\x02\x08P\"=\n\x06Result\x12\x06\n\x02OK\x10\x00\x12\t\n\x05\x45RROR\x10\x01\x12\x0f\n\x0b\x42\x41\x44_REQUEST\x10\x02\x12\x0f\n\x0bUNSUPPORTED\x10\x03\x42_\n\x14org.meshtastic.protoB\tSerialHalZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
|
|
||||||
|
|
||||||
_globals = globals()
|
|
||||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
|
||||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.serial_hal_pb2', _globals)
|
|
||||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
|
||||||
DESCRIPTOR._options = None
|
|
||||||
DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\tSerialHalZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
|
||||||
_SERIALHALCOMMAND.fields_by_name['data']._options = None
|
|
||||||
_SERIALHALCOMMAND.fields_by_name['data']._serialized_options = b'\222?\003\010\200\004'
|
|
||||||
_SERIALHALRESPONSE.fields_by_name['data']._options = None
|
|
||||||
_SERIALHALRESPONSE.fields_by_name['data']._serialized_options = b'\222?\003\010\200\004'
|
|
||||||
_SERIALHALRESPONSE.fields_by_name['error']._options = None
|
|
||||||
_SERIALHALRESPONSE.fields_by_name['error']._serialized_options = b'\222?\002\010P'
|
|
||||||
_globals['_SERIALHALCOMMAND']._serialized_start=96
|
|
||||||
_globals['_SERIALHALCOMMAND']._serialized_end=403
|
|
||||||
_globals['_SERIALHALCOMMAND_TYPE']._serialized_start=263
|
|
||||||
_globals['_SERIALHALCOMMAND_TYPE']._serialized_end=403
|
|
||||||
_globals['_SERIALHALRESPONSE']._serialized_start=406
|
|
||||||
_globals['_SERIALHALRESPONSE']._serialized_end=634
|
|
||||||
_globals['_SERIALHALRESPONSE_RESULT']._serialized_start=573
|
|
||||||
_globals['_SERIALHALRESPONSE_RESULT']._serialized_end=634
|
|
||||||
# @@protoc_insertion_point(module_scope)
|
|
||||||
140
meshtastic/protobuf/serial_hal_pb2.pyi
generated
140
meshtastic/protobuf/serial_hal_pb2.pyi
generated
@@ -1,140 +0,0 @@
|
|||||||
"""
|
|
||||||
@generated by mypy-protobuf. Do not edit manually!
|
|
||||||
isort:skip_file
|
|
||||||
"""
|
|
||||||
|
|
||||||
import builtins
|
|
||||||
import google.protobuf.descriptor
|
|
||||||
import google.protobuf.internal.enum_type_wrapper
|
|
||||||
import google.protobuf.message
|
|
||||||
import sys
|
|
||||||
import typing
|
|
||||||
|
|
||||||
if sys.version_info >= (3, 10):
|
|
||||||
import typing as typing_extensions
|
|
||||||
else:
|
|
||||||
import typing_extensions
|
|
||||||
|
|
||||||
DESCRIPTOR: google.protobuf.descriptor.FileDescriptor
|
|
||||||
|
|
||||||
@typing.final
|
|
||||||
class SerialHalCommand(google.protobuf.message.Message):
|
|
||||||
"""SerialHalCommand messages are sent from host to device over the SerialHal
|
|
||||||
framing stream. Responses normally come back as SerialHalResponse with the
|
|
||||||
same transaction_id.
|
|
||||||
|
|
||||||
Interrupt notifications are the one asynchronous exception: the device emits
|
|
||||||
an unsolicited SerialHalResponse with transaction_id == 0 and value == pin.
|
|
||||||
Host implementations should treat those frames as interrupt events rather
|
|
||||||
than replies to an outstanding request.
|
|
||||||
"""
|
|
||||||
|
|
||||||
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
|
||||||
|
|
||||||
class _Type:
|
|
||||||
ValueType = typing.NewType("ValueType", builtins.int)
|
|
||||||
V: typing_extensions.TypeAlias = ValueType
|
|
||||||
|
|
||||||
class _TypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[SerialHalCommand._Type.ValueType], builtins.type):
|
|
||||||
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
|
|
||||||
UNSET: SerialHalCommand._Type.ValueType # 0
|
|
||||||
PIN_MODE: SerialHalCommand._Type.ValueType # 1
|
|
||||||
DIGITAL_WRITE: SerialHalCommand._Type.ValueType # 2
|
|
||||||
DIGITAL_READ: SerialHalCommand._Type.ValueType # 3
|
|
||||||
ATTACH_INTERRUPT: SerialHalCommand._Type.ValueType # 4
|
|
||||||
DETACH_INTERRUPT: SerialHalCommand._Type.ValueType # 5
|
|
||||||
SPI_TRANSFER: SerialHalCommand._Type.ValueType # 6
|
|
||||||
NOOP: SerialHalCommand._Type.ValueType # 7
|
|
||||||
|
|
||||||
class Type(_Type, metaclass=_TypeEnumTypeWrapper): ...
|
|
||||||
UNSET: SerialHalCommand.Type.ValueType # 0
|
|
||||||
PIN_MODE: SerialHalCommand.Type.ValueType # 1
|
|
||||||
DIGITAL_WRITE: SerialHalCommand.Type.ValueType # 2
|
|
||||||
DIGITAL_READ: SerialHalCommand.Type.ValueType # 3
|
|
||||||
ATTACH_INTERRUPT: SerialHalCommand.Type.ValueType # 4
|
|
||||||
DETACH_INTERRUPT: SerialHalCommand.Type.ValueType # 5
|
|
||||||
SPI_TRANSFER: SerialHalCommand.Type.ValueType # 6
|
|
||||||
NOOP: SerialHalCommand.Type.ValueType # 7
|
|
||||||
|
|
||||||
TRANSACTION_ID_FIELD_NUMBER: builtins.int
|
|
||||||
TYPE_FIELD_NUMBER: builtins.int
|
|
||||||
PIN_FIELD_NUMBER: builtins.int
|
|
||||||
VALUE_FIELD_NUMBER: builtins.int
|
|
||||||
MODE_FIELD_NUMBER: builtins.int
|
|
||||||
DATA_FIELD_NUMBER: builtins.int
|
|
||||||
transaction_id: builtins.int
|
|
||||||
"""Host-assigned request id. Replies echo this id back in
|
|
||||||
SerialHalResponse.transaction_id.
|
|
||||||
"""
|
|
||||||
type: global___SerialHalCommand.Type.ValueType
|
|
||||||
pin: builtins.int
|
|
||||||
value: builtins.int
|
|
||||||
mode: builtins.int
|
|
||||||
data: builtins.bytes
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
*,
|
|
||||||
transaction_id: builtins.int = ...,
|
|
||||||
type: global___SerialHalCommand.Type.ValueType = ...,
|
|
||||||
pin: builtins.int = ...,
|
|
||||||
value: builtins.int = ...,
|
|
||||||
mode: builtins.int = ...,
|
|
||||||
data: builtins.bytes = ...,
|
|
||||||
) -> None: ...
|
|
||||||
def ClearField(self, field_name: typing.Literal["data", b"data", "mode", b"mode", "pin", b"pin", "transaction_id", b"transaction_id", "type", b"type", "value", b"value"]) -> None: ...
|
|
||||||
|
|
||||||
global___SerialHalCommand = SerialHalCommand
|
|
||||||
|
|
||||||
@typing.final
|
|
||||||
class SerialHalResponse(google.protobuf.message.Message):
|
|
||||||
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
|
||||||
|
|
||||||
class _Result:
|
|
||||||
ValueType = typing.NewType("ValueType", builtins.int)
|
|
||||||
V: typing_extensions.TypeAlias = ValueType
|
|
||||||
|
|
||||||
class _ResultEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[SerialHalResponse._Result.ValueType], builtins.type):
|
|
||||||
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
|
|
||||||
OK: SerialHalResponse._Result.ValueType # 0
|
|
||||||
ERROR: SerialHalResponse._Result.ValueType # 1
|
|
||||||
BAD_REQUEST: SerialHalResponse._Result.ValueType # 2
|
|
||||||
UNSUPPORTED: SerialHalResponse._Result.ValueType # 3
|
|
||||||
|
|
||||||
class Result(_Result, metaclass=_ResultEnumTypeWrapper): ...
|
|
||||||
OK: SerialHalResponse.Result.ValueType # 0
|
|
||||||
ERROR: SerialHalResponse.Result.ValueType # 1
|
|
||||||
BAD_REQUEST: SerialHalResponse.Result.ValueType # 2
|
|
||||||
UNSUPPORTED: SerialHalResponse.Result.ValueType # 3
|
|
||||||
|
|
||||||
TRANSACTION_ID_FIELD_NUMBER: builtins.int
|
|
||||||
RESULT_FIELD_NUMBER: builtins.int
|
|
||||||
VALUE_FIELD_NUMBER: builtins.int
|
|
||||||
DATA_FIELD_NUMBER: builtins.int
|
|
||||||
ERROR_FIELD_NUMBER: builtins.int
|
|
||||||
transaction_id: builtins.int
|
|
||||||
"""Matches the originating SerialHalCommand.transaction_id for normal
|
|
||||||
request/response traffic.
|
|
||||||
|
|
||||||
A value of 0 indicates an unsolicited interrupt notification generated by
|
|
||||||
the device. In that case, the host should interpret value as the GPIO pin
|
|
||||||
that triggered.
|
|
||||||
"""
|
|
||||||
result: global___SerialHalResponse.Result.ValueType
|
|
||||||
value: builtins.int
|
|
||||||
"""Used by DIGITAL_READ replies and interrupt notifications. For interrupt
|
|
||||||
notifications (transaction_id == 0), this carries the pin number.
|
|
||||||
"""
|
|
||||||
data: builtins.bytes
|
|
||||||
error: builtins.str
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
*,
|
|
||||||
transaction_id: builtins.int = ...,
|
|
||||||
result: global___SerialHalResponse.Result.ValueType = ...,
|
|
||||||
value: builtins.int = ...,
|
|
||||||
data: builtins.bytes = ...,
|
|
||||||
error: builtins.str = ...,
|
|
||||||
) -> None: ...
|
|
||||||
def ClearField(self, field_name: typing.Literal["data", b"data", "error", b"error", "result", b"result", "transaction_id", b"transaction_id", "value", b"value"]) -> None: ...
|
|
||||||
|
|
||||||
global___SerialHalResponse = SerialHalResponse
|
|
||||||
27
meshtastic/protobuf/storeforward_pb2.py
generated
27
meshtastic/protobuf/storeforward_pb2.py
generated
@@ -11,27 +11,24 @@ from google.protobuf.internal import builder as _builder
|
|||||||
_sym_db = _symbol_database.Default()
|
_sym_db = _symbol_database.Default()
|
||||||
|
|
||||||
|
|
||||||
from meshtastic.protobuf import nanopb_pb2 as meshtastic_dot_protobuf_dot_nanopb__pb2
|
|
||||||
|
|
||||||
|
|
||||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n&meshtastic/protobuf/storeforward.proto\x12\x13meshtastic.protobuf\x1a meshtastic/protobuf/nanopb.proto\"\xc8\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\x16\n\x04text\x18\x05 \x01(\x0c\x42\x06\x92?\x03\x08\xe9\x01H\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\x07variantBk\n\x14org.meshtastic.protoB\x15StoreAndForwardProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
|
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n&meshtastic/protobuf/storeforward.proto\x12\x13meshtastic.protobuf\"\xc0\x07\n\x0fStoreAndForward\x12@\n\x02rr\x18\x01 \x01(\x0e\x32\x34.meshtastic.protobuf.StoreAndForward.RequestResponse\x12@\n\x05stats\x18\x02 \x01(\x0b\x32/.meshtastic.protobuf.StoreAndForward.StatisticsH\x00\x12?\n\x07history\x18\x03 \x01(\x0b\x32,.meshtastic.protobuf.StoreAndForward.HistoryH\x00\x12\x43\n\theartbeat\x18\x04 \x01(\x0b\x32..meshtastic.protobuf.StoreAndForward.HeartbeatH\x00\x12\x0e\n\x04text\x18\x05 \x01(\x0cH\x00\x1a\xcd\x01\n\nStatistics\x12\x16\n\x0emessages_total\x18\x01 \x01(\r\x12\x16\n\x0emessages_saved\x18\x02 \x01(\r\x12\x14\n\x0cmessages_max\x18\x03 \x01(\r\x12\x0f\n\x07up_time\x18\x04 \x01(\r\x12\x10\n\x08requests\x18\x05 \x01(\r\x12\x18\n\x10requests_history\x18\x06 \x01(\r\x12\x11\n\theartbeat\x18\x07 \x01(\x08\x12\x12\n\nreturn_max\x18\x08 \x01(\r\x12\x15\n\rreturn_window\x18\t \x01(\r\x1aI\n\x07History\x12\x18\n\x10history_messages\x18\x01 \x01(\r\x12\x0e\n\x06window\x18\x02 \x01(\r\x12\x14\n\x0clast_request\x18\x03 \x01(\r\x1a.\n\tHeartbeat\x12\x0e\n\x06period\x18\x01 \x01(\r\x12\x11\n\tsecondary\x18\x02 \x01(\r\"\xbc\x02\n\x0fRequestResponse\x12\t\n\x05UNSET\x10\x00\x12\x10\n\x0cROUTER_ERROR\x10\x01\x12\x14\n\x10ROUTER_HEARTBEAT\x10\x02\x12\x0f\n\x0bROUTER_PING\x10\x03\x12\x0f\n\x0bROUTER_PONG\x10\x04\x12\x0f\n\x0bROUTER_BUSY\x10\x05\x12\x12\n\x0eROUTER_HISTORY\x10\x06\x12\x10\n\x0cROUTER_STATS\x10\x07\x12\x16\n\x12ROUTER_TEXT_DIRECT\x10\x08\x12\x19\n\x15ROUTER_TEXT_BROADCAST\x10\t\x12\x10\n\x0c\x43LIENT_ERROR\x10@\x12\x12\n\x0e\x43LIENT_HISTORY\x10\x41\x12\x10\n\x0c\x43LIENT_STATS\x10\x42\x12\x0f\n\x0b\x43LIENT_PING\x10\x43\x12\x0f\n\x0b\x43LIENT_PONG\x10\x44\x12\x10\n\x0c\x43LIENT_ABORT\x10jB\t\n\x07variantBj\n\x13\x63om.geeksville.meshB\x15StoreAndForwardProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
|
||||||
|
|
||||||
_globals = globals()
|
_globals = globals()
|
||||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.storeforward_pb2', _globals)
|
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.storeforward_pb2', _globals)
|
||||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||||
DESCRIPTOR._options = None
|
DESCRIPTOR._options = None
|
||||||
DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\025StoreAndForwardProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\025StoreAndForwardProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||||
_STOREANDFORWARD.fields_by_name['text']._options = None
|
_globals['_STOREANDFORWARD']._serialized_start=64
|
||||||
_STOREANDFORWARD.fields_by_name['text']._serialized_options = b'\222?\003\010\351\001'
|
_globals['_STOREANDFORWARD']._serialized_end=1024
|
||||||
_globals['_STOREANDFORWARD']._serialized_start=98
|
_globals['_STOREANDFORWARD_STATISTICS']._serialized_start=366
|
||||||
_globals['_STOREANDFORWARD']._serialized_end=1066
|
_globals['_STOREANDFORWARD_STATISTICS']._serialized_end=571
|
||||||
_globals['_STOREANDFORWARD_STATISTICS']._serialized_start=408
|
_globals['_STOREANDFORWARD_HISTORY']._serialized_start=573
|
||||||
_globals['_STOREANDFORWARD_STATISTICS']._serialized_end=613
|
_globals['_STOREANDFORWARD_HISTORY']._serialized_end=646
|
||||||
_globals['_STOREANDFORWARD_HISTORY']._serialized_start=615
|
_globals['_STOREANDFORWARD_HEARTBEAT']._serialized_start=648
|
||||||
_globals['_STOREANDFORWARD_HISTORY']._serialized_end=688
|
_globals['_STOREANDFORWARD_HEARTBEAT']._serialized_end=694
|
||||||
_globals['_STOREANDFORWARD_HEARTBEAT']._serialized_start=690
|
_globals['_STOREANDFORWARD_REQUESTRESPONSE']._serialized_start=697
|
||||||
_globals['_STOREANDFORWARD_HEARTBEAT']._serialized_end=736
|
_globals['_STOREANDFORWARD_REQUESTRESPONSE']._serialized_end=1013
|
||||||
_globals['_STOREANDFORWARD_REQUESTRESPONSE']._serialized_start=739
|
|
||||||
_globals['_STOREANDFORWARD_REQUESTRESPONSE']._serialized_end=1055
|
|
||||||
# @@protoc_insertion_point(module_scope)
|
# @@protoc_insertion_point(module_scope)
|
||||||
|
|||||||
73
meshtastic/protobuf/telemetry_pb2.py
generated
73
meshtastic/protobuf/telemetry_pb2.py
generated
File diff suppressed because one or more lines are too long
639
meshtastic/protobuf/telemetry_pb2.pyi
generated
639
meshtastic/protobuf/telemetry_pb2.pyi
generated
@@ -4,9 +4,7 @@ isort:skip_file
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import builtins
|
import builtins
|
||||||
import collections.abc
|
|
||||||
import google.protobuf.descriptor
|
import google.protobuf.descriptor
|
||||||
import google.protobuf.internal.containers
|
|
||||||
import google.protobuf.internal.enum_type_wrapper
|
import google.protobuf.internal.enum_type_wrapper
|
||||||
import google.protobuf.message
|
import google.protobuf.message
|
||||||
import sys
|
import sys
|
||||||
@@ -55,7 +53,7 @@ class _TelemetrySensorTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wra
|
|||||||
"""
|
"""
|
||||||
SHTC3: _TelemetrySensorType.ValueType # 7
|
SHTC3: _TelemetrySensorType.ValueType # 7
|
||||||
"""
|
"""
|
||||||
TODO - REMOVE High accuracy temperature and humidity
|
High accuracy temperature and humidity
|
||||||
"""
|
"""
|
||||||
LPS22: _TelemetrySensorType.ValueType # 8
|
LPS22: _TelemetrySensorType.ValueType # 8
|
||||||
"""
|
"""
|
||||||
@@ -75,7 +73,7 @@ class _TelemetrySensorTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wra
|
|||||||
"""
|
"""
|
||||||
SHT31: _TelemetrySensorType.ValueType # 12
|
SHT31: _TelemetrySensorType.ValueType # 12
|
||||||
"""
|
"""
|
||||||
TODO - REMOVE High accuracy temperature and humidity
|
High accuracy temperature and humidity
|
||||||
"""
|
"""
|
||||||
PMSA003I: _TelemetrySensorType.ValueType # 13
|
PMSA003I: _TelemetrySensorType.ValueType # 13
|
||||||
"""
|
"""
|
||||||
@@ -95,7 +93,7 @@ class _TelemetrySensorTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wra
|
|||||||
"""
|
"""
|
||||||
SHT4X: _TelemetrySensorType.ValueType # 17
|
SHT4X: _TelemetrySensorType.ValueType # 17
|
||||||
"""
|
"""
|
||||||
TODO - REMOVE Sensirion High accuracy temperature and humidity
|
Sensirion High accuracy temperature and humidity
|
||||||
"""
|
"""
|
||||||
VEML7700: _TelemetrySensorType.ValueType # 18
|
VEML7700: _TelemetrySensorType.ValueType # 18
|
||||||
"""
|
"""
|
||||||
@@ -169,78 +167,6 @@ class _TelemetrySensorTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wra
|
|||||||
"""
|
"""
|
||||||
DFRobot Gravity tipping bucket rain gauge
|
DFRobot Gravity tipping bucket rain gauge
|
||||||
"""
|
"""
|
||||||
DPS310: _TelemetrySensorType.ValueType # 36
|
|
||||||
"""
|
|
||||||
Infineon DPS310 High accuracy pressure and temperature
|
|
||||||
"""
|
|
||||||
RAK12035: _TelemetrySensorType.ValueType # 37
|
|
||||||
"""
|
|
||||||
RAKWireless RAK12035 Soil Moisture Sensor Module
|
|
||||||
"""
|
|
||||||
MAX17261: _TelemetrySensorType.ValueType # 38
|
|
||||||
"""
|
|
||||||
MAX17261 lipo battery gauge
|
|
||||||
"""
|
|
||||||
PCT2075: _TelemetrySensorType.ValueType # 39
|
|
||||||
"""
|
|
||||||
PCT2075 Temperature Sensor
|
|
||||||
"""
|
|
||||||
ADS1X15: _TelemetrySensorType.ValueType # 40
|
|
||||||
"""
|
|
||||||
ADS1X15 ADC
|
|
||||||
"""
|
|
||||||
ADS1X15_ALT: _TelemetrySensorType.ValueType # 41
|
|
||||||
"""
|
|
||||||
ADS1X15 ADC_ALT
|
|
||||||
"""
|
|
||||||
SFA30: _TelemetrySensorType.ValueType # 42
|
|
||||||
"""
|
|
||||||
Sensirion SFA30 Formaldehyde sensor
|
|
||||||
"""
|
|
||||||
SEN5X: _TelemetrySensorType.ValueType # 43
|
|
||||||
"""
|
|
||||||
SEN5X PM SENSORS
|
|
||||||
"""
|
|
||||||
TSL2561: _TelemetrySensorType.ValueType # 44
|
|
||||||
"""
|
|
||||||
TSL2561 light sensor
|
|
||||||
"""
|
|
||||||
BH1750: _TelemetrySensorType.ValueType # 45
|
|
||||||
"""
|
|
||||||
BH1750 light sensor
|
|
||||||
"""
|
|
||||||
HDC1080: _TelemetrySensorType.ValueType # 46
|
|
||||||
"""
|
|
||||||
HDC1080 Temperature and Humidity Sensor
|
|
||||||
"""
|
|
||||||
SHT21: _TelemetrySensorType.ValueType # 47
|
|
||||||
"""
|
|
||||||
TODO - REMOVE STH21 Temperature and R. Humidity sensor
|
|
||||||
"""
|
|
||||||
STC31: _TelemetrySensorType.ValueType # 48
|
|
||||||
"""
|
|
||||||
Sensirion STC31 CO2 sensor
|
|
||||||
"""
|
|
||||||
SCD30: _TelemetrySensorType.ValueType # 49
|
|
||||||
"""
|
|
||||||
SCD30 CO2, humidity, temperature sensor
|
|
||||||
"""
|
|
||||||
SHTXX: _TelemetrySensorType.ValueType # 50
|
|
||||||
"""
|
|
||||||
SHT family of sensors for temperature and humidity
|
|
||||||
"""
|
|
||||||
DS248X: _TelemetrySensorType.ValueType # 51
|
|
||||||
"""
|
|
||||||
DS248X Bridge for one-wire temperature sensors
|
|
||||||
"""
|
|
||||||
MMC5983MA: _TelemetrySensorType.ValueType # 52
|
|
||||||
"""
|
|
||||||
MMC5983MA 3-Axis Digital Magnetic Sensor
|
|
||||||
"""
|
|
||||||
ICM42607P: _TelemetrySensorType.ValueType # 53
|
|
||||||
"""
|
|
||||||
ICM-42607-P 6‑Axis IMU
|
|
||||||
"""
|
|
||||||
|
|
||||||
class TelemetrySensorType(_TelemetrySensorType, metaclass=_TelemetrySensorTypeEnumTypeWrapper):
|
class TelemetrySensorType(_TelemetrySensorType, metaclass=_TelemetrySensorTypeEnumTypeWrapper):
|
||||||
"""
|
"""
|
||||||
@@ -277,7 +203,7 @@ High accuracy temperature and pressure
|
|||||||
"""
|
"""
|
||||||
SHTC3: TelemetrySensorType.ValueType # 7
|
SHTC3: TelemetrySensorType.ValueType # 7
|
||||||
"""
|
"""
|
||||||
TODO - REMOVE High accuracy temperature and humidity
|
High accuracy temperature and humidity
|
||||||
"""
|
"""
|
||||||
LPS22: TelemetrySensorType.ValueType # 8
|
LPS22: TelemetrySensorType.ValueType # 8
|
||||||
"""
|
"""
|
||||||
@@ -297,7 +223,7 @@ QMC5883L: TelemetrySensorType.ValueType # 11
|
|||||||
"""
|
"""
|
||||||
SHT31: TelemetrySensorType.ValueType # 12
|
SHT31: TelemetrySensorType.ValueType # 12
|
||||||
"""
|
"""
|
||||||
TODO - REMOVE High accuracy temperature and humidity
|
High accuracy temperature and humidity
|
||||||
"""
|
"""
|
||||||
PMSA003I: TelemetrySensorType.ValueType # 13
|
PMSA003I: TelemetrySensorType.ValueType # 13
|
||||||
"""
|
"""
|
||||||
@@ -317,7 +243,7 @@ RCWL-9620 Doppler Radar Distance Sensor, used for water level detection
|
|||||||
"""
|
"""
|
||||||
SHT4X: TelemetrySensorType.ValueType # 17
|
SHT4X: TelemetrySensorType.ValueType # 17
|
||||||
"""
|
"""
|
||||||
TODO - REMOVE Sensirion High accuracy temperature and humidity
|
Sensirion High accuracy temperature and humidity
|
||||||
"""
|
"""
|
||||||
VEML7700: TelemetrySensorType.ValueType # 18
|
VEML7700: TelemetrySensorType.ValueType # 18
|
||||||
"""
|
"""
|
||||||
@@ -391,78 +317,6 @@ DFROBOT_RAIN: TelemetrySensorType.ValueType # 35
|
|||||||
"""
|
"""
|
||||||
DFRobot Gravity tipping bucket rain gauge
|
DFRobot Gravity tipping bucket rain gauge
|
||||||
"""
|
"""
|
||||||
DPS310: TelemetrySensorType.ValueType # 36
|
|
||||||
"""
|
|
||||||
Infineon DPS310 High accuracy pressure and temperature
|
|
||||||
"""
|
|
||||||
RAK12035: TelemetrySensorType.ValueType # 37
|
|
||||||
"""
|
|
||||||
RAKWireless RAK12035 Soil Moisture Sensor Module
|
|
||||||
"""
|
|
||||||
MAX17261: TelemetrySensorType.ValueType # 38
|
|
||||||
"""
|
|
||||||
MAX17261 lipo battery gauge
|
|
||||||
"""
|
|
||||||
PCT2075: TelemetrySensorType.ValueType # 39
|
|
||||||
"""
|
|
||||||
PCT2075 Temperature Sensor
|
|
||||||
"""
|
|
||||||
ADS1X15: TelemetrySensorType.ValueType # 40
|
|
||||||
"""
|
|
||||||
ADS1X15 ADC
|
|
||||||
"""
|
|
||||||
ADS1X15_ALT: TelemetrySensorType.ValueType # 41
|
|
||||||
"""
|
|
||||||
ADS1X15 ADC_ALT
|
|
||||||
"""
|
|
||||||
SFA30: TelemetrySensorType.ValueType # 42
|
|
||||||
"""
|
|
||||||
Sensirion SFA30 Formaldehyde sensor
|
|
||||||
"""
|
|
||||||
SEN5X: TelemetrySensorType.ValueType # 43
|
|
||||||
"""
|
|
||||||
SEN5X PM SENSORS
|
|
||||||
"""
|
|
||||||
TSL2561: TelemetrySensorType.ValueType # 44
|
|
||||||
"""
|
|
||||||
TSL2561 light sensor
|
|
||||||
"""
|
|
||||||
BH1750: TelemetrySensorType.ValueType # 45
|
|
||||||
"""
|
|
||||||
BH1750 light sensor
|
|
||||||
"""
|
|
||||||
HDC1080: TelemetrySensorType.ValueType # 46
|
|
||||||
"""
|
|
||||||
HDC1080 Temperature and Humidity Sensor
|
|
||||||
"""
|
|
||||||
SHT21: TelemetrySensorType.ValueType # 47
|
|
||||||
"""
|
|
||||||
TODO - REMOVE STH21 Temperature and R. Humidity sensor
|
|
||||||
"""
|
|
||||||
STC31: TelemetrySensorType.ValueType # 48
|
|
||||||
"""
|
|
||||||
Sensirion STC31 CO2 sensor
|
|
||||||
"""
|
|
||||||
SCD30: TelemetrySensorType.ValueType # 49
|
|
||||||
"""
|
|
||||||
SCD30 CO2, humidity, temperature sensor
|
|
||||||
"""
|
|
||||||
SHTXX: TelemetrySensorType.ValueType # 50
|
|
||||||
"""
|
|
||||||
SHT family of sensors for temperature and humidity
|
|
||||||
"""
|
|
||||||
DS248X: TelemetrySensorType.ValueType # 51
|
|
||||||
"""
|
|
||||||
DS248X Bridge for one-wire temperature sensors
|
|
||||||
"""
|
|
||||||
MMC5983MA: TelemetrySensorType.ValueType # 52
|
|
||||||
"""
|
|
||||||
MMC5983MA 3-Axis Digital Magnetic Sensor
|
|
||||||
"""
|
|
||||||
ICM42607P: TelemetrySensorType.ValueType # 53
|
|
||||||
"""
|
|
||||||
ICM-42607-P 6‑Axis IMU
|
|
||||||
"""
|
|
||||||
global___TelemetrySensorType = TelemetrySensorType
|
global___TelemetrySensorType = TelemetrySensorType
|
||||||
|
|
||||||
@typing.final
|
@typing.final
|
||||||
@@ -550,9 +404,6 @@ class EnvironmentMetrics(google.protobuf.message.Message):
|
|||||||
RADIATION_FIELD_NUMBER: builtins.int
|
RADIATION_FIELD_NUMBER: builtins.int
|
||||||
RAINFALL_1H_FIELD_NUMBER: builtins.int
|
RAINFALL_1H_FIELD_NUMBER: builtins.int
|
||||||
RAINFALL_24H_FIELD_NUMBER: builtins.int
|
RAINFALL_24H_FIELD_NUMBER: builtins.int
|
||||||
SOIL_MOISTURE_FIELD_NUMBER: builtins.int
|
|
||||||
SOIL_TEMPERATURE_FIELD_NUMBER: builtins.int
|
|
||||||
ONE_WIRE_TEMPERATURE_FIELD_NUMBER: builtins.int
|
|
||||||
temperature: builtins.float
|
temperature: builtins.float
|
||||||
"""
|
"""
|
||||||
Temperature measured
|
Temperature measured
|
||||||
@@ -635,20 +486,6 @@ class EnvironmentMetrics(google.protobuf.message.Message):
|
|||||||
"""
|
"""
|
||||||
Rainfall in the last 24 hours in mm
|
Rainfall in the last 24 hours in mm
|
||||||
"""
|
"""
|
||||||
soil_moisture: builtins.int
|
|
||||||
"""
|
|
||||||
Soil moisture measured (% 1-100)
|
|
||||||
"""
|
|
||||||
soil_temperature: builtins.float
|
|
||||||
"""
|
|
||||||
Soil temperature measured (*C)
|
|
||||||
"""
|
|
||||||
@property
|
|
||||||
def one_wire_temperature(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.float]:
|
|
||||||
"""
|
|
||||||
One-wire temperature (*C)
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
@@ -672,12 +509,9 @@ class EnvironmentMetrics(google.protobuf.message.Message):
|
|||||||
radiation: builtins.float | None = ...,
|
radiation: builtins.float | None = ...,
|
||||||
rainfall_1h: builtins.float | None = ...,
|
rainfall_1h: builtins.float | None = ...,
|
||||||
rainfall_24h: builtins.float | None = ...,
|
rainfall_24h: builtins.float | None = ...,
|
||||||
soil_moisture: builtins.int | None = ...,
|
|
||||||
soil_temperature: builtins.float | None = ...,
|
|
||||||
one_wire_temperature: collections.abc.Iterable[builtins.float] | None = ...,
|
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
def HasField(self, field_name: typing.Literal["_barometric_pressure", b"_barometric_pressure", "_current", b"_current", "_distance", b"_distance", "_gas_resistance", b"_gas_resistance", "_iaq", b"_iaq", "_ir_lux", b"_ir_lux", "_lux", b"_lux", "_radiation", b"_radiation", "_rainfall_1h", b"_rainfall_1h", "_rainfall_24h", b"_rainfall_24h", "_relative_humidity", b"_relative_humidity", "_soil_moisture", b"_soil_moisture", "_soil_temperature", b"_soil_temperature", "_temperature", b"_temperature", "_uv_lux", b"_uv_lux", "_voltage", b"_voltage", "_weight", b"_weight", "_white_lux", b"_white_lux", "_wind_direction", b"_wind_direction", "_wind_gust", b"_wind_gust", "_wind_lull", b"_wind_lull", "_wind_speed", b"_wind_speed", "barometric_pressure", b"barometric_pressure", "current", b"current", "distance", b"distance", "gas_resistance", b"gas_resistance", "iaq", b"iaq", "ir_lux", b"ir_lux", "lux", b"lux", "radiation", b"radiation", "rainfall_1h", b"rainfall_1h", "rainfall_24h", b"rainfall_24h", "relative_humidity", b"relative_humidity", "soil_moisture", b"soil_moisture", "soil_temperature", b"soil_temperature", "temperature", b"temperature", "uv_lux", b"uv_lux", "voltage", b"voltage", "weight", b"weight", "white_lux", b"white_lux", "wind_direction", b"wind_direction", "wind_gust", b"wind_gust", "wind_lull", b"wind_lull", "wind_speed", b"wind_speed"]) -> builtins.bool: ...
|
def HasField(self, field_name: typing.Literal["_barometric_pressure", b"_barometric_pressure", "_current", b"_current", "_distance", b"_distance", "_gas_resistance", b"_gas_resistance", "_iaq", b"_iaq", "_ir_lux", b"_ir_lux", "_lux", b"_lux", "_radiation", b"_radiation", "_rainfall_1h", b"_rainfall_1h", "_rainfall_24h", b"_rainfall_24h", "_relative_humidity", b"_relative_humidity", "_temperature", b"_temperature", "_uv_lux", b"_uv_lux", "_voltage", b"_voltage", "_weight", b"_weight", "_white_lux", b"_white_lux", "_wind_direction", b"_wind_direction", "_wind_gust", b"_wind_gust", "_wind_lull", b"_wind_lull", "_wind_speed", b"_wind_speed", "barometric_pressure", b"barometric_pressure", "current", b"current", "distance", b"distance", "gas_resistance", b"gas_resistance", "iaq", b"iaq", "ir_lux", b"ir_lux", "lux", b"lux", "radiation", b"radiation", "rainfall_1h", b"rainfall_1h", "rainfall_24h", b"rainfall_24h", "relative_humidity", b"relative_humidity", "temperature", b"temperature", "uv_lux", b"uv_lux", "voltage", b"voltage", "weight", b"weight", "white_lux", b"white_lux", "wind_direction", b"wind_direction", "wind_gust", b"wind_gust", "wind_lull", b"wind_lull", "wind_speed", b"wind_speed"]) -> builtins.bool: ...
|
||||||
def ClearField(self, field_name: typing.Literal["_barometric_pressure", b"_barometric_pressure", "_current", b"_current", "_distance", b"_distance", "_gas_resistance", b"_gas_resistance", "_iaq", b"_iaq", "_ir_lux", b"_ir_lux", "_lux", b"_lux", "_radiation", b"_radiation", "_rainfall_1h", b"_rainfall_1h", "_rainfall_24h", b"_rainfall_24h", "_relative_humidity", b"_relative_humidity", "_soil_moisture", b"_soil_moisture", "_soil_temperature", b"_soil_temperature", "_temperature", b"_temperature", "_uv_lux", b"_uv_lux", "_voltage", b"_voltage", "_weight", b"_weight", "_white_lux", b"_white_lux", "_wind_direction", b"_wind_direction", "_wind_gust", b"_wind_gust", "_wind_lull", b"_wind_lull", "_wind_speed", b"_wind_speed", "barometric_pressure", b"barometric_pressure", "current", b"current", "distance", b"distance", "gas_resistance", b"gas_resistance", "iaq", b"iaq", "ir_lux", b"ir_lux", "lux", b"lux", "one_wire_temperature", b"one_wire_temperature", "radiation", b"radiation", "rainfall_1h", b"rainfall_1h", "rainfall_24h", b"rainfall_24h", "relative_humidity", b"relative_humidity", "soil_moisture", b"soil_moisture", "soil_temperature", b"soil_temperature", "temperature", b"temperature", "uv_lux", b"uv_lux", "voltage", b"voltage", "weight", b"weight", "white_lux", b"white_lux", "wind_direction", b"wind_direction", "wind_gust", b"wind_gust", "wind_lull", b"wind_lull", "wind_speed", b"wind_speed"]) -> None: ...
|
def ClearField(self, field_name: typing.Literal["_barometric_pressure", b"_barometric_pressure", "_current", b"_current", "_distance", b"_distance", "_gas_resistance", b"_gas_resistance", "_iaq", b"_iaq", "_ir_lux", b"_ir_lux", "_lux", b"_lux", "_radiation", b"_radiation", "_rainfall_1h", b"_rainfall_1h", "_rainfall_24h", b"_rainfall_24h", "_relative_humidity", b"_relative_humidity", "_temperature", b"_temperature", "_uv_lux", b"_uv_lux", "_voltage", b"_voltage", "_weight", b"_weight", "_white_lux", b"_white_lux", "_wind_direction", b"_wind_direction", "_wind_gust", b"_wind_gust", "_wind_lull", b"_wind_lull", "_wind_speed", b"_wind_speed", "barometric_pressure", b"barometric_pressure", "current", b"current", "distance", b"distance", "gas_resistance", b"gas_resistance", "iaq", b"iaq", "ir_lux", b"ir_lux", "lux", b"lux", "radiation", b"radiation", "rainfall_1h", b"rainfall_1h", "rainfall_24h", b"rainfall_24h", "relative_humidity", b"relative_humidity", "temperature", b"temperature", "uv_lux", b"uv_lux", "voltage", b"voltage", "weight", b"weight", "white_lux", b"white_lux", "wind_direction", b"wind_direction", "wind_gust", b"wind_gust", "wind_lull", b"wind_lull", "wind_speed", b"wind_speed"]) -> None: ...
|
||||||
@typing.overload
|
@typing.overload
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_barometric_pressure", b"_barometric_pressure"]) -> typing.Literal["barometric_pressure"] | None: ...
|
def WhichOneof(self, oneof_group: typing.Literal["_barometric_pressure", b"_barometric_pressure"]) -> typing.Literal["barometric_pressure"] | None: ...
|
||||||
@typing.overload
|
@typing.overload
|
||||||
@@ -701,10 +535,6 @@ class EnvironmentMetrics(google.protobuf.message.Message):
|
|||||||
@typing.overload
|
@typing.overload
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_relative_humidity", b"_relative_humidity"]) -> typing.Literal["relative_humidity"] | None: ...
|
def WhichOneof(self, oneof_group: typing.Literal["_relative_humidity", b"_relative_humidity"]) -> typing.Literal["relative_humidity"] | None: ...
|
||||||
@typing.overload
|
@typing.overload
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_soil_moisture", b"_soil_moisture"]) -> typing.Literal["soil_moisture"] | None: ...
|
|
||||||
@typing.overload
|
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_soil_temperature", b"_soil_temperature"]) -> typing.Literal["soil_temperature"] | None: ...
|
|
||||||
@typing.overload
|
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_temperature", b"_temperature"]) -> typing.Literal["temperature"] | None: ...
|
def WhichOneof(self, oneof_group: typing.Literal["_temperature", b"_temperature"]) -> typing.Literal["temperature"] | None: ...
|
||||||
@typing.overload
|
@typing.overload
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_uv_lux", b"_uv_lux"]) -> typing.Literal["uv_lux"] | None: ...
|
def WhichOneof(self, oneof_group: typing.Literal["_uv_lux", b"_uv_lux"]) -> typing.Literal["uv_lux"] | None: ...
|
||||||
@@ -739,16 +569,6 @@ class PowerMetrics(google.protobuf.message.Message):
|
|||||||
CH2_CURRENT_FIELD_NUMBER: builtins.int
|
CH2_CURRENT_FIELD_NUMBER: builtins.int
|
||||||
CH3_VOLTAGE_FIELD_NUMBER: builtins.int
|
CH3_VOLTAGE_FIELD_NUMBER: builtins.int
|
||||||
CH3_CURRENT_FIELD_NUMBER: builtins.int
|
CH3_CURRENT_FIELD_NUMBER: builtins.int
|
||||||
CH4_VOLTAGE_FIELD_NUMBER: builtins.int
|
|
||||||
CH4_CURRENT_FIELD_NUMBER: builtins.int
|
|
||||||
CH5_VOLTAGE_FIELD_NUMBER: builtins.int
|
|
||||||
CH5_CURRENT_FIELD_NUMBER: builtins.int
|
|
||||||
CH6_VOLTAGE_FIELD_NUMBER: builtins.int
|
|
||||||
CH6_CURRENT_FIELD_NUMBER: builtins.int
|
|
||||||
CH7_VOLTAGE_FIELD_NUMBER: builtins.int
|
|
||||||
CH7_CURRENT_FIELD_NUMBER: builtins.int
|
|
||||||
CH8_VOLTAGE_FIELD_NUMBER: builtins.int
|
|
||||||
CH8_CURRENT_FIELD_NUMBER: builtins.int
|
|
||||||
ch1_voltage: builtins.float
|
ch1_voltage: builtins.float
|
||||||
"""
|
"""
|
||||||
Voltage (Ch1)
|
Voltage (Ch1)
|
||||||
@@ -773,46 +593,6 @@ class PowerMetrics(google.protobuf.message.Message):
|
|||||||
"""
|
"""
|
||||||
Current (Ch3)
|
Current (Ch3)
|
||||||
"""
|
"""
|
||||||
ch4_voltage: builtins.float
|
|
||||||
"""
|
|
||||||
Voltage (Ch4)
|
|
||||||
"""
|
|
||||||
ch4_current: builtins.float
|
|
||||||
"""
|
|
||||||
Current (Ch4)
|
|
||||||
"""
|
|
||||||
ch5_voltage: builtins.float
|
|
||||||
"""
|
|
||||||
Voltage (Ch5)
|
|
||||||
"""
|
|
||||||
ch5_current: builtins.float
|
|
||||||
"""
|
|
||||||
Current (Ch5)
|
|
||||||
"""
|
|
||||||
ch6_voltage: builtins.float
|
|
||||||
"""
|
|
||||||
Voltage (Ch6)
|
|
||||||
"""
|
|
||||||
ch6_current: builtins.float
|
|
||||||
"""
|
|
||||||
Current (Ch6)
|
|
||||||
"""
|
|
||||||
ch7_voltage: builtins.float
|
|
||||||
"""
|
|
||||||
Voltage (Ch7)
|
|
||||||
"""
|
|
||||||
ch7_current: builtins.float
|
|
||||||
"""
|
|
||||||
Current (Ch7)
|
|
||||||
"""
|
|
||||||
ch8_voltage: builtins.float
|
|
||||||
"""
|
|
||||||
Voltage (Ch8)
|
|
||||||
"""
|
|
||||||
ch8_current: builtins.float
|
|
||||||
"""
|
|
||||||
Current (Ch8)
|
|
||||||
"""
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
@@ -822,19 +602,9 @@ class PowerMetrics(google.protobuf.message.Message):
|
|||||||
ch2_current: builtins.float | None = ...,
|
ch2_current: builtins.float | None = ...,
|
||||||
ch3_voltage: builtins.float | None = ...,
|
ch3_voltage: builtins.float | None = ...,
|
||||||
ch3_current: builtins.float | None = ...,
|
ch3_current: builtins.float | None = ...,
|
||||||
ch4_voltage: builtins.float | None = ...,
|
|
||||||
ch4_current: builtins.float | None = ...,
|
|
||||||
ch5_voltage: builtins.float | None = ...,
|
|
||||||
ch5_current: builtins.float | None = ...,
|
|
||||||
ch6_voltage: builtins.float | None = ...,
|
|
||||||
ch6_current: builtins.float | None = ...,
|
|
||||||
ch7_voltage: builtins.float | None = ...,
|
|
||||||
ch7_current: builtins.float | None = ...,
|
|
||||||
ch8_voltage: builtins.float | None = ...,
|
|
||||||
ch8_current: builtins.float | None = ...,
|
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
def HasField(self, field_name: typing.Literal["_ch1_current", b"_ch1_current", "_ch1_voltage", b"_ch1_voltage", "_ch2_current", b"_ch2_current", "_ch2_voltage", b"_ch2_voltage", "_ch3_current", b"_ch3_current", "_ch3_voltage", b"_ch3_voltage", "_ch4_current", b"_ch4_current", "_ch4_voltage", b"_ch4_voltage", "_ch5_current", b"_ch5_current", "_ch5_voltage", b"_ch5_voltage", "_ch6_current", b"_ch6_current", "_ch6_voltage", b"_ch6_voltage", "_ch7_current", b"_ch7_current", "_ch7_voltage", b"_ch7_voltage", "_ch8_current", b"_ch8_current", "_ch8_voltage", b"_ch8_voltage", "ch1_current", b"ch1_current", "ch1_voltage", b"ch1_voltage", "ch2_current", b"ch2_current", "ch2_voltage", b"ch2_voltage", "ch3_current", b"ch3_current", "ch3_voltage", b"ch3_voltage", "ch4_current", b"ch4_current", "ch4_voltage", b"ch4_voltage", "ch5_current", b"ch5_current", "ch5_voltage", b"ch5_voltage", "ch6_current", b"ch6_current", "ch6_voltage", b"ch6_voltage", "ch7_current", b"ch7_current", "ch7_voltage", b"ch7_voltage", "ch8_current", b"ch8_current", "ch8_voltage", b"ch8_voltage"]) -> builtins.bool: ...
|
def HasField(self, field_name: typing.Literal["_ch1_current", b"_ch1_current", "_ch1_voltage", b"_ch1_voltage", "_ch2_current", b"_ch2_current", "_ch2_voltage", b"_ch2_voltage", "_ch3_current", b"_ch3_current", "_ch3_voltage", b"_ch3_voltage", "ch1_current", b"ch1_current", "ch1_voltage", b"ch1_voltage", "ch2_current", b"ch2_current", "ch2_voltage", b"ch2_voltage", "ch3_current", b"ch3_current", "ch3_voltage", b"ch3_voltage"]) -> builtins.bool: ...
|
||||||
def ClearField(self, field_name: typing.Literal["_ch1_current", b"_ch1_current", "_ch1_voltage", b"_ch1_voltage", "_ch2_current", b"_ch2_current", "_ch2_voltage", b"_ch2_voltage", "_ch3_current", b"_ch3_current", "_ch3_voltage", b"_ch3_voltage", "_ch4_current", b"_ch4_current", "_ch4_voltage", b"_ch4_voltage", "_ch5_current", b"_ch5_current", "_ch5_voltage", b"_ch5_voltage", "_ch6_current", b"_ch6_current", "_ch6_voltage", b"_ch6_voltage", "_ch7_current", b"_ch7_current", "_ch7_voltage", b"_ch7_voltage", "_ch8_current", b"_ch8_current", "_ch8_voltage", b"_ch8_voltage", "ch1_current", b"ch1_current", "ch1_voltage", b"ch1_voltage", "ch2_current", b"ch2_current", "ch2_voltage", b"ch2_voltage", "ch3_current", b"ch3_current", "ch3_voltage", b"ch3_voltage", "ch4_current", b"ch4_current", "ch4_voltage", b"ch4_voltage", "ch5_current", b"ch5_current", "ch5_voltage", b"ch5_voltage", "ch6_current", b"ch6_current", "ch6_voltage", b"ch6_voltage", "ch7_current", b"ch7_current", "ch7_voltage", b"ch7_voltage", "ch8_current", b"ch8_current", "ch8_voltage", b"ch8_voltage"]) -> None: ...
|
def ClearField(self, field_name: typing.Literal["_ch1_current", b"_ch1_current", "_ch1_voltage", b"_ch1_voltage", "_ch2_current", b"_ch2_current", "_ch2_voltage", b"_ch2_voltage", "_ch3_current", b"_ch3_current", "_ch3_voltage", b"_ch3_voltage", "ch1_current", b"ch1_current", "ch1_voltage", b"ch1_voltage", "ch2_current", b"ch2_current", "ch2_voltage", b"ch2_voltage", "ch3_current", b"ch3_current", "ch3_voltage", b"ch3_voltage"]) -> None: ...
|
||||||
@typing.overload
|
@typing.overload
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_ch1_current", b"_ch1_current"]) -> typing.Literal["ch1_current"] | None: ...
|
def WhichOneof(self, oneof_group: typing.Literal["_ch1_current", b"_ch1_current"]) -> typing.Literal["ch1_current"] | None: ...
|
||||||
@typing.overload
|
@typing.overload
|
||||||
@@ -847,26 +617,6 @@ class PowerMetrics(google.protobuf.message.Message):
|
|||||||
def WhichOneof(self, oneof_group: typing.Literal["_ch3_current", b"_ch3_current"]) -> typing.Literal["ch3_current"] | None: ...
|
def WhichOneof(self, oneof_group: typing.Literal["_ch3_current", b"_ch3_current"]) -> typing.Literal["ch3_current"] | None: ...
|
||||||
@typing.overload
|
@typing.overload
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_ch3_voltage", b"_ch3_voltage"]) -> typing.Literal["ch3_voltage"] | None: ...
|
def WhichOneof(self, oneof_group: typing.Literal["_ch3_voltage", b"_ch3_voltage"]) -> typing.Literal["ch3_voltage"] | None: ...
|
||||||
@typing.overload
|
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_ch4_current", b"_ch4_current"]) -> typing.Literal["ch4_current"] | None: ...
|
|
||||||
@typing.overload
|
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_ch4_voltage", b"_ch4_voltage"]) -> typing.Literal["ch4_voltage"] | None: ...
|
|
||||||
@typing.overload
|
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_ch5_current", b"_ch5_current"]) -> typing.Literal["ch5_current"] | None: ...
|
|
||||||
@typing.overload
|
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_ch5_voltage", b"_ch5_voltage"]) -> typing.Literal["ch5_voltage"] | None: ...
|
|
||||||
@typing.overload
|
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_ch6_current", b"_ch6_current"]) -> typing.Literal["ch6_current"] | None: ...
|
|
||||||
@typing.overload
|
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_ch6_voltage", b"_ch6_voltage"]) -> typing.Literal["ch6_voltage"] | None: ...
|
|
||||||
@typing.overload
|
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_ch7_current", b"_ch7_current"]) -> typing.Literal["ch7_current"] | None: ...
|
|
||||||
@typing.overload
|
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_ch7_voltage", b"_ch7_voltage"]) -> typing.Literal["ch7_voltage"] | None: ...
|
|
||||||
@typing.overload
|
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_ch8_current", b"_ch8_current"]) -> typing.Literal["ch8_current"] | None: ...
|
|
||||||
@typing.overload
|
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_ch8_voltage", b"_ch8_voltage"]) -> typing.Literal["ch8_voltage"] | None: ...
|
|
||||||
|
|
||||||
global___PowerMetrics = PowerMetrics
|
global___PowerMetrics = PowerMetrics
|
||||||
|
|
||||||
@@ -891,117 +641,57 @@ class AirQualityMetrics(google.protobuf.message.Message):
|
|||||||
PARTICLES_50UM_FIELD_NUMBER: builtins.int
|
PARTICLES_50UM_FIELD_NUMBER: builtins.int
|
||||||
PARTICLES_100UM_FIELD_NUMBER: builtins.int
|
PARTICLES_100UM_FIELD_NUMBER: builtins.int
|
||||||
CO2_FIELD_NUMBER: builtins.int
|
CO2_FIELD_NUMBER: builtins.int
|
||||||
CO2_TEMPERATURE_FIELD_NUMBER: builtins.int
|
|
||||||
CO2_HUMIDITY_FIELD_NUMBER: builtins.int
|
|
||||||
FORM_FORMALDEHYDE_FIELD_NUMBER: builtins.int
|
|
||||||
FORM_HUMIDITY_FIELD_NUMBER: builtins.int
|
|
||||||
FORM_TEMPERATURE_FIELD_NUMBER: builtins.int
|
|
||||||
PM40_STANDARD_FIELD_NUMBER: builtins.int
|
|
||||||
PARTICLES_40UM_FIELD_NUMBER: builtins.int
|
|
||||||
PM_TEMPERATURE_FIELD_NUMBER: builtins.int
|
|
||||||
PM_HUMIDITY_FIELD_NUMBER: builtins.int
|
|
||||||
PM_VOC_IDX_FIELD_NUMBER: builtins.int
|
|
||||||
PM_NOX_IDX_FIELD_NUMBER: builtins.int
|
|
||||||
PARTICLES_TPS_FIELD_NUMBER: builtins.int
|
|
||||||
pm10_standard: builtins.int
|
pm10_standard: builtins.int
|
||||||
"""
|
"""
|
||||||
Concentration Units Standard PM1.0 in ug/m3
|
Concentration Units Standard PM1.0
|
||||||
"""
|
"""
|
||||||
pm25_standard: builtins.int
|
pm25_standard: builtins.int
|
||||||
"""
|
"""
|
||||||
Concentration Units Standard PM2.5 in ug/m3
|
Concentration Units Standard PM2.5
|
||||||
"""
|
"""
|
||||||
pm100_standard: builtins.int
|
pm100_standard: builtins.int
|
||||||
"""
|
"""
|
||||||
Concentration Units Standard PM10.0 in ug/m3
|
Concentration Units Standard PM10.0
|
||||||
"""
|
"""
|
||||||
pm10_environmental: builtins.int
|
pm10_environmental: builtins.int
|
||||||
"""
|
"""
|
||||||
Concentration Units Environmental PM1.0 in ug/m3
|
Concentration Units Environmental PM1.0
|
||||||
"""
|
"""
|
||||||
pm25_environmental: builtins.int
|
pm25_environmental: builtins.int
|
||||||
"""
|
"""
|
||||||
Concentration Units Environmental PM2.5 in ug/m3
|
Concentration Units Environmental PM2.5
|
||||||
"""
|
"""
|
||||||
pm100_environmental: builtins.int
|
pm100_environmental: builtins.int
|
||||||
"""
|
"""
|
||||||
Concentration Units Environmental PM10.0 in ug/m3
|
Concentration Units Environmental PM10.0
|
||||||
"""
|
"""
|
||||||
particles_03um: builtins.int
|
particles_03um: builtins.int
|
||||||
"""
|
"""
|
||||||
0.3um Particle Count in #/0.1l
|
0.3um Particle Count
|
||||||
"""
|
"""
|
||||||
particles_05um: builtins.int
|
particles_05um: builtins.int
|
||||||
"""
|
"""
|
||||||
0.5um Particle Count in #/0.1l
|
0.5um Particle Count
|
||||||
"""
|
"""
|
||||||
particles_10um: builtins.int
|
particles_10um: builtins.int
|
||||||
"""
|
"""
|
||||||
1.0um Particle Count in #/0.1l
|
1.0um Particle Count
|
||||||
"""
|
"""
|
||||||
particles_25um: builtins.int
|
particles_25um: builtins.int
|
||||||
"""
|
"""
|
||||||
2.5um Particle Count in #/0.1l
|
2.5um Particle Count
|
||||||
"""
|
"""
|
||||||
particles_50um: builtins.int
|
particles_50um: builtins.int
|
||||||
"""
|
"""
|
||||||
5.0um Particle Count in #/0.1l
|
5.0um Particle Count
|
||||||
"""
|
"""
|
||||||
particles_100um: builtins.int
|
particles_100um: builtins.int
|
||||||
"""
|
"""
|
||||||
10.0um Particle Count in #/0.1l
|
10.0um Particle Count
|
||||||
"""
|
"""
|
||||||
co2: builtins.int
|
co2: builtins.int
|
||||||
"""
|
"""
|
||||||
CO2 concentration in ppm
|
10.0um Particle Count
|
||||||
"""
|
|
||||||
co2_temperature: builtins.float
|
|
||||||
"""
|
|
||||||
CO2 sensor temperature in degC
|
|
||||||
"""
|
|
||||||
co2_humidity: builtins.float
|
|
||||||
"""
|
|
||||||
CO2 sensor relative humidity in %
|
|
||||||
"""
|
|
||||||
form_formaldehyde: builtins.float
|
|
||||||
"""
|
|
||||||
Formaldehyde sensor formaldehyde concentration in ppb
|
|
||||||
"""
|
|
||||||
form_humidity: builtins.float
|
|
||||||
"""
|
|
||||||
Formaldehyde sensor relative humidity in %RH
|
|
||||||
"""
|
|
||||||
form_temperature: builtins.float
|
|
||||||
"""
|
|
||||||
Formaldehyde sensor temperature in degrees Celsius
|
|
||||||
"""
|
|
||||||
pm40_standard: builtins.int
|
|
||||||
"""
|
|
||||||
Concentration Units Standard PM4.0 in ug/m3
|
|
||||||
"""
|
|
||||||
particles_40um: builtins.int
|
|
||||||
"""
|
|
||||||
4.0um Particle Count in #/0.1l
|
|
||||||
"""
|
|
||||||
pm_temperature: builtins.float
|
|
||||||
"""
|
|
||||||
PM Sensor Temperature
|
|
||||||
"""
|
|
||||||
pm_humidity: builtins.float
|
|
||||||
"""
|
|
||||||
PM Sensor humidity
|
|
||||||
"""
|
|
||||||
pm_voc_idx: builtins.float
|
|
||||||
"""
|
|
||||||
PM Sensor VOC Index
|
|
||||||
"""
|
|
||||||
pm_nox_idx: builtins.float
|
|
||||||
"""
|
|
||||||
PM Sensor NOx Index
|
|
||||||
"""
|
|
||||||
particles_tps: builtins.float
|
|
||||||
"""
|
|
||||||
Typical Particle Size in um
|
|
||||||
"""
|
"""
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@@ -1019,34 +709,12 @@ class AirQualityMetrics(google.protobuf.message.Message):
|
|||||||
particles_50um: builtins.int | None = ...,
|
particles_50um: builtins.int | None = ...,
|
||||||
particles_100um: builtins.int | None = ...,
|
particles_100um: builtins.int | None = ...,
|
||||||
co2: builtins.int | None = ...,
|
co2: builtins.int | None = ...,
|
||||||
co2_temperature: builtins.float | None = ...,
|
|
||||||
co2_humidity: builtins.float | None = ...,
|
|
||||||
form_formaldehyde: builtins.float | None = ...,
|
|
||||||
form_humidity: builtins.float | None = ...,
|
|
||||||
form_temperature: builtins.float | None = ...,
|
|
||||||
pm40_standard: builtins.int | None = ...,
|
|
||||||
particles_40um: builtins.int | None = ...,
|
|
||||||
pm_temperature: builtins.float | None = ...,
|
|
||||||
pm_humidity: builtins.float | None = ...,
|
|
||||||
pm_voc_idx: builtins.float | None = ...,
|
|
||||||
pm_nox_idx: builtins.float | None = ...,
|
|
||||||
particles_tps: builtins.float | None = ...,
|
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
def HasField(self, field_name: typing.Literal["_co2", b"_co2", "_co2_humidity", b"_co2_humidity", "_co2_temperature", b"_co2_temperature", "_form_formaldehyde", b"_form_formaldehyde", "_form_humidity", b"_form_humidity", "_form_temperature", b"_form_temperature", "_particles_03um", b"_particles_03um", "_particles_05um", b"_particles_05um", "_particles_100um", b"_particles_100um", "_particles_10um", b"_particles_10um", "_particles_25um", b"_particles_25um", "_particles_40um", b"_particles_40um", "_particles_50um", b"_particles_50um", "_particles_tps", b"_particles_tps", "_pm100_environmental", b"_pm100_environmental", "_pm100_standard", b"_pm100_standard", "_pm10_environmental", b"_pm10_environmental", "_pm10_standard", b"_pm10_standard", "_pm25_environmental", b"_pm25_environmental", "_pm25_standard", b"_pm25_standard", "_pm40_standard", b"_pm40_standard", "_pm_humidity", b"_pm_humidity", "_pm_nox_idx", b"_pm_nox_idx", "_pm_temperature", b"_pm_temperature", "_pm_voc_idx", b"_pm_voc_idx", "co2", b"co2", "co2_humidity", b"co2_humidity", "co2_temperature", b"co2_temperature", "form_formaldehyde", b"form_formaldehyde", "form_humidity", b"form_humidity", "form_temperature", b"form_temperature", "particles_03um", b"particles_03um", "particles_05um", b"particles_05um", "particles_100um", b"particles_100um", "particles_10um", b"particles_10um", "particles_25um", b"particles_25um", "particles_40um", b"particles_40um", "particles_50um", b"particles_50um", "particles_tps", b"particles_tps", "pm100_environmental", b"pm100_environmental", "pm100_standard", b"pm100_standard", "pm10_environmental", b"pm10_environmental", "pm10_standard", b"pm10_standard", "pm25_environmental", b"pm25_environmental", "pm25_standard", b"pm25_standard", "pm40_standard", b"pm40_standard", "pm_humidity", b"pm_humidity", "pm_nox_idx", b"pm_nox_idx", "pm_temperature", b"pm_temperature", "pm_voc_idx", b"pm_voc_idx"]) -> builtins.bool: ...
|
def HasField(self, field_name: typing.Literal["_co2", b"_co2", "_particles_03um", b"_particles_03um", "_particles_05um", b"_particles_05um", "_particles_100um", b"_particles_100um", "_particles_10um", b"_particles_10um", "_particles_25um", b"_particles_25um", "_particles_50um", b"_particles_50um", "_pm100_environmental", b"_pm100_environmental", "_pm100_standard", b"_pm100_standard", "_pm10_environmental", b"_pm10_environmental", "_pm10_standard", b"_pm10_standard", "_pm25_environmental", b"_pm25_environmental", "_pm25_standard", b"_pm25_standard", "co2", b"co2", "particles_03um", b"particles_03um", "particles_05um", b"particles_05um", "particles_100um", b"particles_100um", "particles_10um", b"particles_10um", "particles_25um", b"particles_25um", "particles_50um", b"particles_50um", "pm100_environmental", b"pm100_environmental", "pm100_standard", b"pm100_standard", "pm10_environmental", b"pm10_environmental", "pm10_standard", b"pm10_standard", "pm25_environmental", b"pm25_environmental", "pm25_standard", b"pm25_standard"]) -> builtins.bool: ...
|
||||||
def ClearField(self, field_name: typing.Literal["_co2", b"_co2", "_co2_humidity", b"_co2_humidity", "_co2_temperature", b"_co2_temperature", "_form_formaldehyde", b"_form_formaldehyde", "_form_humidity", b"_form_humidity", "_form_temperature", b"_form_temperature", "_particles_03um", b"_particles_03um", "_particles_05um", b"_particles_05um", "_particles_100um", b"_particles_100um", "_particles_10um", b"_particles_10um", "_particles_25um", b"_particles_25um", "_particles_40um", b"_particles_40um", "_particles_50um", b"_particles_50um", "_particles_tps", b"_particles_tps", "_pm100_environmental", b"_pm100_environmental", "_pm100_standard", b"_pm100_standard", "_pm10_environmental", b"_pm10_environmental", "_pm10_standard", b"_pm10_standard", "_pm25_environmental", b"_pm25_environmental", "_pm25_standard", b"_pm25_standard", "_pm40_standard", b"_pm40_standard", "_pm_humidity", b"_pm_humidity", "_pm_nox_idx", b"_pm_nox_idx", "_pm_temperature", b"_pm_temperature", "_pm_voc_idx", b"_pm_voc_idx", "co2", b"co2", "co2_humidity", b"co2_humidity", "co2_temperature", b"co2_temperature", "form_formaldehyde", b"form_formaldehyde", "form_humidity", b"form_humidity", "form_temperature", b"form_temperature", "particles_03um", b"particles_03um", "particles_05um", b"particles_05um", "particles_100um", b"particles_100um", "particles_10um", b"particles_10um", "particles_25um", b"particles_25um", "particles_40um", b"particles_40um", "particles_50um", b"particles_50um", "particles_tps", b"particles_tps", "pm100_environmental", b"pm100_environmental", "pm100_standard", b"pm100_standard", "pm10_environmental", b"pm10_environmental", "pm10_standard", b"pm10_standard", "pm25_environmental", b"pm25_environmental", "pm25_standard", b"pm25_standard", "pm40_standard", b"pm40_standard", "pm_humidity", b"pm_humidity", "pm_nox_idx", b"pm_nox_idx", "pm_temperature", b"pm_temperature", "pm_voc_idx", b"pm_voc_idx"]) -> None: ...
|
def ClearField(self, field_name: typing.Literal["_co2", b"_co2", "_particles_03um", b"_particles_03um", "_particles_05um", b"_particles_05um", "_particles_100um", b"_particles_100um", "_particles_10um", b"_particles_10um", "_particles_25um", b"_particles_25um", "_particles_50um", b"_particles_50um", "_pm100_environmental", b"_pm100_environmental", "_pm100_standard", b"_pm100_standard", "_pm10_environmental", b"_pm10_environmental", "_pm10_standard", b"_pm10_standard", "_pm25_environmental", b"_pm25_environmental", "_pm25_standard", b"_pm25_standard", "co2", b"co2", "particles_03um", b"particles_03um", "particles_05um", b"particles_05um", "particles_100um", b"particles_100um", "particles_10um", b"particles_10um", "particles_25um", b"particles_25um", "particles_50um", b"particles_50um", "pm100_environmental", b"pm100_environmental", "pm100_standard", b"pm100_standard", "pm10_environmental", b"pm10_environmental", "pm10_standard", b"pm10_standard", "pm25_environmental", b"pm25_environmental", "pm25_standard", b"pm25_standard"]) -> None: ...
|
||||||
@typing.overload
|
@typing.overload
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_co2", b"_co2"]) -> typing.Literal["co2"] | None: ...
|
def WhichOneof(self, oneof_group: typing.Literal["_co2", b"_co2"]) -> typing.Literal["co2"] | None: ...
|
||||||
@typing.overload
|
@typing.overload
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_co2_humidity", b"_co2_humidity"]) -> typing.Literal["co2_humidity"] | None: ...
|
|
||||||
@typing.overload
|
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_co2_temperature", b"_co2_temperature"]) -> typing.Literal["co2_temperature"] | None: ...
|
|
||||||
@typing.overload
|
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_form_formaldehyde", b"_form_formaldehyde"]) -> typing.Literal["form_formaldehyde"] | None: ...
|
|
||||||
@typing.overload
|
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_form_humidity", b"_form_humidity"]) -> typing.Literal["form_humidity"] | None: ...
|
|
||||||
@typing.overload
|
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_form_temperature", b"_form_temperature"]) -> typing.Literal["form_temperature"] | None: ...
|
|
||||||
@typing.overload
|
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_particles_03um", b"_particles_03um"]) -> typing.Literal["particles_03um"] | None: ...
|
def WhichOneof(self, oneof_group: typing.Literal["_particles_03um", b"_particles_03um"]) -> typing.Literal["particles_03um"] | None: ...
|
||||||
@typing.overload
|
@typing.overload
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_particles_05um", b"_particles_05um"]) -> typing.Literal["particles_05um"] | None: ...
|
def WhichOneof(self, oneof_group: typing.Literal["_particles_05um", b"_particles_05um"]) -> typing.Literal["particles_05um"] | None: ...
|
||||||
@@ -1057,12 +725,8 @@ class AirQualityMetrics(google.protobuf.message.Message):
|
|||||||
@typing.overload
|
@typing.overload
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_particles_25um", b"_particles_25um"]) -> typing.Literal["particles_25um"] | None: ...
|
def WhichOneof(self, oneof_group: typing.Literal["_particles_25um", b"_particles_25um"]) -> typing.Literal["particles_25um"] | None: ...
|
||||||
@typing.overload
|
@typing.overload
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_particles_40um", b"_particles_40um"]) -> typing.Literal["particles_40um"] | None: ...
|
|
||||||
@typing.overload
|
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_particles_50um", b"_particles_50um"]) -> typing.Literal["particles_50um"] | None: ...
|
def WhichOneof(self, oneof_group: typing.Literal["_particles_50um", b"_particles_50um"]) -> typing.Literal["particles_50um"] | None: ...
|
||||||
@typing.overload
|
@typing.overload
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_particles_tps", b"_particles_tps"]) -> typing.Literal["particles_tps"] | None: ...
|
|
||||||
@typing.overload
|
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_pm100_environmental", b"_pm100_environmental"]) -> typing.Literal["pm100_environmental"] | None: ...
|
def WhichOneof(self, oneof_group: typing.Literal["_pm100_environmental", b"_pm100_environmental"]) -> typing.Literal["pm100_environmental"] | None: ...
|
||||||
@typing.overload
|
@typing.overload
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_pm100_standard", b"_pm100_standard"]) -> typing.Literal["pm100_standard"] | None: ...
|
def WhichOneof(self, oneof_group: typing.Literal["_pm100_standard", b"_pm100_standard"]) -> typing.Literal["pm100_standard"] | None: ...
|
||||||
@@ -1074,16 +738,6 @@ class AirQualityMetrics(google.protobuf.message.Message):
|
|||||||
def WhichOneof(self, oneof_group: typing.Literal["_pm25_environmental", b"_pm25_environmental"]) -> typing.Literal["pm25_environmental"] | None: ...
|
def WhichOneof(self, oneof_group: typing.Literal["_pm25_environmental", b"_pm25_environmental"]) -> typing.Literal["pm25_environmental"] | None: ...
|
||||||
@typing.overload
|
@typing.overload
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_pm25_standard", b"_pm25_standard"]) -> typing.Literal["pm25_standard"] | None: ...
|
def WhichOneof(self, oneof_group: typing.Literal["_pm25_standard", b"_pm25_standard"]) -> typing.Literal["pm25_standard"] | None: ...
|
||||||
@typing.overload
|
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_pm40_standard", b"_pm40_standard"]) -> typing.Literal["pm40_standard"] | None: ...
|
|
||||||
@typing.overload
|
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_pm_humidity", b"_pm_humidity"]) -> typing.Literal["pm_humidity"] | None: ...
|
|
||||||
@typing.overload
|
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_pm_nox_idx", b"_pm_nox_idx"]) -> typing.Literal["pm_nox_idx"] | None: ...
|
|
||||||
@typing.overload
|
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_pm_temperature", b"_pm_temperature"]) -> typing.Literal["pm_temperature"] | None: ...
|
|
||||||
@typing.overload
|
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_pm_voc_idx", b"_pm_voc_idx"]) -> typing.Literal["pm_voc_idx"] | None: ...
|
|
||||||
|
|
||||||
global___AirQualityMetrics = AirQualityMetrics
|
global___AirQualityMetrics = AirQualityMetrics
|
||||||
|
|
||||||
@@ -1106,10 +760,6 @@ class LocalStats(google.protobuf.message.Message):
|
|||||||
NUM_RX_DUPE_FIELD_NUMBER: builtins.int
|
NUM_RX_DUPE_FIELD_NUMBER: builtins.int
|
||||||
NUM_TX_RELAY_FIELD_NUMBER: builtins.int
|
NUM_TX_RELAY_FIELD_NUMBER: builtins.int
|
||||||
NUM_TX_RELAY_CANCELED_FIELD_NUMBER: builtins.int
|
NUM_TX_RELAY_CANCELED_FIELD_NUMBER: builtins.int
|
||||||
HEAP_TOTAL_BYTES_FIELD_NUMBER: builtins.int
|
|
||||||
HEAP_FREE_BYTES_FIELD_NUMBER: builtins.int
|
|
||||||
NUM_TX_DROPPED_FIELD_NUMBER: builtins.int
|
|
||||||
NOISE_FLOOR_FIELD_NUMBER: builtins.int
|
|
||||||
uptime_seconds: builtins.int
|
uptime_seconds: builtins.int
|
||||||
"""
|
"""
|
||||||
How long the device has been running since the last reboot (in seconds)
|
How long the device has been running since the last reboot (in seconds)
|
||||||
@@ -1156,22 +806,6 @@ class LocalStats(google.protobuf.message.Message):
|
|||||||
Number of times we canceled a packet to be relayed, because someone else did it before us.
|
Number of times we canceled a packet to be relayed, because someone else did it before us.
|
||||||
This will always be zero for ROUTERs/REPEATERs. If this number is high, some other node(s) is/are relaying faster than you.
|
This will always be zero for ROUTERs/REPEATERs. If this number is high, some other node(s) is/are relaying faster than you.
|
||||||
"""
|
"""
|
||||||
heap_total_bytes: builtins.int
|
|
||||||
"""
|
|
||||||
Number of bytes used in the heap
|
|
||||||
"""
|
|
||||||
heap_free_bytes: builtins.int
|
|
||||||
"""
|
|
||||||
Number of bytes free in the heap
|
|
||||||
"""
|
|
||||||
num_tx_dropped: builtins.int
|
|
||||||
"""
|
|
||||||
Number of packets that were dropped because the transmit queue was full.
|
|
||||||
"""
|
|
||||||
noise_floor: builtins.int
|
|
||||||
"""
|
|
||||||
Noise floor value measured in dBm
|
|
||||||
"""
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
@@ -1186,73 +820,11 @@ class LocalStats(google.protobuf.message.Message):
|
|||||||
num_rx_dupe: builtins.int = ...,
|
num_rx_dupe: builtins.int = ...,
|
||||||
num_tx_relay: builtins.int = ...,
|
num_tx_relay: builtins.int = ...,
|
||||||
num_tx_relay_canceled: builtins.int = ...,
|
num_tx_relay_canceled: builtins.int = ...,
|
||||||
heap_total_bytes: builtins.int = ...,
|
|
||||||
heap_free_bytes: builtins.int = ...,
|
|
||||||
num_tx_dropped: builtins.int = ...,
|
|
||||||
noise_floor: builtins.int = ...,
|
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
def ClearField(self, field_name: typing.Literal["air_util_tx", b"air_util_tx", "channel_utilization", b"channel_utilization", "heap_free_bytes", b"heap_free_bytes", "heap_total_bytes", b"heap_total_bytes", "noise_floor", b"noise_floor", "num_online_nodes", b"num_online_nodes", "num_packets_rx", b"num_packets_rx", "num_packets_rx_bad", b"num_packets_rx_bad", "num_packets_tx", b"num_packets_tx", "num_rx_dupe", b"num_rx_dupe", "num_total_nodes", b"num_total_nodes", "num_tx_dropped", b"num_tx_dropped", "num_tx_relay", b"num_tx_relay", "num_tx_relay_canceled", b"num_tx_relay_canceled", "uptime_seconds", b"uptime_seconds"]) -> None: ...
|
def ClearField(self, field_name: typing.Literal["air_util_tx", b"air_util_tx", "channel_utilization", b"channel_utilization", "num_online_nodes", b"num_online_nodes", "num_packets_rx", b"num_packets_rx", "num_packets_rx_bad", b"num_packets_rx_bad", "num_packets_tx", b"num_packets_tx", "num_rx_dupe", b"num_rx_dupe", "num_total_nodes", b"num_total_nodes", "num_tx_relay", b"num_tx_relay", "num_tx_relay_canceled", b"num_tx_relay_canceled", "uptime_seconds", b"uptime_seconds"]) -> None: ...
|
||||||
|
|
||||||
global___LocalStats = LocalStats
|
global___LocalStats = LocalStats
|
||||||
|
|
||||||
@typing.final
|
|
||||||
class TrafficManagementStats(google.protobuf.message.Message):
|
|
||||||
"""
|
|
||||||
Traffic management statistics for mesh network optimization
|
|
||||||
"""
|
|
||||||
|
|
||||||
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
|
||||||
|
|
||||||
PACKETS_INSPECTED_FIELD_NUMBER: builtins.int
|
|
||||||
POSITION_DEDUP_DROPS_FIELD_NUMBER: builtins.int
|
|
||||||
NODEINFO_CACHE_HITS_FIELD_NUMBER: builtins.int
|
|
||||||
RATE_LIMIT_DROPS_FIELD_NUMBER: builtins.int
|
|
||||||
UNKNOWN_PACKET_DROPS_FIELD_NUMBER: builtins.int
|
|
||||||
HOP_EXHAUSTED_PACKETS_FIELD_NUMBER: builtins.int
|
|
||||||
ROUTER_HOPS_PRESERVED_FIELD_NUMBER: builtins.int
|
|
||||||
packets_inspected: builtins.int
|
|
||||||
"""
|
|
||||||
Total number of packets inspected by traffic management
|
|
||||||
"""
|
|
||||||
position_dedup_drops: builtins.int
|
|
||||||
"""
|
|
||||||
Number of position packets dropped due to deduplication
|
|
||||||
"""
|
|
||||||
nodeinfo_cache_hits: builtins.int
|
|
||||||
"""
|
|
||||||
Number of NodeInfo requests answered from cache
|
|
||||||
"""
|
|
||||||
rate_limit_drops: builtins.int
|
|
||||||
"""
|
|
||||||
Number of packets dropped due to rate limiting
|
|
||||||
"""
|
|
||||||
unknown_packet_drops: builtins.int
|
|
||||||
"""
|
|
||||||
Number of unknown/undecryptable packets dropped
|
|
||||||
"""
|
|
||||||
hop_exhausted_packets: builtins.int
|
|
||||||
"""
|
|
||||||
Number of packets with hop_limit exhausted for local-only broadcast
|
|
||||||
"""
|
|
||||||
router_hops_preserved: builtins.int
|
|
||||||
"""
|
|
||||||
Number of times router hop preservation was applied
|
|
||||||
"""
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
*,
|
|
||||||
packets_inspected: builtins.int = ...,
|
|
||||||
position_dedup_drops: builtins.int = ...,
|
|
||||||
nodeinfo_cache_hits: builtins.int = ...,
|
|
||||||
rate_limit_drops: builtins.int = ...,
|
|
||||||
unknown_packet_drops: builtins.int = ...,
|
|
||||||
hop_exhausted_packets: builtins.int = ...,
|
|
||||||
router_hops_preserved: builtins.int = ...,
|
|
||||||
) -> None: ...
|
|
||||||
def ClearField(self, field_name: typing.Literal["hop_exhausted_packets", b"hop_exhausted_packets", "nodeinfo_cache_hits", b"nodeinfo_cache_hits", "packets_inspected", b"packets_inspected", "position_dedup_drops", b"position_dedup_drops", "rate_limit_drops", b"rate_limit_drops", "router_hops_preserved", b"router_hops_preserved", "unknown_packet_drops", b"unknown_packet_drops"]) -> None: ...
|
|
||||||
|
|
||||||
global___TrafficManagementStats = TrafficManagementStats
|
|
||||||
|
|
||||||
@typing.final
|
@typing.final
|
||||||
class HealthMetrics(google.protobuf.message.Message):
|
class HealthMetrics(google.protobuf.message.Message):
|
||||||
"""
|
"""
|
||||||
@@ -1294,84 +866,6 @@ class HealthMetrics(google.protobuf.message.Message):
|
|||||||
|
|
||||||
global___HealthMetrics = HealthMetrics
|
global___HealthMetrics = HealthMetrics
|
||||||
|
|
||||||
@typing.final
|
|
||||||
class HostMetrics(google.protobuf.message.Message):
|
|
||||||
"""
|
|
||||||
Linux host metrics
|
|
||||||
"""
|
|
||||||
|
|
||||||
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
|
||||||
|
|
||||||
UPTIME_SECONDS_FIELD_NUMBER: builtins.int
|
|
||||||
FREEMEM_BYTES_FIELD_NUMBER: builtins.int
|
|
||||||
DISKFREE1_BYTES_FIELD_NUMBER: builtins.int
|
|
||||||
DISKFREE2_BYTES_FIELD_NUMBER: builtins.int
|
|
||||||
DISKFREE3_BYTES_FIELD_NUMBER: builtins.int
|
|
||||||
LOAD1_FIELD_NUMBER: builtins.int
|
|
||||||
LOAD5_FIELD_NUMBER: builtins.int
|
|
||||||
LOAD15_FIELD_NUMBER: builtins.int
|
|
||||||
USER_STRING_FIELD_NUMBER: builtins.int
|
|
||||||
uptime_seconds: builtins.int
|
|
||||||
"""
|
|
||||||
Host system uptime
|
|
||||||
"""
|
|
||||||
freemem_bytes: builtins.int
|
|
||||||
"""
|
|
||||||
Host system free memory
|
|
||||||
"""
|
|
||||||
diskfree1_bytes: builtins.int
|
|
||||||
"""
|
|
||||||
Host system disk space free for /
|
|
||||||
"""
|
|
||||||
diskfree2_bytes: builtins.int
|
|
||||||
"""
|
|
||||||
Secondary system disk space free
|
|
||||||
"""
|
|
||||||
diskfree3_bytes: builtins.int
|
|
||||||
"""
|
|
||||||
Tertiary disk space free
|
|
||||||
"""
|
|
||||||
load1: builtins.int
|
|
||||||
"""
|
|
||||||
Host system one minute load in 1/100ths
|
|
||||||
"""
|
|
||||||
load5: builtins.int
|
|
||||||
"""
|
|
||||||
Host system five minute load in 1/100ths
|
|
||||||
"""
|
|
||||||
load15: builtins.int
|
|
||||||
"""
|
|
||||||
Host system fifteen minute load in 1/100ths
|
|
||||||
"""
|
|
||||||
user_string: builtins.str
|
|
||||||
"""
|
|
||||||
Optional User-provided string for arbitrary host system information
|
|
||||||
that doesn't make sense as a dedicated entry.
|
|
||||||
"""
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
*,
|
|
||||||
uptime_seconds: builtins.int = ...,
|
|
||||||
freemem_bytes: builtins.int = ...,
|
|
||||||
diskfree1_bytes: builtins.int = ...,
|
|
||||||
diskfree2_bytes: builtins.int | None = ...,
|
|
||||||
diskfree3_bytes: builtins.int | None = ...,
|
|
||||||
load1: builtins.int = ...,
|
|
||||||
load5: builtins.int = ...,
|
|
||||||
load15: builtins.int = ...,
|
|
||||||
user_string: builtins.str | None = ...,
|
|
||||||
) -> None: ...
|
|
||||||
def HasField(self, field_name: typing.Literal["_diskfree2_bytes", b"_diskfree2_bytes", "_diskfree3_bytes", b"_diskfree3_bytes", "_user_string", b"_user_string", "diskfree2_bytes", b"diskfree2_bytes", "diskfree3_bytes", b"diskfree3_bytes", "user_string", b"user_string"]) -> builtins.bool: ...
|
|
||||||
def ClearField(self, field_name: typing.Literal["_diskfree2_bytes", b"_diskfree2_bytes", "_diskfree3_bytes", b"_diskfree3_bytes", "_user_string", b"_user_string", "diskfree1_bytes", b"diskfree1_bytes", "diskfree2_bytes", b"diskfree2_bytes", "diskfree3_bytes", b"diskfree3_bytes", "freemem_bytes", b"freemem_bytes", "load1", b"load1", "load15", b"load15", "load5", b"load5", "uptime_seconds", b"uptime_seconds", "user_string", b"user_string"]) -> None: ...
|
|
||||||
@typing.overload
|
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_diskfree2_bytes", b"_diskfree2_bytes"]) -> typing.Literal["diskfree2_bytes"] | None: ...
|
|
||||||
@typing.overload
|
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_diskfree3_bytes", b"_diskfree3_bytes"]) -> typing.Literal["diskfree3_bytes"] | None: ...
|
|
||||||
@typing.overload
|
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_user_string", b"_user_string"]) -> typing.Literal["user_string"] | None: ...
|
|
||||||
|
|
||||||
global___HostMetrics = HostMetrics
|
|
||||||
|
|
||||||
@typing.final
|
@typing.final
|
||||||
class Telemetry(google.protobuf.message.Message):
|
class Telemetry(google.protobuf.message.Message):
|
||||||
"""
|
"""
|
||||||
@@ -1387,8 +881,6 @@ class Telemetry(google.protobuf.message.Message):
|
|||||||
POWER_METRICS_FIELD_NUMBER: builtins.int
|
POWER_METRICS_FIELD_NUMBER: builtins.int
|
||||||
LOCAL_STATS_FIELD_NUMBER: builtins.int
|
LOCAL_STATS_FIELD_NUMBER: builtins.int
|
||||||
HEALTH_METRICS_FIELD_NUMBER: builtins.int
|
HEALTH_METRICS_FIELD_NUMBER: builtins.int
|
||||||
HOST_METRICS_FIELD_NUMBER: builtins.int
|
|
||||||
TRAFFIC_MANAGEMENT_STATS_FIELD_NUMBER: builtins.int
|
|
||||||
time: builtins.int
|
time: builtins.int
|
||||||
"""
|
"""
|
||||||
Seconds since 1970 - or 0 for unknown/unset
|
Seconds since 1970 - or 0 for unknown/unset
|
||||||
@@ -1429,18 +921,6 @@ class Telemetry(google.protobuf.message.Message):
|
|||||||
Health telemetry metrics
|
Health telemetry metrics
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@property
|
|
||||||
def host_metrics(self) -> global___HostMetrics:
|
|
||||||
"""
|
|
||||||
Linux host metrics
|
|
||||||
"""
|
|
||||||
|
|
||||||
@property
|
|
||||||
def traffic_management_stats(self) -> global___TrafficManagementStats:
|
|
||||||
"""
|
|
||||||
Traffic management statistics
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
@@ -1451,12 +931,10 @@ class Telemetry(google.protobuf.message.Message):
|
|||||||
power_metrics: global___PowerMetrics | None = ...,
|
power_metrics: global___PowerMetrics | None = ...,
|
||||||
local_stats: global___LocalStats | None = ...,
|
local_stats: global___LocalStats | None = ...,
|
||||||
health_metrics: global___HealthMetrics | None = ...,
|
health_metrics: global___HealthMetrics | None = ...,
|
||||||
host_metrics: global___HostMetrics | None = ...,
|
|
||||||
traffic_management_stats: global___TrafficManagementStats | None = ...,
|
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
def HasField(self, field_name: typing.Literal["air_quality_metrics", b"air_quality_metrics", "device_metrics", b"device_metrics", "environment_metrics", b"environment_metrics", "health_metrics", b"health_metrics", "host_metrics", b"host_metrics", "local_stats", b"local_stats", "power_metrics", b"power_metrics", "traffic_management_stats", b"traffic_management_stats", "variant", b"variant"]) -> builtins.bool: ...
|
def HasField(self, field_name: typing.Literal["air_quality_metrics", b"air_quality_metrics", "device_metrics", b"device_metrics", "environment_metrics", b"environment_metrics", "health_metrics", b"health_metrics", "local_stats", b"local_stats", "power_metrics", b"power_metrics", "variant", b"variant"]) -> builtins.bool: ...
|
||||||
def ClearField(self, field_name: typing.Literal["air_quality_metrics", b"air_quality_metrics", "device_metrics", b"device_metrics", "environment_metrics", b"environment_metrics", "health_metrics", b"health_metrics", "host_metrics", b"host_metrics", "local_stats", b"local_stats", "power_metrics", b"power_metrics", "time", b"time", "traffic_management_stats", b"traffic_management_stats", "variant", b"variant"]) -> None: ...
|
def ClearField(self, field_name: typing.Literal["air_quality_metrics", b"air_quality_metrics", "device_metrics", b"device_metrics", "environment_metrics", b"environment_metrics", "health_metrics", b"health_metrics", "local_stats", b"local_stats", "power_metrics", b"power_metrics", "time", b"time", "variant", b"variant"]) -> None: ...
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["variant", b"variant"]) -> typing.Literal["device_metrics", "environment_metrics", "air_quality_metrics", "power_metrics", "local_stats", "health_metrics", "host_metrics", "traffic_management_stats"] | None: ...
|
def WhichOneof(self, oneof_group: typing.Literal["variant", b"variant"]) -> typing.Literal["device_metrics", "environment_metrics", "air_quality_metrics", "power_metrics", "local_stats", "health_metrics"] | None: ...
|
||||||
|
|
||||||
global___Telemetry = Telemetry
|
global___Telemetry = Telemetry
|
||||||
|
|
||||||
@@ -1487,62 +965,3 @@ class Nau7802Config(google.protobuf.message.Message):
|
|||||||
def ClearField(self, field_name: typing.Literal["calibrationFactor", b"calibrationFactor", "zeroOffset", b"zeroOffset"]) -> None: ...
|
def ClearField(self, field_name: typing.Literal["calibrationFactor", b"calibrationFactor", "zeroOffset", b"zeroOffset"]) -> None: ...
|
||||||
|
|
||||||
global___Nau7802Config = Nau7802Config
|
global___Nau7802Config = Nau7802Config
|
||||||
|
|
||||||
@typing.final
|
|
||||||
class SEN5XState(google.protobuf.message.Message):
|
|
||||||
"""
|
|
||||||
SEN5X State, for saving to flash
|
|
||||||
"""
|
|
||||||
|
|
||||||
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
|
||||||
|
|
||||||
LAST_CLEANING_TIME_FIELD_NUMBER: builtins.int
|
|
||||||
LAST_CLEANING_VALID_FIELD_NUMBER: builtins.int
|
|
||||||
ONE_SHOT_MODE_FIELD_NUMBER: builtins.int
|
|
||||||
VOC_STATE_TIME_FIELD_NUMBER: builtins.int
|
|
||||||
VOC_STATE_VALID_FIELD_NUMBER: builtins.int
|
|
||||||
VOC_STATE_ARRAY_FIELD_NUMBER: builtins.int
|
|
||||||
last_cleaning_time: builtins.int
|
|
||||||
"""
|
|
||||||
Last cleaning time for SEN5X
|
|
||||||
"""
|
|
||||||
last_cleaning_valid: builtins.bool
|
|
||||||
"""
|
|
||||||
Last cleaning time for SEN5X - valid flag
|
|
||||||
"""
|
|
||||||
one_shot_mode: builtins.bool
|
|
||||||
"""
|
|
||||||
Config flag for one-shot mode (see admin.proto)
|
|
||||||
"""
|
|
||||||
voc_state_time: builtins.int
|
|
||||||
"""
|
|
||||||
Last VOC state time for SEN55
|
|
||||||
"""
|
|
||||||
voc_state_valid: builtins.bool
|
|
||||||
"""
|
|
||||||
Last VOC state validity flag for SEN55
|
|
||||||
"""
|
|
||||||
voc_state_array: builtins.int
|
|
||||||
"""
|
|
||||||
VOC state array (8x uint8t) for SEN55
|
|
||||||
"""
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
*,
|
|
||||||
last_cleaning_time: builtins.int = ...,
|
|
||||||
last_cleaning_valid: builtins.bool = ...,
|
|
||||||
one_shot_mode: builtins.bool = ...,
|
|
||||||
voc_state_time: builtins.int | None = ...,
|
|
||||||
voc_state_valid: builtins.bool | None = ...,
|
|
||||||
voc_state_array: builtins.int | None = ...,
|
|
||||||
) -> None: ...
|
|
||||||
def HasField(self, field_name: typing.Literal["_voc_state_array", b"_voc_state_array", "_voc_state_time", b"_voc_state_time", "_voc_state_valid", b"_voc_state_valid", "voc_state_array", b"voc_state_array", "voc_state_time", b"voc_state_time", "voc_state_valid", b"voc_state_valid"]) -> builtins.bool: ...
|
|
||||||
def ClearField(self, field_name: typing.Literal["_voc_state_array", b"_voc_state_array", "_voc_state_time", b"_voc_state_time", "_voc_state_valid", b"_voc_state_valid", "last_cleaning_time", b"last_cleaning_time", "last_cleaning_valid", b"last_cleaning_valid", "one_shot_mode", b"one_shot_mode", "voc_state_array", b"voc_state_array", "voc_state_time", b"voc_state_time", "voc_state_valid", b"voc_state_valid"]) -> None: ...
|
|
||||||
@typing.overload
|
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_voc_state_array", b"_voc_state_array"]) -> typing.Literal["voc_state_array"] | None: ...
|
|
||||||
@typing.overload
|
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_voc_state_time", b"_voc_state_time"]) -> typing.Literal["voc_state_time"] | None: ...
|
|
||||||
@typing.overload
|
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_voc_state_valid", b"_voc_state_valid"]) -> typing.Literal["voc_state_valid"] | None: ...
|
|
||||||
|
|
||||||
global___SEN5XState = SEN5XState
|
|
||||||
|
|||||||
19
meshtastic/protobuf/xmodem_pb2.py
generated
19
meshtastic/protobuf/xmodem_pb2.py
generated
@@ -11,25 +11,18 @@ from google.protobuf.internal import builder as _builder
|
|||||||
_sym_db = _symbol_database.Default()
|
_sym_db = _symbol_database.Default()
|
||||||
|
|
||||||
|
|
||||||
from meshtastic.protobuf import nanopb_pb2 as meshtastic_dot_protobuf_dot_nanopb__pb2
|
|
||||||
|
|
||||||
|
|
||||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n meshtastic/protobuf/xmodem.proto\x12\x13meshtastic.protobuf\x1a meshtastic/protobuf/nanopb.proto\"\xd5\x01\n\x06XModem\x12\x34\n\x07\x63ontrol\x18\x01 \x01(\x0e\x32#.meshtastic.protobuf.XModem.Control\x12\x12\n\x03seq\x18\x02 \x01(\rB\x05\x92?\x02\x38\x10\x12\x14\n\x05\x63rc16\x18\x03 \x01(\rB\x05\x92?\x02\x38\x10\x12\x16\n\x06\x62uffer\x18\x04 \x01(\x0c\x42\x06\x92?\x03\x08\x80\x01\"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\x62\n\x14org.meshtastic.protoB\x0cXmodemProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
|
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n meshtastic/protobuf/xmodem.proto\x12\x13meshtastic.protobuf\"\xbf\x01\n\x06XModem\x12\x34\n\x07\x63ontrol\x18\x01 \x01(\x0e\x32#.meshtastic.protobuf.XModem.Control\x12\x0b\n\x03seq\x18\x02 \x01(\r\x12\r\n\x05\x63rc16\x18\x03 \x01(\r\x12\x0e\n\x06\x62uffer\x18\x04 \x01(\x0c\"S\n\x07\x43ontrol\x12\x07\n\x03NUL\x10\x00\x12\x07\n\x03SOH\x10\x01\x12\x07\n\x03STX\x10\x02\x12\x07\n\x03\x45OT\x10\x04\x12\x07\n\x03\x41\x43K\x10\x06\x12\x07\n\x03NAK\x10\x15\x12\x07\n\x03\x43\x41N\x10\x18\x12\t\n\x05\x43TRLZ\x10\x1a\x42\x61\n\x13\x63om.geeksville.meshB\x0cXmodemProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
|
||||||
|
|
||||||
_globals = globals()
|
_globals = globals()
|
||||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.xmodem_pb2', _globals)
|
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.xmodem_pb2', _globals)
|
||||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||||
DESCRIPTOR._options = None
|
DESCRIPTOR._options = None
|
||||||
DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\014XmodemProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\014XmodemProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||||
_XMODEM.fields_by_name['seq']._options = None
|
_globals['_XMODEM']._serialized_start=58
|
||||||
_XMODEM.fields_by_name['seq']._serialized_options = b'\222?\0028\020'
|
_globals['_XMODEM']._serialized_end=249
|
||||||
_XMODEM.fields_by_name['crc16']._options = None
|
_globals['_XMODEM_CONTROL']._serialized_start=166
|
||||||
_XMODEM.fields_by_name['crc16']._serialized_options = b'\222?\0028\020'
|
_globals['_XMODEM_CONTROL']._serialized_end=249
|
||||||
_XMODEM.fields_by_name['buffer']._options = None
|
|
||||||
_XMODEM.fields_by_name['buffer']._serialized_options = b'\222?\003\010\200\001'
|
|
||||||
_globals['_XMODEM']._serialized_start=92
|
|
||||||
_globals['_XMODEM']._serialized_end=305
|
|
||||||
_globals['_XMODEM_CONTROL']._serialized_start=222
|
|
||||||
_globals['_XMODEM_CONTROL']._serialized_end=305
|
|
||||||
# @@protoc_insertion_point(module_scope)
|
# @@protoc_insertion_point(module_scope)
|
||||||
|
|||||||
@@ -7,11 +7,10 @@ from pubsub import pub # type: ignore[import-untyped]
|
|||||||
from meshtastic.protobuf import portnums_pb2, remote_hardware_pb2
|
from meshtastic.protobuf import portnums_pb2, remote_hardware_pb2
|
||||||
from meshtastic.util import our_exit
|
from meshtastic.util import our_exit
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
def onGPIOreceive(packet, interface) -> None:
|
def onGPIOreceive(packet, interface) -> None:
|
||||||
"""Callback for received GPIO responses"""
|
"""Callback for received GPIO responses"""
|
||||||
logger.debug(f"packet:{packet} interface:{interface}")
|
logging.debug(f"packet:{packet} interface:{interface}")
|
||||||
gpioValue = 0
|
gpioValue = 0
|
||||||
hw = packet["decoded"]["remotehw"]
|
hw = packet["decoded"]["remotehw"]
|
||||||
if "gpioValue" in hw:
|
if "gpioValue" in hw:
|
||||||
@@ -77,7 +76,7 @@ class RemoteHardwareClient:
|
|||||||
Write the specified vals bits to the device GPIOs. Only bits in mask that
|
Write the specified vals bits to the device GPIOs. Only bits in mask that
|
||||||
are 1 will be changed
|
are 1 will be changed
|
||||||
"""
|
"""
|
||||||
logger.debug(f"writeGPIOs nodeid:{nodeid} mask:{mask} vals:{vals}")
|
logging.debug(f"writeGPIOs nodeid:{nodeid} mask:{mask} vals:{vals}")
|
||||||
r = remote_hardware_pb2.HardwareMessage()
|
r = remote_hardware_pb2.HardwareMessage()
|
||||||
r.type = remote_hardware_pb2.HardwareMessage.Type.WRITE_GPIOS
|
r.type = remote_hardware_pb2.HardwareMessage.Type.WRITE_GPIOS
|
||||||
r.gpio_mask = mask
|
r.gpio_mask = mask
|
||||||
@@ -86,7 +85,7 @@ class RemoteHardwareClient:
|
|||||||
|
|
||||||
def readGPIOs(self, nodeid, mask, onResponse=None):
|
def readGPIOs(self, nodeid, mask, onResponse=None):
|
||||||
"""Read the specified bits from GPIO inputs on the device"""
|
"""Read the specified bits from GPIO inputs on the device"""
|
||||||
logger.debug(f"readGPIOs nodeid:{nodeid} mask:{mask}")
|
logging.debug(f"readGPIOs nodeid:{nodeid} mask:{mask}")
|
||||||
r = remote_hardware_pb2.HardwareMessage()
|
r = remote_hardware_pb2.HardwareMessage()
|
||||||
r.type = remote_hardware_pb2.HardwareMessage.Type.READ_GPIOS
|
r.type = remote_hardware_pb2.HardwareMessage.Type.READ_GPIOS
|
||||||
r.gpio_mask = mask
|
r.gpio_mask = mask
|
||||||
@@ -94,7 +93,7 @@ class RemoteHardwareClient:
|
|||||||
|
|
||||||
def watchGPIOs(self, nodeid, mask):
|
def watchGPIOs(self, nodeid, mask):
|
||||||
"""Watch the specified bits from GPIO inputs on the device for changes"""
|
"""Watch the specified bits from GPIO inputs on the device for changes"""
|
||||||
logger.debug(f"watchGPIOs nodeid:{nodeid} mask:{mask}")
|
logging.debug(f"watchGPIOs nodeid:{nodeid} mask:{mask}")
|
||||||
r = remote_hardware_pb2.HardwareMessage()
|
r = remote_hardware_pb2.HardwareMessage()
|
||||||
r.type = remote_hardware_pb2.HardwareMessage.Type.WATCH_GPIOS
|
r.type = remote_hardware_pb2.HardwareMessage.Type.WATCH_GPIOS
|
||||||
r.gpio_mask = mask
|
r.gpio_mask = mask
|
||||||
|
|||||||
@@ -2,9 +2,8 @@
|
|||||||
"""
|
"""
|
||||||
# pylint: disable=R0917
|
# pylint: disable=R0917
|
||||||
import logging
|
import logging
|
||||||
import sys
|
import platform
|
||||||
import time
|
import time
|
||||||
from io import TextIOWrapper
|
|
||||||
|
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
|
|
||||||
@@ -13,33 +12,28 @@ import serial # type: ignore[import-untyped]
|
|||||||
import meshtastic.util
|
import meshtastic.util
|
||||||
from meshtastic.stream_interface import StreamInterface
|
from meshtastic.stream_interface import StreamInterface
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
if platform.system() != "Windows":
|
||||||
|
import termios
|
||||||
|
|
||||||
|
|
||||||
class SerialInterface(StreamInterface):
|
class SerialInterface(StreamInterface):
|
||||||
"""Interface class for meshtastic devices over a serial link"""
|
"""Interface class for meshtastic devices over a serial link"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(self, devPath: Optional[str]=None, debugOut=None, noProto: bool=False, connectNow: bool=True, noNodes: bool=False) -> None:
|
||||||
self,
|
|
||||||
devPath: Optional[str] = None,
|
|
||||||
debugOut=None,
|
|
||||||
noProto: bool = False,
|
|
||||||
connectNow: bool = True,
|
|
||||||
noNodes: bool = False,
|
|
||||||
timeout: int = 300
|
|
||||||
) -> None:
|
|
||||||
"""Constructor, opens a connection to a specified serial port, or if unspecified try to
|
"""Constructor, opens a connection to a specified serial port, or if unspecified try to
|
||||||
find one Meshtastic device by probing
|
find one Meshtastic device by probing
|
||||||
|
|
||||||
Keyword Arguments:
|
Keyword Arguments:
|
||||||
devPath {string} -- A filepath to a device, i.e. /dev/ttyUSB0 (default: {None})
|
devPath {string} -- A filepath to a device, i.e. /dev/ttyUSB0 (default: {None})
|
||||||
debugOut {stream} -- If a stream is provided, any debug serial output from the device will be emitted to that stream. (default: {None})
|
debugOut {stream} -- If a stream is provided, any debug serial output from the device will be emitted to that stream. (default: {None})
|
||||||
timeout -- How long to wait for replies (default: 300 seconds)
|
|
||||||
"""
|
"""
|
||||||
|
self.noProto = noProto
|
||||||
|
|
||||||
self.devPath: Optional[str] = devPath
|
self.devPath: Optional[str] = devPath
|
||||||
|
|
||||||
if self.devPath is None:
|
if self.devPath is None:
|
||||||
ports: List[str] = meshtastic.util.findPorts(True)
|
ports: List[str] = meshtastic.util.findPorts(True)
|
||||||
logger.debug(f"ports:{ports}")
|
logging.debug(f"ports:{ports}")
|
||||||
if len(ports) == 0:
|
if len(ports) == 0:
|
||||||
print("No Serial Meshtastic device detected, attempting TCP connection on localhost.")
|
print("No Serial Meshtastic device detected, attempting TCP connection on localhost.")
|
||||||
return
|
return
|
||||||
@@ -50,51 +44,27 @@ class SerialInterface(StreamInterface):
|
|||||||
else:
|
else:
|
||||||
self.devPath = ports[0]
|
self.devPath = ports[0]
|
||||||
|
|
||||||
StreamInterface.__init__(
|
logging.debug(f"Connecting to {self.devPath}")
|
||||||
self, debugOut=debugOut, noProto=noProto, connectNow=connectNow, noNodes=noNodes, timeout=timeout
|
|
||||||
)
|
|
||||||
|
|
||||||
def connect(self) -> None:
|
# first we need to set the HUPCL so the device will not reboot based on RTS and/or DTR
|
||||||
logger.debug(f"Connecting to {self.devPath}")
|
# see https://github.com/pyserial/pyserial/issues/124
|
||||||
dev_path = self.devPath
|
if platform.system() != "Windows":
|
||||||
if dev_path is None:
|
with open(self.devPath, encoding="utf8") as f:
|
||||||
raise RuntimeError("Serial device path is not set")
|
attrs = termios.tcgetattr(f)
|
||||||
|
attrs[2] = attrs[2] & ~termios.HUPCL
|
||||||
if sys.platform != "win32":
|
termios.tcsetattr(f, termios.TCSAFLUSH, attrs)
|
||||||
with open(dev_path, encoding="utf8") as f:
|
f.close()
|
||||||
self._set_hupcl_with_termios(f)
|
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
|
|
||||||
self.stream = serial.Serial(
|
self.stream = serial.Serial(
|
||||||
dev_path, 115200, exclusive=True, timeout=0.5, write_timeout=0
|
self.devPath, 115200, exclusive=True, timeout=0.5, write_timeout=0
|
||||||
)
|
)
|
||||||
self.stream.flush() # type: ignore[attr-defined]
|
self.stream.flush() # type: ignore[attr-defined]
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
|
|
||||||
super().connect()
|
StreamInterface.__init__(
|
||||||
|
self, debugOut=debugOut, noProto=noProto, connectNow=connectNow, noNodes=noNodes
|
||||||
def _set_hupcl_with_termios(self, f: TextIOWrapper):
|
)
|
||||||
"""first we need to set the HUPCL so the device will not reboot based on RTS and/or DTR
|
|
||||||
see https://github.com/pyserial/pyserial/issues/124
|
|
||||||
"""
|
|
||||||
if sys.platform == "win32":
|
|
||||||
return
|
|
||||||
|
|
||||||
import termios # pylint: disable=C0415,E0401
|
|
||||||
attrs = termios.tcgetattr(f)
|
|
||||||
attrs[2] = attrs[2] & ~termios.HUPCL
|
|
||||||
termios.tcsetattr(f, termios.TCSAFLUSH, attrs)
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
rep = f"SerialInterface(devPath={self.devPath!r}"
|
|
||||||
if hasattr(self, 'debugOut') and self.debugOut is not None:
|
|
||||||
rep += f", debugOut={self.debugOut!r}"
|
|
||||||
if self.noProto:
|
|
||||||
rep += ", noProto=True"
|
|
||||||
if hasattr(self, 'noNodes') and self.noNodes:
|
|
||||||
rep += ", noNodes=True"
|
|
||||||
rep += ")"
|
|
||||||
return rep
|
|
||||||
|
|
||||||
def close(self) -> None:
|
def close(self) -> None:
|
||||||
"""Close a connection to the device"""
|
"""Close a connection to the device"""
|
||||||
@@ -103,5 +73,5 @@ class SerialInterface(StreamInterface):
|
|||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
self.stream.flush()
|
self.stream.flush()
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
logger.debug("Closing Serial stream")
|
logging.debug("Closing Serial stream")
|
||||||
StreamInterface.close(self)
|
StreamInterface.close(self)
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import time
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
from pathlib import Path
|
|
||||||
from typing import Optional, List, Tuple
|
from typing import Optional, List, Tuple
|
||||||
|
|
||||||
import parse # type: ignore[import-untyped]
|
import parse # type: ignore[import-untyped]
|
||||||
@@ -23,7 +22,6 @@ from meshtastic.powermon import PowerMeter
|
|||||||
|
|
||||||
from .arrow import FeatherWriter
|
from .arrow import FeatherWriter
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
def root_dir() -> str:
|
def root_dir() -> str:
|
||||||
"""Return the root directory for slog files."""
|
"""Return the root directory for slog files."""
|
||||||
@@ -31,9 +29,9 @@ def root_dir() -> str:
|
|||||||
app_name = "meshtastic"
|
app_name = "meshtastic"
|
||||||
app_author = "meshtastic"
|
app_author = "meshtastic"
|
||||||
app_dir = platformdirs.user_data_dir(app_name, app_author)
|
app_dir = platformdirs.user_data_dir(app_name, app_author)
|
||||||
dir_name = Path(app_dir, "slogs")
|
dir_name = f"{app_dir}/slogs"
|
||||||
dir_name.mkdir(exist_ok=True, parents=True)
|
os.makedirs(dir_name, exist_ok=True)
|
||||||
return str(dir_name)
|
return dir_name
|
||||||
|
|
||||||
|
|
||||||
@dataclass(init=False)
|
@dataclass(init=False)
|
||||||
@@ -146,7 +144,7 @@ class StructuredLogger:
|
|||||||
self.power_logger = power_logger
|
self.power_logger = power_logger
|
||||||
|
|
||||||
# Setup the arrow writer (and its schema)
|
# Setup the arrow writer (and its schema)
|
||||||
self.writer = FeatherWriter(os.path.join(dir_path, "slog"))
|
self.writer = FeatherWriter(f"{dir_path}/slog")
|
||||||
all_fields = reduce(
|
all_fields = reduce(
|
||||||
(lambda x, y: x + y), map(lambda x: x.fields, log_defs.values())
|
(lambda x, y: x + y), map(lambda x: x.fields, log_defs.values())
|
||||||
)
|
)
|
||||||
@@ -166,7 +164,7 @@ class StructuredLogger:
|
|||||||
self.raw_file: Optional[
|
self.raw_file: Optional[
|
||||||
io.TextIOWrapper
|
io.TextIOWrapper
|
||||||
] = open( # pylint: disable=consider-using-with
|
] = open( # pylint: disable=consider-using-with
|
||||||
os.path.join(dir_path, "raw.txt"), "w", encoding="utf8"
|
f"{dir_path}/raw.txt", "w", encoding="utf8"
|
||||||
)
|
)
|
||||||
|
|
||||||
# We need a closure here because the subscription API is very strict about exact arg matching
|
# We need a closure here because the subscription API is very strict about exact arg matching
|
||||||
@@ -199,7 +197,7 @@ class StructuredLogger:
|
|||||||
if m:
|
if m:
|
||||||
src = m.group(1)
|
src = m.group(1)
|
||||||
args = m.group(2)
|
args = m.group(2)
|
||||||
logger.debug(f"SLog {src}, args: {args}")
|
logging.debug(f"SLog {src}, args: {args}")
|
||||||
|
|
||||||
d = log_defs.get(src)
|
d = log_defs.get(src)
|
||||||
if d:
|
if d:
|
||||||
@@ -221,9 +219,9 @@ class StructuredLogger:
|
|||||||
# If the last field is an empty string, remove it
|
# If the last field is an empty string, remove it
|
||||||
del di[last_field[0]]
|
del di[last_field[0]]
|
||||||
else:
|
else:
|
||||||
logger.warning(f"Failed to parse slog {line} with {d.format}")
|
logging.warning(f"Failed to parse slog {line} with {d.format}")
|
||||||
else:
|
else:
|
||||||
logger.warning(f"Unknown Structured Log: {line}")
|
logging.warning(f"Unknown Structured Log: {line}")
|
||||||
|
|
||||||
# Store our structured log record
|
# Store our structured log record
|
||||||
if di or self.include_raw:
|
if di or self.include_raw:
|
||||||
@@ -258,28 +256,23 @@ class LogSet:
|
|||||||
|
|
||||||
if not dir_name:
|
if not dir_name:
|
||||||
app_dir = root_dir()
|
app_dir = root_dir()
|
||||||
app_time_dir = Path(app_dir, datetime.now().strftime('%Y%m%d-%H%M%S'))
|
dir_name = f"{app_dir}/{datetime.now().strftime('%Y%m%d-%H%M%S')}"
|
||||||
app_time_dir.mkdir(exist_ok=True)
|
os.makedirs(dir_name, exist_ok=True)
|
||||||
dir_name = str(app_time_dir)
|
|
||||||
|
|
||||||
# Also make a 'latest' directory that always points to the most recent logs
|
# Also make a 'latest' directory that always points to the most recent logs
|
||||||
latest_dir = Path(app_dir, "latest")
|
|
||||||
latest_dir.unlink(missing_ok=True)
|
|
||||||
|
|
||||||
# symlink might fail on some platforms, if it does fail silently
|
# symlink might fail on some platforms, if it does fail silently
|
||||||
try:
|
if os.path.exists(f"{app_dir}/latest"):
|
||||||
latest_dir.symlink_to(dir_name, target_is_directory=True)
|
os.unlink(f"{app_dir}/latest")
|
||||||
except OSError:
|
os.symlink(dir_name, f"{app_dir}/latest", target_is_directory=True)
|
||||||
pass
|
|
||||||
|
|
||||||
self.dir_name = dir_name
|
self.dir_name = dir_name
|
||||||
|
|
||||||
logger.info(f"Writing slogs to {dir_name}")
|
logging.info(f"Writing slogs to {dir_name}")
|
||||||
|
|
||||||
self.power_logger: Optional[PowerLogger] = (
|
self.power_logger: Optional[PowerLogger] = (
|
||||||
None
|
None
|
||||||
if not power_meter
|
if not power_meter
|
||||||
else PowerLogger(power_meter, os.path.join(self.dir_name, "power"))
|
else PowerLogger(power_meter, f"{self.dir_name}/power")
|
||||||
)
|
)
|
||||||
|
|
||||||
self.slog_logger: Optional[StructuredLogger] = StructuredLogger(
|
self.slog_logger: Optional[StructuredLogger] = StructuredLogger(
|
||||||
@@ -293,7 +286,7 @@ class LogSet:
|
|||||||
"""Close the log set."""
|
"""Close the log set."""
|
||||||
|
|
||||||
if self.slog_logger:
|
if self.slog_logger:
|
||||||
logger.info(f"Closing slogs in {self.dir_name}")
|
logging.info(f"Closing slogs in {self.dir_name}")
|
||||||
atexit.unregister(
|
atexit.unregister(
|
||||||
self.atexit_handler
|
self.atexit_handler
|
||||||
) # docs say it will silently ignore if not found
|
) # docs say it will silently ignore if not found
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
"""Stream Interface base class
|
"""Stream Interface base class
|
||||||
"""
|
"""
|
||||||
import contextlib
|
|
||||||
import io
|
import io
|
||||||
import logging
|
import logging
|
||||||
import threading
|
import threading
|
||||||
@@ -18,36 +17,28 @@ START1 = 0x94
|
|||||||
START2 = 0xC3
|
START2 = 0xC3
|
||||||
HEADER_LEN = 4
|
HEADER_LEN = 4
|
||||||
MAX_TO_FROM_RADIO_SIZE = 512
|
MAX_TO_FROM_RADIO_SIZE = 512
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class StreamInterface(MeshInterface):
|
class StreamInterface(MeshInterface):
|
||||||
"""Interface class for meshtastic devices over a stream link (serial, TCP, etc)"""
|
"""Interface class for meshtastic devices over a stream link (serial, TCP, etc)"""
|
||||||
|
|
||||||
def __init__( # pylint: disable=R0917
|
def __init__(self, debugOut: Optional[io.TextIOWrapper]=None, noProto: bool=False, connectNow: bool=True, noNodes: bool=False) -> None:
|
||||||
self,
|
|
||||||
debugOut: Optional[io.TextIOWrapper] = None,
|
|
||||||
noProto: bool = False,
|
|
||||||
connectNow: bool = True,
|
|
||||||
noNodes: bool = False,
|
|
||||||
timeout: int = 300
|
|
||||||
) -> None:
|
|
||||||
"""Constructor, opens a connection to self.stream
|
"""Constructor, opens a connection to self.stream
|
||||||
|
|
||||||
Keyword Arguments:
|
Keyword Arguments:
|
||||||
debugOut {stream} -- If a stream is provided, any debug serial output from the
|
debugOut {stream} -- If a stream is provided, any debug serial output from the
|
||||||
device will be emitted to that stream. (default: {None})
|
device will be emitted to that stream. (default: {None})
|
||||||
timeout -- How long to wait for replies (default: 300 seconds)
|
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
RuntimeError: Raised if StreamInterface is instantiated when noProto is false.
|
Exception: [description]
|
||||||
|
Exception: [description]
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not noProto and type(self) == StreamInterface: # pylint: disable=C0123
|
if not hasattr(self, "stream") and not noProto:
|
||||||
raise RuntimeError(
|
raise Exception( # pylint: disable=W0719
|
||||||
"StreamInterface is now abstract (to update existing code create SerialInterface instead)"
|
"StreamInterface is now abstract (to update existing code create SerialInterface instead)"
|
||||||
)
|
)
|
||||||
self.stream: Optional[serial.Serial] = None # only serial uses this, TCPInterface overrides the relevant methods instead
|
self.stream: Optional[serial.Serial] # only serial uses this, TCPInterface overrides the relevant methods instead
|
||||||
self._rxBuf = bytes() # empty
|
self._rxBuf = bytes() # empty
|
||||||
self._wantExit = False
|
self._wantExit = False
|
||||||
|
|
||||||
@@ -57,21 +48,13 @@ class StreamInterface(MeshInterface):
|
|||||||
# FIXME, figure out why daemon=True causes reader thread to exit too early
|
# FIXME, figure out why daemon=True causes reader thread to exit too early
|
||||||
self._rxThread = threading.Thread(target=self.__reader, args=(), daemon=True, name="stream reader")
|
self._rxThread = threading.Thread(target=self.__reader, args=(), daemon=True, name="stream reader")
|
||||||
|
|
||||||
MeshInterface.__init__(self, debugOut=debugOut, noProto=noProto, noNodes=noNodes, timeout=timeout)
|
MeshInterface.__init__(self, debugOut=debugOut, noProto=noProto, noNodes=noNodes)
|
||||||
|
|
||||||
# Start the reader thread after superclass constructor completes init
|
# Start the reader thread after superclass constructor completes init
|
||||||
if connectNow:
|
if connectNow:
|
||||||
try:
|
self.connect()
|
||||||
self.connect()
|
if not noProto:
|
||||||
if not noProto:
|
self.waitForConfig()
|
||||||
self.waitForConfig()
|
|
||||||
except Exception:
|
|
||||||
# If the handshake raises, the caller never receives a reference
|
|
||||||
# to this instance and cannot call close() themselves. Clean up
|
|
||||||
# the reader thread + stream here so retries don't leak.
|
|
||||||
with contextlib.suppress(Exception):
|
|
||||||
self.close()
|
|
||||||
raise
|
|
||||||
|
|
||||||
def connect(self) -> None:
|
def connect(self) -> None:
|
||||||
"""Connect to our radio
|
"""Connect to our radio
|
||||||
@@ -99,7 +82,7 @@ class StreamInterface(MeshInterface):
|
|||||||
"""We override the superclass implementation to close our port"""
|
"""We override the superclass implementation to close our port"""
|
||||||
MeshInterface._disconnected(self)
|
MeshInterface._disconnected(self)
|
||||||
|
|
||||||
logger.debug("Closing our port")
|
logging.debug("Closing our port")
|
||||||
# pylint: disable=E0203
|
# pylint: disable=E0203
|
||||||
if not self.stream is None:
|
if not self.stream is None:
|
||||||
# pylint: disable=E0203
|
# pylint: disable=E0203
|
||||||
@@ -128,32 +111,23 @@ class StreamInterface(MeshInterface):
|
|||||||
|
|
||||||
def _sendToRadioImpl(self, toRadio) -> None:
|
def _sendToRadioImpl(self, toRadio) -> None:
|
||||||
"""Send a ToRadio protobuf to the device"""
|
"""Send a ToRadio protobuf to the device"""
|
||||||
logger.debug(f"Sending: {stripnl(toRadio)}")
|
logging.debug(f"Sending: {stripnl(toRadio)}")
|
||||||
b: bytes = toRadio.SerializeToString()
|
b: bytes = toRadio.SerializeToString()
|
||||||
bufLen: int = len(b)
|
bufLen: int = len(b)
|
||||||
# We convert into a string, because the TCP code doesn't work with byte arrays
|
# We convert into a string, because the TCP code doesn't work with byte arrays
|
||||||
header: bytes = bytes([START1, START2, (bufLen >> 8) & 0xFF, bufLen & 0xFF])
|
header: bytes = bytes([START1, START2, (bufLen >> 8) & 0xFF, bufLen & 0xFF])
|
||||||
logger.debug(f"sending header:{header!r} b:{b!r}")
|
logging.debug(f"sending header:{header!r} b:{b!r}")
|
||||||
self._writeBytes(header + b)
|
self._writeBytes(header + b)
|
||||||
|
|
||||||
def close(self) -> None:
|
def close(self) -> None:
|
||||||
"""Close a connection to the device"""
|
"""Close a connection to the device"""
|
||||||
logger.debug("Closing stream")
|
logging.debug("Closing stream")
|
||||||
MeshInterface.close(self)
|
MeshInterface.close(self)
|
||||||
# pyserial cancel_read doesn't seem to work, therefore we ask the
|
# pyserial cancel_read doesn't seem to work, therefore we ask the
|
||||||
# reader thread to close things for us
|
# reader thread to close things for us
|
||||||
self._wantExit = True
|
self._wantExit = True
|
||||||
if self._rxThread != threading.current_thread():
|
if self._rxThread != threading.current_thread():
|
||||||
try:
|
self._rxThread.join() # wait for it to exit
|
||||||
self._rxThread.join() # wait for it to exit
|
|
||||||
except RuntimeError:
|
|
||||||
# Thread was never started — happens when close() is invoked
|
|
||||||
# from a failed __init__ before connect() could spawn it.
|
|
||||||
# In this case there is no reader thread to close the stream.
|
|
||||||
if self.stream is not None:
|
|
||||||
with contextlib.suppress(Exception):
|
|
||||||
self.stream.close()
|
|
||||||
self.stream = None
|
|
||||||
|
|
||||||
def _handleLogByte(self, b):
|
def _handleLogByte(self, b):
|
||||||
"""Handle a byte that is part of a log message from the device."""
|
"""Handle a byte that is part of a log message from the device."""
|
||||||
@@ -174,18 +148,18 @@ class StreamInterface(MeshInterface):
|
|||||||
|
|
||||||
def __reader(self) -> None:
|
def __reader(self) -> None:
|
||||||
"""The reader thread that reads bytes from our stream"""
|
"""The reader thread that reads bytes from our stream"""
|
||||||
logger.debug("in __reader()")
|
logging.debug("in __reader()")
|
||||||
empty = bytes()
|
empty = bytes()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
while not self._wantExit:
|
while not self._wantExit:
|
||||||
# logger.debug("reading character")
|
# logging.debug("reading character")
|
||||||
b: Optional[bytes] = self._readBytes(1)
|
b: Optional[bytes] = self._readBytes(1)
|
||||||
# logger.debug("In reader loop")
|
# logging.debug("In reader loop")
|
||||||
# logger.debug(f"read returned {b}")
|
# logging.debug(f"read returned {b}")
|
||||||
if b is not None and len(cast(bytes, b)) > 0:
|
if b is not None and len(cast(bytes, b)) > 0:
|
||||||
c: int = b[0]
|
c: int = b[0]
|
||||||
# logger.debug(f'c:{c}')
|
# logging.debug(f'c:{c}')
|
||||||
ptr: int = len(self._rxBuf)
|
ptr: int = len(self._rxBuf)
|
||||||
|
|
||||||
# Assume we want to append this byte, fixme use bytearray instead
|
# Assume we want to append this byte, fixme use bytearray instead
|
||||||
@@ -202,7 +176,7 @@ class StreamInterface(MeshInterface):
|
|||||||
if c != START2:
|
if c != START2:
|
||||||
self._rxBuf = empty # failed to find start2
|
self._rxBuf = empty # failed to find start2
|
||||||
elif ptr >= HEADER_LEN - 1: # we've at least got a header
|
elif ptr >= HEADER_LEN - 1: # we've at least got a header
|
||||||
# logger.debug('at least we received a header')
|
# logging.debug('at least we received a header')
|
||||||
# big endian length follows header
|
# big endian length follows header
|
||||||
packetlen = (self._rxBuf[2] << 8) + self._rxBuf[3]
|
packetlen = (self._rxBuf[2] << 8) + self._rxBuf[3]
|
||||||
|
|
||||||
@@ -218,32 +192,32 @@ class StreamInterface(MeshInterface):
|
|||||||
try:
|
try:
|
||||||
self._handleFromRadio(self._rxBuf[HEADER_LEN:])
|
self._handleFromRadio(self._rxBuf[HEADER_LEN:])
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
logger.error(
|
logging.error(
|
||||||
f"Error while handling message from radio {ex}"
|
f"Error while handling message from radio {ex}"
|
||||||
)
|
)
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
self._rxBuf = empty
|
self._rxBuf = empty
|
||||||
else:
|
else:
|
||||||
# logger.debug(f"timeout")
|
# logging.debug(f"timeout")
|
||||||
pass
|
pass
|
||||||
except serial.SerialException as ex:
|
except serial.SerialException as ex:
|
||||||
if (
|
if (
|
||||||
not self._wantExit
|
not self._wantExit
|
||||||
): # We might intentionally get an exception during shutdown
|
): # We might intentionally get an exception during shutdown
|
||||||
logger.warning(
|
logging.warning(
|
||||||
f"Meshtastic serial port disconnected, disconnecting... {ex}"
|
f"Meshtastic serial port disconnected, disconnecting... {ex}"
|
||||||
)
|
)
|
||||||
except OSError as ex:
|
except OSError as ex:
|
||||||
if (
|
if (
|
||||||
not self._wantExit
|
not self._wantExit
|
||||||
): # We might intentionally get an exception during shutdown
|
): # We might intentionally get an exception during shutdown
|
||||||
logger.error(
|
logging.error(
|
||||||
f"Unexpected OSError, terminating meshtastic reader... {ex}"
|
f"Unexpected OSError, terminating meshtastic reader... {ex}"
|
||||||
)
|
)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
logger.error(
|
logging.error(
|
||||||
f"Unexpected exception, terminating meshtastic reader... {ex}"
|
f"Unexpected exception, terminating meshtastic reader... {ex}"
|
||||||
)
|
)
|
||||||
finally:
|
finally:
|
||||||
logger.debug("reader is exiting")
|
logging.debug("reader is exiting")
|
||||||
self._disconnected()
|
self._disconnected()
|
||||||
|
|||||||
@@ -207,30 +207,6 @@ nano_g1 = SupportedDevice(
|
|||||||
usb_product_id_in_hex="55d4",
|
usb_product_id_in_hex="55d4",
|
||||||
)
|
)
|
||||||
|
|
||||||
seeed_xiao_s3 = SupportedDevice(
|
|
||||||
name = "Seeed Xiao ESP32-S3",
|
|
||||||
version = "",
|
|
||||||
for_firmware="seeed-xiao-esp32s3",
|
|
||||||
baseport_on_linux="ttyACM",
|
|
||||||
baseport_on_mac="cu.usbmodem",
|
|
||||||
usb_vendor_id_in_hex="2886",
|
|
||||||
usb_product_id_in_hex="0059",
|
|
||||||
)
|
|
||||||
|
|
||||||
tdeck = SupportedDevice(
|
|
||||||
name="T-Deck",
|
|
||||||
version="",
|
|
||||||
for_firmware="t-deck", # Confirmed firmware identifier
|
|
||||||
device_class="esp32",
|
|
||||||
baseport_on_linux="ttyACM",
|
|
||||||
baseport_on_mac="cu.usbmodem",
|
|
||||||
baseport_on_windows="COM",
|
|
||||||
usb_vendor_id_in_hex="303a", # Espressif Systems (VERIFIED)
|
|
||||||
usb_product_id_in_hex="1001", # VERIFIED from actual device
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
supported_devices = [
|
supported_devices = [
|
||||||
tbeam_v0_7,
|
tbeam_v0_7,
|
||||||
tbeam_v1_1,
|
tbeam_v1_1,
|
||||||
@@ -250,6 +226,4 @@ supported_devices = [
|
|||||||
rak4631_19003,
|
rak4631_19003,
|
||||||
rak11200,
|
rak11200,
|
||||||
nano_g1,
|
nano_g1,
|
||||||
seeed_xiao_s3,
|
|
||||||
tdeck, # T-Deck support added
|
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,24 +1,14 @@
|
|||||||
"""TCPInterface class for interfacing with http endpoint
|
"""TCPInterface class for interfacing with http endpoint
|
||||||
"""
|
"""
|
||||||
# pylint: disable=R0917
|
# pylint: disable=R0917
|
||||||
import contextlib
|
|
||||||
import logging
|
import logging
|
||||||
import socket
|
import socket
|
||||||
import threading
|
from typing import Optional, cast
|
||||||
import time
|
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
from meshtastic.stream_interface import StreamInterface
|
from meshtastic.stream_interface import StreamInterface
|
||||||
|
|
||||||
DEFAULT_TCP_PORT = 4403
|
DEFAULT_TCP_PORT = 4403
|
||||||
|
|
||||||
# How long close() gives the device to consume what we last wrote before forcing
|
|
||||||
# the connection down. See close() for why this exists.
|
|
||||||
GRACEFUL_CLOSE_TIMEOUT = 0.25
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class TCPInterface(StreamInterface):
|
class TCPInterface(StreamInterface):
|
||||||
"""Interface class for meshtastic devices over a TCP link"""
|
"""Interface class for meshtastic devices over a TCP link"""
|
||||||
|
|
||||||
@@ -26,155 +16,71 @@ class TCPInterface(StreamInterface):
|
|||||||
self,
|
self,
|
||||||
hostname: str,
|
hostname: str,
|
||||||
debugOut=None,
|
debugOut=None,
|
||||||
noProto: bool = False,
|
noProto: bool=False,
|
||||||
connectNow: bool = True,
|
connectNow: bool=True,
|
||||||
portNumber: int = DEFAULT_TCP_PORT,
|
portNumber: int=DEFAULT_TCP_PORT,
|
||||||
noNodes: bool = False,
|
noNodes:bool=False,
|
||||||
timeout: int = 300,
|
|
||||||
):
|
):
|
||||||
"""Constructor, opens a connection to a specified IP address/hostname
|
"""Constructor, opens a connection to a specified IP address/hostname
|
||||||
|
|
||||||
Keyword Arguments:
|
Keyword Arguments:
|
||||||
hostname {string} -- Hostname/IP address of the device to connect to
|
hostname {string} -- Hostname/IP address of the device to connect to
|
||||||
timeout -- How long to wait for replies (default: 300 seconds)
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
self.stream = None
|
||||||
|
|
||||||
self.hostname: str = hostname
|
self.hostname: str = hostname
|
||||||
self.portNumber: int = portNumber
|
self.portNumber: int = portNumber
|
||||||
|
|
||||||
self.socket: Optional[socket.socket] = None
|
self.socket: Optional[socket.socket] = None
|
||||||
self.reconnectLock = threading.Lock()
|
|
||||||
|
|
||||||
super().__init__(
|
if connectNow:
|
||||||
debugOut=debugOut,
|
logging.debug(f"Connecting to {hostname}") # type: ignore[str-bytes-safe]
|
||||||
noProto=noProto,
|
server_address: tuple[str, int] = (hostname, portNumber)
|
||||||
connectNow=connectNow,
|
sock: Optional[socket.socket] = socket.create_connection(server_address)
|
||||||
noNodes=noNodes,
|
self.socket = sock
|
||||||
timeout=timeout,
|
else:
|
||||||
|
self.socket = None
|
||||||
|
|
||||||
|
StreamInterface.__init__(
|
||||||
|
self, debugOut=debugOut, noProto=noProto, connectNow=connectNow, noNodes=noNodes
|
||||||
)
|
)
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
rep = f"TCPInterface({self.hostname!r}"
|
|
||||||
if self.debugOut is not None:
|
|
||||||
rep += f", debugOut={self.debugOut!r}"
|
|
||||||
if self.noProto:
|
|
||||||
rep += ", noProto=True"
|
|
||||||
if self.socket is None:
|
|
||||||
rep += ", connectNow=False"
|
|
||||||
if self.portNumber != DEFAULT_TCP_PORT:
|
|
||||||
rep += f", portNumber={self.portNumber!r}"
|
|
||||||
if self.noNodes:
|
|
||||||
rep += ", noNodes=True"
|
|
||||||
rep += ")"
|
|
||||||
return rep
|
|
||||||
|
|
||||||
def _socket_shutdown(self) -> None:
|
def _socket_shutdown(self) -> None:
|
||||||
"""Shutdown the socket.
|
"""Shutdown the socket.
|
||||||
Note: Broke out this line so the exception could be unit tested.
|
Note: Broke out this line so the exception could be unit tested.
|
||||||
"""
|
"""
|
||||||
if self.socket is not None:
|
if self.socket: #mian: please check that this should be "if self.socket:"
|
||||||
self.socket.shutdown(socket.SHUT_RDWR)
|
cast(socket.socket, self.socket).shutdown(socket.SHUT_RDWR)
|
||||||
|
|
||||||
def connect(self) -> None:
|
|
||||||
"""Connect the interface"""
|
|
||||||
self.myConnect()
|
|
||||||
super().connect()
|
|
||||||
|
|
||||||
def myConnect(self) -> None:
|
def myConnect(self) -> None:
|
||||||
"""Connect to socket (without attempting to start the interface's receive thread)"""
|
"""Connect to socket"""
|
||||||
logger.debug(f"Connecting to {self.hostname}") # type: ignore[str-bytes-safe]
|
server_address: tuple[str, int] = (self.hostname, self.portNumber)
|
||||||
server_address = (self.hostname, self.portNumber)
|
sock: Optional[socket.socket] = socket.create_connection(server_address)
|
||||||
self.socket = socket.create_connection(server_address)
|
self.socket = sock
|
||||||
|
|
||||||
def _wait_for_reader_exit(self, timeout: float) -> None:
|
|
||||||
"""Wait briefly for the reader thread to drain and exit after a half-close.
|
|
||||||
|
|
||||||
Returns as soon as it exits, or after timeout: the device is not obliged
|
|
||||||
to close just because we did.
|
|
||||||
"""
|
|
||||||
rx = getattr(self, "_rxThread", None)
|
|
||||||
if rx is None or rx is threading.current_thread():
|
|
||||||
return
|
|
||||||
with contextlib.suppress(Exception):
|
|
||||||
rx.join(timeout)
|
|
||||||
|
|
||||||
def close(self) -> None:
|
def close(self) -> None:
|
||||||
"""Close a connection to the device."""
|
"""Close a connection to the device"""
|
||||||
logger.debug("Closing TCP stream")
|
logging.debug("Closing TCP stream")
|
||||||
|
StreamInterface.close(self)
|
||||||
# Sometimes the socket read might be blocked in the reader thread.
|
# Sometimes the socket read might be blocked in the reader thread.
|
||||||
# Therefore force a shutdown first to unblock reader thread reads.
|
# Therefore we force the shutdown by closing the socket here
|
||||||
self._wantExit = True
|
self._wantExit: bool = True
|
||||||
if self.socket is not None:
|
if not self.socket is None:
|
||||||
# Half-close first. shutdown(SHUT_WR) sends FIN, which tells the
|
try:
|
||||||
# device we are done writing and lets it consume what we last wrote
|
|
||||||
# before the connection goes away -- typically the admin message a
|
|
||||||
# one-shot command such as `--set` just sent.
|
|
||||||
#
|
|
||||||
# Going straight to shutdown(SHUT_RDWR) + close() while either side
|
|
||||||
# still has unread data makes the stack send RST instead. Winsock
|
|
||||||
# then discards data the peer had already received but not yet read,
|
|
||||||
# so the write is lost; Linux delivers it before reporting
|
|
||||||
# ECONNRESET, which is why this only bites on Windows.
|
|
||||||
with contextlib.suppress(Exception):
|
|
||||||
self.socket.shutdown(socket.SHUT_WR)
|
|
||||||
self._wait_for_reader_exit(GRACEFUL_CLOSE_TIMEOUT)
|
|
||||||
|
|
||||||
with contextlib.suppress(
|
|
||||||
Exception
|
|
||||||
): # Ignore errors in shutdown, because we might have a race with the server
|
|
||||||
self._socket_shutdown()
|
self._socket_shutdown()
|
||||||
with contextlib.suppress(Exception):
|
except:
|
||||||
self.socket.close()
|
pass # Ignore errors in shutdown, because we might have a race with the server
|
||||||
|
self.socket.close()
|
||||||
self.socket = None
|
|
||||||
super().close()
|
|
||||||
|
|
||||||
def _writeBytes(self, b: bytes) -> None:
|
def _writeBytes(self, b: bytes) -> None:
|
||||||
"""Write an array of bytes to our stream"""
|
"""Write an array of bytes to our stream and flush"""
|
||||||
if self.socket is not None:
|
if self.socket:
|
||||||
try:
|
self.socket.send(b)
|
||||||
self.socket.sendall(b)
|
|
||||||
except OSError as e:
|
|
||||||
logger.error(f"Socket send error, reconnecting: {e}")
|
|
||||||
if not self._wantExit:
|
|
||||||
self._reconnect()
|
|
||||||
raise
|
|
||||||
|
|
||||||
def _readBytes(self, length) -> Optional[bytes]:
|
def _readBytes(self, length) -> Optional[bytes]:
|
||||||
"""Read an array of bytes from our stream"""
|
"""Read an array of bytes from our stream"""
|
||||||
if self.socket is not None:
|
if self.socket:
|
||||||
data = self.socket.recv(length)
|
return self.socket.recv(length)
|
||||||
# empty byte indicates a disconnected socket,
|
else:
|
||||||
# we need to handle it to avoid an infinite loop reading from null socket
|
return None
|
||||||
if data == b"":
|
|
||||||
logger.debug("Closed socket, re-connecting")
|
|
||||||
if not self._wantExit:
|
|
||||||
self._reconnect()
|
|
||||||
return data
|
|
||||||
|
|
||||||
# no socket, break reader thread
|
|
||||||
self._wantExit = True
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _reconnect(self) -> None:
|
|
||||||
"""Reconnect to the socket"""
|
|
||||||
# Save the socket reference before attempting to acquire the lock.
|
|
||||||
sock = self.socket
|
|
||||||
start_config = False
|
|
||||||
with self.reconnectLock:
|
|
||||||
if self._wantExit:
|
|
||||||
return
|
|
||||||
# Don't reconnect: someone else already did it.
|
|
||||||
if sock is not self.socket:
|
|
||||||
return
|
|
||||||
|
|
||||||
with contextlib.suppress(Exception):
|
|
||||||
self._socket_shutdown()
|
|
||||||
if self.socket is not None:
|
|
||||||
self.socket.close()
|
|
||||||
self.socket = None
|
|
||||||
time.sleep(1)
|
|
||||||
self.myConnect()
|
|
||||||
start_config = True
|
|
||||||
|
|
||||||
if start_config and not self._wantExit and self.socket is not None:
|
|
||||||
self._startConfig()
|
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ testNumber: int = 0
|
|||||||
|
|
||||||
sendingInterface = None
|
sendingInterface = None
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
def onReceive(packet, interface) -> None:
|
def onReceive(packet, interface) -> None:
|
||||||
"""Callback invoked when a packet arrives"""
|
"""Callback invoked when a packet arrives"""
|
||||||
@@ -80,7 +79,7 @@ def testSend(
|
|||||||
else:
|
else:
|
||||||
toNode = toInterface.myInfo.my_node_num
|
toNode = toInterface.myInfo.my_node_num
|
||||||
|
|
||||||
logger.debug(f"Sending test wantAck={wantAck} packet from {fromNode} to {toNode}")
|
logging.debug(f"Sending test wantAck={wantAck} packet from {fromNode} to {toNode}")
|
||||||
# pylint: disable=W0603
|
# pylint: disable=W0603
|
||||||
global sendingInterface
|
global sendingInterface
|
||||||
sendingInterface = fromInterface
|
sendingInterface = fromInterface
|
||||||
@@ -99,7 +98,7 @@ def testSend(
|
|||||||
|
|
||||||
def runTests(numTests: int=50, wantAck: bool=False, maxFailures: int=0) -> bool:
|
def runTests(numTests: int=50, wantAck: bool=False, maxFailures: int=0) -> bool:
|
||||||
"""Run the tests."""
|
"""Run the tests."""
|
||||||
logger.info(f"Running {numTests} tests with wantAck={wantAck}")
|
logging.info(f"Running {numTests} tests with wantAck={wantAck}")
|
||||||
numFail: int = 0
|
numFail: int = 0
|
||||||
numSuccess: int = 0
|
numSuccess: int = 0
|
||||||
for _ in range(numTests):
|
for _ in range(numTests):
|
||||||
@@ -113,26 +112,26 @@ def runTests(numTests: int=50, wantAck: bool=False, maxFailures: int=0) -> bool:
|
|||||||
)
|
)
|
||||||
if not success:
|
if not success:
|
||||||
numFail = numFail + 1
|
numFail = numFail + 1
|
||||||
logger.error(
|
logging.error(
|
||||||
f"Test {testNumber} failed, expected packet not received ({numFail} failures so far)"
|
f"Test {testNumber} failed, expected packet not received ({numFail} failures so far)"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
numSuccess = numSuccess + 1
|
numSuccess = numSuccess + 1
|
||||||
logger.info(
|
logging.info(
|
||||||
f"Test {testNumber} succeeded {numSuccess} successes {numFail} failures so far"
|
f"Test {testNumber} succeeded {numSuccess} successes {numFail} failures so far"
|
||||||
)
|
)
|
||||||
|
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
if numFail > maxFailures:
|
if numFail > maxFailures:
|
||||||
logger.error("Too many failures! Test failed!")
|
logging.error("Too many failures! Test failed!")
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def testThread(numTests=50) -> bool:
|
def testThread(numTests=50) -> bool:
|
||||||
"""Test thread"""
|
"""Test thread"""
|
||||||
logger.info("Found devices, starting tests...")
|
logging.info("Found devices, starting tests...")
|
||||||
result: bool = runTests(numTests, wantAck=True)
|
result: bool = runTests(numTests, wantAck=True)
|
||||||
if result:
|
if result:
|
||||||
# Run another test
|
# Run another test
|
||||||
@@ -149,7 +148,7 @@ def onConnection(topic=pub.AUTO_TOPIC) -> None:
|
|||||||
def openDebugLog(portName) -> io.TextIOWrapper:
|
def openDebugLog(portName) -> io.TextIOWrapper:
|
||||||
"""Open the debug log file"""
|
"""Open the debug log file"""
|
||||||
debugname = "log" + portName.replace("/", "_")
|
debugname = "log" + portName.replace("/", "_")
|
||||||
logger.info(f"Writing serial debugging to {debugname}")
|
logging.info(f"Writing serial debugging to {debugname}")
|
||||||
return open(debugname, "w+", buffering=1, encoding="utf8")
|
return open(debugname, "w+", buffering=1, encoding="utf8")
|
||||||
|
|
||||||
|
|
||||||
@@ -178,7 +177,7 @@ def testAll(numTests: int=5) -> bool:
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
logger.info("Ports opened, starting test")
|
logging.info("Ports opened, starting test")
|
||||||
result: bool = testThread(numTests)
|
result: bool = testThread(numTests)
|
||||||
|
|
||||||
for i in interfaces:
|
for i in interfaces:
|
||||||
@@ -197,14 +196,14 @@ def testSimulator() -> None:
|
|||||||
python3 -c 'from meshtastic.test import testSimulator; testSimulator()'
|
python3 -c 'from meshtastic.test import testSimulator; testSimulator()'
|
||||||
"""
|
"""
|
||||||
logging.basicConfig(level=logging.DEBUG)
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
logger.info("Connecting to simulator on localhost!")
|
logging.info("Connecting to simulator on localhost!")
|
||||||
try:
|
try:
|
||||||
iface: meshtastic.tcp_interface.TCPInterface = TCPInterface("localhost")
|
iface: meshtastic.tcp_interface.TCPInterface = TCPInterface("localhost")
|
||||||
iface.showInfo()
|
iface.showInfo()
|
||||||
iface.localNode.showInfo()
|
iface.localNode.showInfo()
|
||||||
iface.localNode.exitSimulator()
|
iface.localNode.exitSimulator()
|
||||||
iface.close()
|
iface.close()
|
||||||
logger.info("Integration test successful!")
|
logging.info("Integration test successful!")
|
||||||
except:
|
except:
|
||||||
print("Error while testing simulator:", sys.exc_info()[0])
|
print("Error while testing simulator:", sys.exc_info()[0])
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
|||||||
@@ -8,87 +8,6 @@ import pytest
|
|||||||
from meshtastic import mt_config
|
from meshtastic import mt_config
|
||||||
|
|
||||||
from ..mesh_interface import MeshInterface
|
from ..mesh_interface import MeshInterface
|
||||||
from .firmware_harness import (
|
|
||||||
CHAIN_TOPOLOGY,
|
|
||||||
DEFAULT_BASE_PORT,
|
|
||||||
SimMesh,
|
|
||||||
find_meshtasticd,
|
|
||||||
is_compatible_host,
|
|
||||||
)
|
|
||||||
from .fw_helpers import set_region
|
|
||||||
|
|
||||||
# Use a different base port for the single-node fixture so it doesn't
|
|
||||||
# conflict with the multi-node mesh fixture.
|
|
||||||
SINGLE_NODE_BASE_PORT = DEFAULT_BASE_PORT + 100
|
|
||||||
|
|
||||||
|
|
||||||
def _skip_firmware_if_unavailable() -> None:
|
|
||||||
"""Skip the test when meshtasticd can't run on this host."""
|
|
||||||
if not is_compatible_host():
|
|
||||||
pytest.skip("meshtasticd firmware tests require Linux")
|
|
||||||
if find_meshtasticd() is None:
|
|
||||||
pytest.skip(
|
|
||||||
"meshtasticd not found — set MESHTASTICD_BIN or install it on PATH"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="function")
|
|
||||||
def firmware_node():
|
|
||||||
"""A single meshtasticd sim node for smokevirt tests.
|
|
||||||
|
|
||||||
Function-scoped so every test gets a freshly-erased node with no
|
|
||||||
state leaking from previous tests. This makes destructive commands
|
|
||||||
(``--reboot``, ``--set factory_reset true``) safe to run and lets
|
|
||||||
tests be order-independent.
|
|
||||||
|
|
||||||
Yields the SimNode instance. The node is booted with a fresh erased
|
|
||||||
config and listens on localhost at its TCP port. Region is set to US
|
|
||||||
so modem-preset tests work against firmware >= 2.8, which clamps
|
|
||||||
presets to the legal set for the current region.
|
|
||||||
"""
|
|
||||||
_skip_firmware_if_unavailable()
|
|
||||||
mesh = SimMesh(n_nodes=1, base_port=SINGLE_NODE_BASE_PORT)
|
|
||||||
mesh.start()
|
|
||||||
node = mesh.get_node(0)
|
|
||||||
set_region(node.port, "US")
|
|
||||||
# The region commit restarts the TCP listener, so reconnect the harness
|
|
||||||
# interface in case a test wants to use it directly.
|
|
||||||
if node.iface is not None:
|
|
||||||
try:
|
|
||||||
node.iface.close()
|
|
||||||
except Exception: # pylint: disable=broad-except
|
|
||||||
pass
|
|
||||||
node.connect()
|
|
||||||
yield node
|
|
||||||
mesh.stop()
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="function")
|
|
||||||
def firmware_mesh():
|
|
||||||
"""A 3-node chain (A-B-C) meshtasticd sim mesh for smokemesh tests.
|
|
||||||
|
|
||||||
Yields the SimMesh instance. Nodes are connected and the SIMULATOR_APP
|
|
||||||
packet bridge is running. Region is set to US for firmware >= 2.8
|
|
||||||
compatibility, interfaces are reconnected after the region change, and
|
|
||||||
node DB convergence is awaited.
|
|
||||||
"""
|
|
||||||
_skip_firmware_if_unavailable()
|
|
||||||
mesh = SimMesh(n_nodes=3, topology=CHAIN_TOPOLOGY)
|
|
||||||
mesh.start()
|
|
||||||
for node in mesh.nodes:
|
|
||||||
set_region(node.port, "US")
|
|
||||||
# The region commit restarts each node's TCP listener, so reconnect the
|
|
||||||
# harness interfaces before waiting for convergence.
|
|
||||||
for node in mesh.nodes:
|
|
||||||
if node.iface is not None:
|
|
||||||
try:
|
|
||||||
node.iface.close()
|
|
||||||
except Exception: # pylint: disable=broad-except
|
|
||||||
pass
|
|
||||||
node.connect()
|
|
||||||
mesh.wait_for_convergence(timeout=30)
|
|
||||||
yield mesh
|
|
||||||
mesh.stop()
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
|||||||
@@ -1,357 +0,0 @@
|
|||||||
"""Test harness for running real meshtasticd firmware instances.
|
|
||||||
|
|
||||||
Launches one or more meshtasticd processes in simulator mode (-s) and bridges
|
|
||||||
their "over-the-air" packets via the SIMULATOR_APP protocol so that multiple
|
|
||||||
instances can communicate as if via LoRa, with a configurable topology.
|
|
||||||
|
|
||||||
The harness expects meshtasticd to be available on PATH or via the
|
|
||||||
MESHTASTICD_BIN environment variable. It does not download or build the
|
|
||||||
binary itself.
|
|
||||||
"""
|
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
import platform
|
|
||||||
import shutil
|
|
||||||
import signal
|
|
||||||
import socket
|
|
||||||
import subprocess
|
|
||||||
import tempfile
|
|
||||||
import time
|
|
||||||
from typing import Dict, List, Optional, Set
|
|
||||||
|
|
||||||
from pubsub import pub # type: ignore[import-untyped]
|
|
||||||
|
|
||||||
from meshtastic import BROADCAST_NUM, mesh_pb2, portnums_pb2
|
|
||||||
from meshtastic.tcp_interface import TCPInterface
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
HW_ID_OFFSET = 16
|
|
||||||
DEFAULT_BASE_PORT = 4404
|
|
||||||
DEFAULT_RSSI = -50
|
|
||||||
DEFAULT_SNR = 10.0
|
|
||||||
BOOT_TIMEOUT = 30
|
|
||||||
CONNECT_TIMEOUT = 30
|
|
||||||
|
|
||||||
CHAIN_TOPOLOGY: Dict[int, Set[int]] = {
|
|
||||||
0: {1},
|
|
||||||
1: {0, 2},
|
|
||||||
2: {1},
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def find_meshtasticd() -> Optional[str]:
|
|
||||||
"""Return the path to the meshtasticd binary, or None if not found."""
|
|
||||||
env_path = os.environ.get("MESHTASTICD_BIN")
|
|
||||||
if env_path and os.path.isfile(env_path) and os.access(env_path, os.X_OK):
|
|
||||||
return env_path
|
|
||||||
return shutil.which("meshtasticd")
|
|
||||||
|
|
||||||
|
|
||||||
def is_compatible_host() -> bool:
|
|
||||||
"""True when the host can run meshtasticd natively (Linux only)."""
|
|
||||||
return platform.system() == "Linux"
|
|
||||||
|
|
||||||
|
|
||||||
def _wait_for_port(port: int, timeout: int = BOOT_TIMEOUT) -> bool:
|
|
||||||
deadline = time.monotonic() + timeout
|
|
||||||
while time.monotonic() < deadline:
|
|
||||||
try:
|
|
||||||
s = socket.create_connection(("localhost", port), timeout=0.5)
|
|
||||||
s.close()
|
|
||||||
return True
|
|
||||||
except OSError:
|
|
||||||
time.sleep(0.5)
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
class SimNode:
|
|
||||||
"""A single meshtasticd simulator instance."""
|
|
||||||
|
|
||||||
def __init__(self, node_id: int, base_port: int = DEFAULT_BASE_PORT):
|
|
||||||
self.node_id = node_id
|
|
||||||
self.hw_id = node_id + HW_ID_OFFSET
|
|
||||||
self.port = base_port + node_id
|
|
||||||
self.process: Optional[subprocess.Popen] = None
|
|
||||||
self.workdir: Optional[str] = None
|
|
||||||
self.iface: Optional[TCPInterface] = None
|
|
||||||
self._log_files: list = []
|
|
||||||
|
|
||||||
@property
|
|
||||||
def node_num(self) -> int:
|
|
||||||
"""The firmware-assigned node number (== hw_id)."""
|
|
||||||
if self.iface and self.iface.myInfo:
|
|
||||||
return self.iface.myInfo.my_node_num
|
|
||||||
return self.hw_id
|
|
||||||
|
|
||||||
def start(self, binary: str) -> None:
|
|
||||||
"""Launch the meshtasticd process in simulator mode."""
|
|
||||||
self.workdir = tempfile.mkdtemp(prefix=f"mtd_node{self.node_id}_")
|
|
||||||
vfs_dir = os.path.join(self.workdir, "vfs")
|
|
||||||
os.mkdir(vfs_dir)
|
|
||||||
# Files are closed in _kill(); keep them open for the process lifetime.
|
|
||||||
log_stdout = open( # pylint: disable=consider-using-with
|
|
||||||
os.path.join(self.workdir, "meshtasticd.log"), "wb", buffering=0
|
|
||||||
)
|
|
||||||
log_stderr = open( # pylint: disable=consider-using-with
|
|
||||||
os.path.join(self.workdir, "meshtasticd.err"), "wb", buffering=0
|
|
||||||
)
|
|
||||||
self._log_files = [log_stdout, log_stderr]
|
|
||||||
self.process = subprocess.Popen( # pylint: disable=consider-using-with
|
|
||||||
[
|
|
||||||
binary,
|
|
||||||
"-s",
|
|
||||||
"-h", str(self.hw_id),
|
|
||||||
"-p", str(self.port),
|
|
||||||
"-d", vfs_dir,
|
|
||||||
"-e",
|
|
||||||
],
|
|
||||||
stdout=log_stdout,
|
|
||||||
stderr=log_stderr,
|
|
||||||
start_new_session=True,
|
|
||||||
)
|
|
||||||
if not _wait_for_port(self.port):
|
|
||||||
self._kill()
|
|
||||||
raise RuntimeError(
|
|
||||||
f"meshtasticd node {self.node_id} did not start listening on port {self.port}"
|
|
||||||
)
|
|
||||||
def connect(self) -> None:
|
|
||||||
"""Open a TCPInterface connection to this node."""
|
|
||||||
self.iface = TCPInterface(
|
|
||||||
hostname="localhost",
|
|
||||||
portNumber=self.port,
|
|
||||||
connectNow=False,
|
|
||||||
)
|
|
||||||
self.iface.myConnect()
|
|
||||||
self.iface.connect()
|
|
||||||
|
|
||||||
def close(self) -> None:
|
|
||||||
"""Close the interface and kill the process."""
|
|
||||||
if self.iface is not None:
|
|
||||||
try:
|
|
||||||
self.iface.localNode.exitSimulator()
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
try:
|
|
||||||
self.iface.close()
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
self.iface = None
|
|
||||||
self._kill()
|
|
||||||
if self.workdir:
|
|
||||||
shutil.rmtree(self.workdir, ignore_errors=True)
|
|
||||||
self.workdir = None
|
|
||||||
|
|
||||||
def _kill(self) -> None:
|
|
||||||
for f in self._log_files:
|
|
||||||
try:
|
|
||||||
f.close()
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
self._log_files = []
|
|
||||||
if self.process is not None:
|
|
||||||
try:
|
|
||||||
os.killpg(os.getpgid(self.process.pid), signal.SIGTERM)
|
|
||||||
except (ProcessLookupError, OSError):
|
|
||||||
pass
|
|
||||||
try:
|
|
||||||
self.process.wait(timeout=5)
|
|
||||||
except Exception:
|
|
||||||
try:
|
|
||||||
os.killpg(os.getpgid(self.process.pid), signal.SIGKILL)
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
# Give OS time to release TCP port (avoid TIME_WAIT preventing
|
|
||||||
# next instance from binding the same port)
|
|
||||||
time.sleep(1.0)
|
|
||||||
self.process = None
|
|
||||||
|
|
||||||
|
|
||||||
class SimMesh:
|
|
||||||
"""Manages N meshtasticd sim instances and bridges their SIMULATOR_APP packets.
|
|
||||||
|
|
||||||
When *topology* is None every node hears every other node (full mesh).
|
|
||||||
Otherwise *topology* maps a transmitter's node index to the set of receiver
|
|
||||||
node indices that can hear it.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
n_nodes: int = 1,
|
|
||||||
topology: Optional[Dict[int, Set[int]]] = None,
|
|
||||||
base_port: int = DEFAULT_BASE_PORT,
|
|
||||||
):
|
|
||||||
self.n_nodes = n_nodes
|
|
||||||
self.topology = topology
|
|
||||||
self.base_port = base_port
|
|
||||||
self.nodes: List[SimNode] = [
|
|
||||||
SimNode(i, base_port) for i in range(n_nodes)
|
|
||||||
]
|
|
||||||
self._port_to_idx: Dict[int, int] = {}
|
|
||||||
self._started = False
|
|
||||||
|
|
||||||
def start(self) -> None:
|
|
||||||
"""Launch all nodes, connect, and start the packet bridge."""
|
|
||||||
binary = find_meshtasticd()
|
|
||||||
if binary is None:
|
|
||||||
raise RuntimeError(
|
|
||||||
"meshtasticd not found. Set MESHTASTICD_BIN or install it on PATH."
|
|
||||||
)
|
|
||||||
|
|
||||||
for node in self.nodes:
|
|
||||||
node.start(binary)
|
|
||||||
|
|
||||||
for node in self.nodes:
|
|
||||||
node.connect()
|
|
||||||
self._port_to_idx[node.port] = node.node_id
|
|
||||||
|
|
||||||
pub.subscribe(self._on_sim_packet, "meshtastic.receive.simulator")
|
|
||||||
self._started = True
|
|
||||||
|
|
||||||
if self.n_nodes > 1:
|
|
||||||
self._trigger_convergence()
|
|
||||||
|
|
||||||
def _trigger_convergence(self) -> None:
|
|
||||||
"""Actively trigger NodeInfo exchange instead of waiting passively.
|
|
||||||
|
|
||||||
Sends a NODEINFO_APP packet with wantResponse from each node so the
|
|
||||||
firmware's NodeInfoModule responds with its own user info, populating
|
|
||||||
all node DBs deterministically.
|
|
||||||
"""
|
|
||||||
for node in self.nodes:
|
|
||||||
iface = node.iface
|
|
||||||
if iface is None:
|
|
||||||
continue
|
|
||||||
user = mesh_pb2.User()
|
|
||||||
user.id = f"!{node.node_num:08x}"
|
|
||||||
user.long_name = f"Node {node.node_id}"
|
|
||||||
user.short_name = f"{node.node_id:04d}"[:4]
|
|
||||||
user.hw_model = mesh_pb2.HardwareModel.PORTDUINO
|
|
||||||
try:
|
|
||||||
iface.sendData(
|
|
||||||
user,
|
|
||||||
destinationId=BROADCAST_NUM,
|
|
||||||
portNum=portnums_pb2.PortNum.NODEINFO_APP,
|
|
||||||
wantAck=False,
|
|
||||||
wantResponse=True,
|
|
||||||
)
|
|
||||||
except Exception as ex:
|
|
||||||
logger.debug("NodeInfo trigger for node %d failed: %s", node.node_id, ex)
|
|
||||||
time.sleep(5)
|
|
||||||
|
|
||||||
def stop(self) -> None:
|
|
||||||
"""Shut down all nodes and clean up."""
|
|
||||||
if not self._started:
|
|
||||||
return
|
|
||||||
try:
|
|
||||||
pub.unsubscribe(self._on_sim_packet, "meshtastic.receive.simulator")
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
for node in self.nodes:
|
|
||||||
node.close()
|
|
||||||
self._started = False
|
|
||||||
|
|
||||||
def get_node(self, idx: int) -> SimNode:
|
|
||||||
"""Return the SimNode at the given index."""
|
|
||||||
return self.nodes[idx]
|
|
||||||
|
|
||||||
def get_iface(self, idx: int) -> TCPInterface:
|
|
||||||
"""Return the TCPInterface for the node at the given index."""
|
|
||||||
iface = self.nodes[idx].iface
|
|
||||||
assert iface is not None, f"node {idx} has no interface"
|
|
||||||
return iface
|
|
||||||
|
|
||||||
def wait_for_convergence(self, timeout: int = 30) -> bool:
|
|
||||||
"""Wait until every node sees all others in its node DB.
|
|
||||||
|
|
||||||
Returns True if converged, False if timed out (non-fatal — the packet
|
|
||||||
bridge forwards regardless of node DB state).
|
|
||||||
"""
|
|
||||||
if self.n_nodes <= 1:
|
|
||||||
return True
|
|
||||||
deadline = time.monotonic() + timeout
|
|
||||||
while time.monotonic() < deadline:
|
|
||||||
if all(
|
|
||||||
node.iface is not None
|
|
||||||
and node.iface.nodes is not None
|
|
||||||
and len(node.iface.nodes) >= self.n_nodes
|
|
||||||
for node in self.nodes
|
|
||||||
):
|
|
||||||
return True
|
|
||||||
time.sleep(2)
|
|
||||||
logger.warning("Mesh did not fully converge within %ds", timeout)
|
|
||||||
return False
|
|
||||||
|
|
||||||
def _get_receivers(self, tx_idx: int) -> List[int]:
|
|
||||||
"""Return node indices that can hear a transmission from *tx_idx*."""
|
|
||||||
if self.topology is not None:
|
|
||||||
return sorted(self.topology.get(tx_idx, set()))
|
|
||||||
return [i for i in range(self.n_nodes) if i != tx_idx]
|
|
||||||
|
|
||||||
def _on_sim_packet(self, interface, packet) -> None:
|
|
||||||
"""Bridge callback: forward a SIMULATOR_APP packet to receiving nodes."""
|
|
||||||
tx_port = getattr(interface, "portNumber", None)
|
|
||||||
tx_idx = self._port_to_idx.get(tx_port) if tx_port else None
|
|
||||||
if tx_idx is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
rx_indices = self._get_receivers(tx_idx)
|
|
||||||
if not rx_indices:
|
|
||||||
return
|
|
||||||
|
|
||||||
data = packet["decoded"]["payload"]
|
|
||||||
if hasattr(data, "SerializeToString"):
|
|
||||||
data = data.SerializeToString()
|
|
||||||
|
|
||||||
if len(data) > mesh_pb2.Constants.DATA_PAYLOAD_LEN:
|
|
||||||
logger.warning("Simulator payload too big (%d bytes), dropping", len(data))
|
|
||||||
return
|
|
||||||
|
|
||||||
mesh_packet = _build_mesh_packet(packet, data)
|
|
||||||
|
|
||||||
for rx_idx in rx_indices:
|
|
||||||
rx_iface = self.nodes[rx_idx].iface
|
|
||||||
if rx_iface is None:
|
|
||||||
continue
|
|
||||||
mesh_packet.rx_rssi = DEFAULT_RSSI
|
|
||||||
mesh_packet.rx_snr = DEFAULT_SNR
|
|
||||||
to_radio = mesh_pb2.ToRadio()
|
|
||||||
to_radio.packet.CopyFrom(mesh_packet)
|
|
||||||
try:
|
|
||||||
rx_iface._sendToRadio(to_radio)
|
|
||||||
except Exception as ex:
|
|
||||||
logger.error("Error forwarding packet to node %d: %s", rx_idx, ex)
|
|
||||||
|
|
||||||
def __enter__(self):
|
|
||||||
self.start()
|
|
||||||
return self
|
|
||||||
|
|
||||||
def __exit__(self, *exc):
|
|
||||||
self.stop()
|
|
||||||
|
|
||||||
|
|
||||||
def _build_mesh_packet(packet: dict, data: bytes) -> mesh_pb2.MeshPacket:
|
|
||||||
"""Reconstruct a MeshPacket for SIMULATOR_APP injection."""
|
|
||||||
mp = mesh_pb2.MeshPacket()
|
|
||||||
mp.decoded.payload = data
|
|
||||||
mp.decoded.portnum = portnums_pb2.PortNum.SIMULATOR_APP
|
|
||||||
mp.to = packet.get("to", BROADCAST_NUM)
|
|
||||||
setattr(mp, "from", packet.get("from", 0))
|
|
||||||
mp.id = packet.get("id", 0)
|
|
||||||
mp.want_ack = packet.get("wantAck", False)
|
|
||||||
mp.hop_limit = packet.get("hopLimit", 0)
|
|
||||||
mp.hop_start = packet.get("hopStart", 0)
|
|
||||||
mp.via_mqtt = packet.get("viaMQTT", False)
|
|
||||||
mp.relay_node = packet.get("relayNode", 0)
|
|
||||||
mp.next_hop = packet.get("nextHop", 0)
|
|
||||||
mp.channel = int(packet.get("channel", 0))
|
|
||||||
|
|
||||||
decoded = packet.get("decoded", {})
|
|
||||||
if "requestId" in decoded:
|
|
||||||
mp.decoded.request_id = decoded["requestId"]
|
|
||||||
if "wantResponse" in decoded:
|
|
||||||
mp.decoded.want_response = decoded["wantResponse"]
|
|
||||||
if "bitfield" in decoded:
|
|
||||||
mp.decoded.bitfield = decoded["bitfield"]
|
|
||||||
|
|
||||||
return mp
|
|
||||||
@@ -1,374 +0,0 @@
|
|||||||
"""Shared helpers for meshtasticd-backed smoke tests.
|
|
||||||
|
|
||||||
Both ``test_smokevirt`` (single node) and ``test_smokemesh`` (multi-node
|
|
||||||
chain) use these helpers to drive the ``meshtastic`` CLI against real
|
|
||||||
``meshtasticd`` simulator instances and then verify the resulting firmware
|
|
||||||
state through the Python library's ``TCPInterface``.
|
|
||||||
|
|
||||||
Verifying through the library (rather than regex on CLI stdout) is the
|
|
||||||
core design choice: it makes tests robust against CLI wording changes
|
|
||||||
while still exercising both the CLI argparse path and the firmware I/O
|
|
||||||
path of every feature.
|
|
||||||
"""
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import logging
|
|
||||||
import shlex
|
|
||||||
import socket
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
import time
|
|
||||||
from typing import Callable, List, Optional, Tuple
|
|
||||||
|
|
||||||
from pubsub import pub # type: ignore[import-untyped]
|
|
||||||
|
|
||||||
from meshtastic.tcp_interface import TCPInterface
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
# Pause between a CLI command finishing and a verification interface
|
|
||||||
# opening, to let the firmware flush its TCP bookkeeping. Keeps the
|
|
||||||
# simulator happy when many short-lived connections are happening.
|
|
||||||
PAUSE_AFTER_CLI = 0.2
|
|
||||||
|
|
||||||
# Pause after changing the LoRa region. The firmware may restart the TCP
|
|
||||||
# listener while committing the new radio config, so give it time to settle
|
|
||||||
# before the next CLI call or harness reconnect.
|
|
||||||
PAUSE_AFTER_REGION_CHANGE = 2.0
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# CLI invocation
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def resolve_cli() -> str:
|
|
||||||
"""Return a shell-invokable ``meshtastic`` command.
|
|
||||||
|
|
||||||
Prefers the in-tree module run through the current interpreter so
|
|
||||||
tests exercise the source we are editing, regardless of any
|
|
||||||
separately-installed ``meshtastic`` entry point on PATH.
|
|
||||||
"""
|
|
||||||
# The PATH binary may live outside the nono sandbox's allowed paths;
|
|
||||||
# ``python -m meshtastic`` is more portable and always available.
|
|
||||||
return f"{sys.executable} -m meshtastic"
|
|
||||||
|
|
||||||
|
|
||||||
def run_cli(
|
|
||||||
port: int,
|
|
||||||
*args: str,
|
|
||||||
timeout: int = 60,
|
|
||||||
retries: int = 2,
|
|
||||||
retry_delay: float = 1.0,
|
|
||||||
) -> Tuple[int, str]:
|
|
||||||
"""Run the ``meshtastic`` CLI against the sim node on *port*.
|
|
||||||
|
|
||||||
Returns ``(return_code, merged_stdout_stderr)``. ``--host
|
|
||||||
localhost:PORT`` is prefixed automatically. stderr is merged into
|
|
||||||
stdout so callers can match warning text such as "Warning: Need to
|
|
||||||
specify ..." regardless of which stream it lands on.
|
|
||||||
|
|
||||||
If the CLI fails to connect on the first attempt (which happens
|
|
||||||
transiently when a freshly-booted sim node needs a moment to settle
|
|
||||||
after the harness interface connects), retry up to *retries*
|
|
||||||
additional times after *retry_delay* seconds.
|
|
||||||
"""
|
|
||||||
cli = resolve_cli()
|
|
||||||
argv = [*_shlex_split(cli)]
|
|
||||||
argv.extend(["--host", f"localhost:{port}"])
|
|
||||||
argv.extend(args)
|
|
||||||
logger.debug("run_cli: %s", argv)
|
|
||||||
|
|
||||||
last_out = ""
|
|
||||||
for attempt in range(retries + 1):
|
|
||||||
try:
|
|
||||||
proc = subprocess.run(
|
|
||||||
argv,
|
|
||||||
stdout=subprocess.PIPE,
|
|
||||||
stderr=subprocess.STDOUT,
|
|
||||||
timeout=timeout,
|
|
||||||
check=False,
|
|
||||||
)
|
|
||||||
except subprocess.TimeoutExpired as ex:
|
|
||||||
last_out = ex.stdout.decode("utf-8", errors="replace") if ex.stdout else ""
|
|
||||||
if attempt < retries:
|
|
||||||
time.sleep(retry_delay)
|
|
||||||
continue
|
|
||||||
return 124, last_out
|
|
||||||
|
|
||||||
out = proc.stdout.decode("utf-8", errors="replace")
|
|
||||||
rc = proc.returncode
|
|
||||||
|
|
||||||
# Retry on transient connection-refused / timed-out errors that
|
|
||||||
# are common right after a sim node spins up.
|
|
||||||
transient = (
|
|
||||||
"Error connecting" in out
|
|
||||||
or "Timed out waiting for connection" in out
|
|
||||||
or "Connection reset by peer" in out
|
|
||||||
)
|
|
||||||
if rc != 0 and transient and attempt < retries:
|
|
||||||
logger.debug("run_cli: transient failure, retry %d/%d", attempt + 1, retries)
|
|
||||||
time.sleep(retry_delay)
|
|
||||||
last_out = out
|
|
||||||
continue
|
|
||||||
return rc, out
|
|
||||||
return rc, last_out
|
|
||||||
|
|
||||||
|
|
||||||
def _shlex_split(cmd: str) -> List[str]:
|
|
||||||
"""Split a shell string into argv, honoring quotes."""
|
|
||||||
return shlex.split(cmd)
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# Fresh-connection state verification
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def _wait_for_port(port: int, timeout: float = 30.0) -> None:
|
|
||||||
"""Wait until *port* accepts a TCP connection (firmware sim comes up
|
|
||||||
or comes back after a config-commit reboot)."""
|
|
||||||
deadline = time.monotonic() + timeout
|
|
||||||
while time.monotonic() < deadline:
|
|
||||||
try:
|
|
||||||
s = socket.create_connection(("localhost", port), timeout=0.5)
|
|
||||||
s.close()
|
|
||||||
return
|
|
||||||
except OSError:
|
|
||||||
time.sleep(0.2)
|
|
||||||
raise TimeoutError(
|
|
||||||
f"port {port} did not accept connections within {timeout}s"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def connect_iface(
|
|
||||||
port: int,
|
|
||||||
no_nodes: bool = False,
|
|
||||||
retries: int = 4,
|
|
||||||
wait_timeout: float = 30.0,
|
|
||||||
) -> TCPInterface:
|
|
||||||
"""Open a fresh ``TCPInterface`` to *port* and block on the config exchange.
|
|
||||||
|
|
||||||
Firmware config writes (``writeChannel``, ``--seturl``, ``--factory_reset``)
|
|
||||||
can briefly restart the sim's TCP listener. We wait for the port to
|
|
||||||
come up first, then retry the connect+config-exchange a few times.
|
|
||||||
"""
|
|
||||||
last_exc: Optional[Exception] = None
|
|
||||||
for attempt in range(retries + 1):
|
|
||||||
try:
|
|
||||||
_wait_for_port(port, timeout=wait_timeout)
|
|
||||||
return TCPInterface(
|
|
||||||
hostname="localhost",
|
|
||||||
portNumber=port,
|
|
||||||
connectNow=True,
|
|
||||||
noNodes=no_nodes,
|
|
||||||
)
|
|
||||||
except Exception as ex: # pylint: disable=broad-except
|
|
||||||
last_exc = ex
|
|
||||||
if attempt < retries:
|
|
||||||
logger.debug(
|
|
||||||
"connect_iface attempt %d/%d failed: %s",
|
|
||||||
attempt + 1, retries, ex,
|
|
||||||
)
|
|
||||||
time.sleep(0.5)
|
|
||||||
continue
|
|
||||||
raise
|
|
||||||
assert last_exc is not None # type guard; unreachable
|
|
||||||
raise last_exc # pragma: no cover
|
|
||||||
|
|
||||||
|
|
||||||
def verify_state(
|
|
||||||
port: int,
|
|
||||||
verifier: Callable[[TCPInterface], None],
|
|
||||||
*,
|
|
||||||
no_nodes: bool = False,
|
|
||||||
) -> None:
|
|
||||||
"""Open a fresh interface and run *verifier(iface)*, then close.
|
|
||||||
|
|
||||||
Used after a CLI mutation to verify firmware state through the
|
|
||||||
library. Always closes the interface so the next test starts clean.
|
|
||||||
"""
|
|
||||||
iface = connect_iface(port, no_nodes=no_nodes)
|
|
||||||
try:
|
|
||||||
verifier(iface)
|
|
||||||
finally:
|
|
||||||
try:
|
|
||||||
iface.close()
|
|
||||||
except Exception: # pylint: disable=broad-except
|
|
||||||
pass
|
|
||||||
time.sleep(PAUSE_AFTER_CLI)
|
|
||||||
|
|
||||||
|
|
||||||
def cli_then_verify(
|
|
||||||
port: int,
|
|
||||||
cli_args: List[str],
|
|
||||||
verifier: Optional[Callable[[TCPInterface], None]],
|
|
||||||
*,
|
|
||||||
expect_rc: Optional[int] = 0,
|
|
||||||
no_nodes: bool = False,
|
|
||||||
cli_timeout: int = 60,
|
|
||||||
) -> str:
|
|
||||||
"""Run *cli_args* against *port*, optionally asserting *expect_rc*,
|
|
||||||
then (if *verifier* is not None) open a fresh interface and run
|
|
||||||
*verifier(iface)* against the just-mutated firmware state.
|
|
||||||
|
|
||||||
Returns the CLI stdout.
|
|
||||||
"""
|
|
||||||
rc, out = run_cli(port, *cli_args, timeout=cli_timeout)
|
|
||||||
if expect_rc is not None:
|
|
||||||
assert rc == expect_rc, f"CLI rc={rc} (expected {expect_rc}): {out}"
|
|
||||||
time.sleep(PAUSE_AFTER_CLI)
|
|
||||||
if verifier is not None:
|
|
||||||
verify_state(port, verifier, no_nodes=no_nodes)
|
|
||||||
return out
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# Packet collectors (used by smokemesh receive-verification tests)
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
RECEIVE_TIMEOUT = 15.0
|
|
||||||
|
|
||||||
|
|
||||||
class PacketCollector:
|
|
||||||
"""Collect packets received on a specific interface via pubsub.
|
|
||||||
|
|
||||||
``Listener`` (pubsub 4.x) wraps handlers with a weak reference, so
|
|
||||||
we keep a strong reference to ``handler`` on the instance to prevent
|
|
||||||
garbage collection before the publishing thread gets to call it.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.packets: List[dict] = []
|
|
||||||
self._handler: Optional[Callable] = None
|
|
||||||
|
|
||||||
def on_receive(self, packet, interface): # pylint: disable=unused-argument
|
|
||||||
"""Append a received packet to the internal list."""
|
|
||||||
self.packets.append(packet)
|
|
||||||
|
|
||||||
def wait_for(self, count: int, timeout: float = RECEIVE_TIMEOUT) -> bool:
|
|
||||||
"""Wait until *count* packets have been collected or *timeout* expires."""
|
|
||||||
deadline = time.monotonic() + timeout
|
|
||||||
while time.monotonic() < deadline:
|
|
||||||
if len(self.packets) >= count:
|
|
||||||
return True
|
|
||||||
time.sleep(0.2)
|
|
||||||
return len(self.packets) >= count
|
|
||||||
|
|
||||||
@property
|
|
||||||
def texts(self) -> List[str]:
|
|
||||||
"""Return text payloads from TEXT_MESSAGE_APP packets."""
|
|
||||||
return [
|
|
||||||
p.get("decoded", {}).get("text", "")
|
|
||||||
for p in self.packets
|
|
||||||
if p.get("decoded", {}).get("portnum") == "TEXT_MESSAGE_APP"
|
|
||||||
]
|
|
||||||
|
|
||||||
@property
|
|
||||||
def traceroutes(self) -> List[dict]:
|
|
||||||
"""Return TRACEROUTE_APP packets."""
|
|
||||||
return [
|
|
||||||
p for p in self.packets
|
|
||||||
if p.get("decoded", {}).get("portnum") == "TRACEROUTE_APP"
|
|
||||||
]
|
|
||||||
|
|
||||||
@property
|
|
||||||
def telemetries(self) -> List[dict]:
|
|
||||||
"""Return TELEMETRY_APP packets."""
|
|
||||||
return [
|
|
||||||
p for p in self.packets
|
|
||||||
if p.get("decoded", {}).get("portnum") == "TELEMETRY_APP"
|
|
||||||
]
|
|
||||||
|
|
||||||
@property
|
|
||||||
def positions(self) -> List[dict]:
|
|
||||||
"""Return POSITION_APP packets."""
|
|
||||||
return [
|
|
||||||
p for p in self.packets
|
|
||||||
if p.get("decoded", {}).get("portnum") == "POSITION_APP"
|
|
||||||
]
|
|
||||||
|
|
||||||
def reset(self) -> None:
|
|
||||||
"""Clear all collected packets."""
|
|
||||||
self.packets.clear()
|
|
||||||
|
|
||||||
|
|
||||||
def _subscribe_topic(
|
|
||||||
iface: TCPInterface, topic: str
|
|
||||||
) -> PacketCollector:
|
|
||||||
"""Internal: subscribe a fully-qualified *topic* and return a collector.
|
|
||||||
|
|
||||||
Filters by *interface* so multi-node tests can subscribe several
|
|
||||||
collectors concurrently without cross-talk.
|
|
||||||
"""
|
|
||||||
collector = PacketCollector()
|
|
||||||
|
|
||||||
def handler(packet, interface):
|
|
||||||
if interface is iface:
|
|
||||||
collector.on_receive(packet, interface)
|
|
||||||
|
|
||||||
pub.subscribe(handler, topic)
|
|
||||||
collector._handler = handler # strong ref; see PacketCollector docstring
|
|
||||||
return collector
|
|
||||||
|
|
||||||
|
|
||||||
def subscribe_texts(iface: TCPInterface) -> PacketCollector:
|
|
||||||
"""Subscribe to ``meshtastic.receive.text`` filtered to *iface*."""
|
|
||||||
return _subscribe_topic(iface, "meshtastic.receive.text")
|
|
||||||
|
|
||||||
|
|
||||||
def subscribe_traceroutes(iface: TCPInterface) -> PacketCollector:
|
|
||||||
"""Subscribe to ``meshtastic.receive.traceroute`` filtered to *iface*."""
|
|
||||||
return _subscribe_topic(iface, "meshtastic.receive.traceroute")
|
|
||||||
|
|
||||||
|
|
||||||
def subscribe_telemetries(iface: TCPInterface) -> PacketCollector:
|
|
||||||
"""Subscribe to ``meshtastic.receive.telemetry`` filtered to *iface*."""
|
|
||||||
return _subscribe_topic(iface, "meshtastic.receive.telemetry")
|
|
||||||
|
|
||||||
|
|
||||||
def subscribe_positions(iface: TCPInterface) -> PacketCollector:
|
|
||||||
"""Subscribe to ``meshtastic.receive.position`` filtered to *iface*."""
|
|
||||||
return _subscribe_topic(iface, "meshtastic.receive.position")
|
|
||||||
|
|
||||||
|
|
||||||
def set_region(port: int, region: str = "US") -> None:
|
|
||||||
"""Set the LoRa region on a sim node via the CLI.
|
|
||||||
|
|
||||||
The node briefly restarts its TCP listener while committing the radio
|
|
||||||
config; ``run_cli()`` handles the reconnect retry, and we sleep long
|
|
||||||
enough for the new region to take effect before callers continue.
|
|
||||||
"""
|
|
||||||
rc, out = run_cli(port, "--set", "lora.region", region)
|
|
||||||
if rc != 0:
|
|
||||||
raise RuntimeError(f"Failed to set lora.region={region}: {out}")
|
|
||||||
time.sleep(PAUSE_AFTER_REGION_CHANGE)
|
|
||||||
|
|
||||||
|
|
||||||
def unsubscribe_all(topic: str) -> None:
|
|
||||||
"""Drop every handler currently registered on *topic*.
|
|
||||||
|
|
||||||
Tests use this in a ``finally`` block to keep pubsub clean across
|
|
||||||
the function-scoped mesh fixtures.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
pub.unsubAll(topic)
|
|
||||||
except Exception: # pylint: disable=broad-except
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
|
||||||
"PAUSE_AFTER_CLI",
|
|
||||||
"PAUSE_AFTER_REGION_CHANGE",
|
|
||||||
"PacketCollector",
|
|
||||||
"RECEIVE_TIMEOUT",
|
|
||||||
"cli_then_verify",
|
|
||||||
"connect_iface",
|
|
||||||
"resolve_cli",
|
|
||||||
"run_cli",
|
|
||||||
"set_region",
|
|
||||||
"subscribe_positions",
|
|
||||||
"subscribe_telemetries",
|
|
||||||
"subscribe_texts",
|
|
||||||
"subscribe_traceroutes",
|
|
||||||
"unsubscribe_all",
|
|
||||||
"verify_state",
|
|
||||||
]
|
|
||||||
@@ -6,11 +6,7 @@ import sys
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
try:
|
from meshtastic.analysis.__main__ import main
|
||||||
# Depends upon matplotlib & other packages in poetry's analysis group, not installed by default
|
|
||||||
from meshtastic.analysis.__main__ import main
|
|
||||||
except ImportError:
|
|
||||||
pytest.skip("Can't import meshtastic.analysis", allow_module_level=True)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
|
|||||||
@@ -1,69 +0,0 @@
|
|||||||
"""Meshtastic unit tests for ble_interface.py"""
|
|
||||||
|
|
||||||
from unittest.mock import MagicMock, patch
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
from bleak.exc import BleakError
|
|
||||||
|
|
||||||
from ..ble_interface import BLEInterface
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_ble_error_default_kind_unknown():
|
|
||||||
"""BLEError defaults to UNKNOWN kind."""
|
|
||||||
error = BLEInterface.BLEError("test")
|
|
||||||
assert error.kind == BLEInterface.BLEError.UNKNOWN
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_ble_find_device_not_found_sets_kind():
|
|
||||||
"""find_device emits DEVICE_NOT_FOUND for no scan results."""
|
|
||||||
iface = object.__new__(BLEInterface)
|
|
||||||
with patch("meshtastic.ble_interface.BLEInterface.scan", return_value=[]):
|
|
||||||
with pytest.raises(BLEInterface.BLEError) as excinfo:
|
|
||||||
iface.find_device("missing")
|
|
||||||
assert excinfo.value.kind == BLEInterface.BLEError.DEVICE_NOT_FOUND
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_ble_find_device_multiple_sets_kind():
|
|
||||||
"""find_device emits MULTIPLE_DEVICES for ambiguous matches."""
|
|
||||||
iface = object.__new__(BLEInterface)
|
|
||||||
first = MagicMock()
|
|
||||||
first.name = "dup"
|
|
||||||
first.address = "AA:AA:AA:AA:AA:01"
|
|
||||||
second = MagicMock()
|
|
||||||
second.name = "dup"
|
|
||||||
second.address = "AA:AA:AA:AA:AA:02"
|
|
||||||
with patch(
|
|
||||||
"meshtastic.ble_interface.BLEInterface.scan", return_value=[first, second]
|
|
||||||
):
|
|
||||||
with pytest.raises(BLEInterface.BLEError) as excinfo:
|
|
||||||
iface.find_device("dup")
|
|
||||||
assert excinfo.value.kind == BLEInterface.BLEError.MULTIPLE_DEVICES
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_ble_send_to_radio_wraps_write_errors_with_kind():
|
|
||||||
"""_sendToRadioImpl wraps write failures with WRITE_ERROR."""
|
|
||||||
iface = object.__new__(BLEInterface)
|
|
||||||
iface.client = MagicMock()
|
|
||||||
iface.client.write_gatt_char.side_effect = RuntimeError("boom")
|
|
||||||
to_radio = MagicMock()
|
|
||||||
to_radio.SerializeToString.return_value = b"\x01"
|
|
||||||
with pytest.raises(BLEInterface.BLEError) as excinfo:
|
|
||||||
iface._sendToRadioImpl(to_radio)
|
|
||||||
assert excinfo.value.kind == BLEInterface.BLEError.WRITE_ERROR
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_ble_receive_wraps_unexpected_bleak_error_with_kind():
|
|
||||||
"""_receiveFromRadioImpl wraps unexpected BleakError with READ_ERROR."""
|
|
||||||
iface = object.__new__(BLEInterface)
|
|
||||||
iface.should_read = True
|
|
||||||
iface._want_receive = True
|
|
||||||
iface.client = MagicMock()
|
|
||||||
iface.client.read_gatt_char.side_effect = BleakError("some other BLE failure")
|
|
||||||
with pytest.raises(BLEInterface.BLEError) as excinfo:
|
|
||||||
iface._receiveFromRadioImpl()
|
|
||||||
assert excinfo.value.kind == BLEInterface.BLEError.READ_ERROR
|
|
||||||
@@ -1,646 +0,0 @@
|
|||||||
"""Tests for bin/inject_nanopb_options.py — the nanopb options injection script
|
|
||||||
and the generated protobuf descriptors it produces.
|
|
||||||
|
|
||||||
Part 1 (test_parse_*, test_inject_*): unit-tests the script's logic directly,
|
|
||||||
using small synthetic proto snippets.
|
|
||||||
|
|
||||||
Part 2 (test_descriptor_*): smoke-tests the already-generated _pb2.py files to
|
|
||||||
confirm the regen pipeline embedded the expected nanopb options.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import importlib.util
|
|
||||||
import sys
|
|
||||||
import textwrap
|
|
||||||
from pathlib import Path
|
|
||||||
from unittest.mock import patch
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
from hypothesis import given, strategies as st
|
|
||||||
|
|
||||||
from meshtastic.protobuf import (
|
|
||||||
atak_pb2,
|
|
||||||
config_pb2,
|
|
||||||
mesh_pb2,
|
|
||||||
nanopb_pb2,
|
|
||||||
telemetry_pb2,
|
|
||||||
)
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# Load bin/inject_nanopb_options.py as a module without adding it to the
|
|
||||||
# package. __main__ guard means no side-effects on import.
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
_SCRIPT_PATH = Path(__file__).parent.parent.parent / "bin" / "inject_nanopb_options.py"
|
|
||||||
|
|
||||||
|
|
||||||
def _load_inject_module():
|
|
||||||
spec = importlib.util.spec_from_file_location("inject_nanopb_options", _SCRIPT_PATH)
|
|
||||||
mod = importlib.util.module_from_spec(spec)
|
|
||||||
with patch.object(sys, "argv", ["inject_nanopb_options.py"]):
|
|
||||||
spec.loader.exec_module(mod)
|
|
||||||
return mod
|
|
||||||
|
|
||||||
|
|
||||||
_inj = _load_inject_module()
|
|
||||||
|
|
||||||
parse_value = _inj.parse_value
|
|
||||||
parse_options_file = _inj.parse_options_file
|
|
||||||
format_nanopb_opts = _inj.format_nanopb_opts
|
|
||||||
inject_into_proto = _inj.inject_into_proto
|
|
||||||
message_path_matches = _inj.message_path_matches
|
|
||||||
|
|
||||||
# Convenience: the nanopb import path the script uses after the sed fixup
|
|
||||||
NANOPB_IMPORT = 'import "meshtastic/protobuf/nanopb.proto";'
|
|
||||||
|
|
||||||
|
|
||||||
# ===========================================================================
|
|
||||||
# Part 1 — Script unit tests
|
|
||||||
# ===========================================================================
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# parse_value
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_parse_value_integer():
|
|
||||||
"""parse_value converts a decimal string to int."""
|
|
||||||
assert parse_value("40") == 40
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_parse_value_negative_integer():
|
|
||||||
"""parse_value handles negative integer strings."""
|
|
||||||
assert parse_value("-1") == -1
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_parse_value_true():
|
|
||||||
"""parse_value converts 'true' to Python True."""
|
|
||||||
assert parse_value("true") is True
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_parse_value_false():
|
|
||||||
"""parse_value converts 'false' to Python False."""
|
|
||||||
assert parse_value("false") is False
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_parse_value_string():
|
|
||||||
"""parse_value returns non-numeric, non-boolean strings as-is."""
|
|
||||||
assert parse_value("IS_8") == "IS_8"
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
@given(st.integers())
|
|
||||||
def test_parse_value_any_integer_returns_int(n):
|
|
||||||
"""parse_value always returns int for any decimal integer string."""
|
|
||||||
assert parse_value(str(n)) == n
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
@given(st.text())
|
|
||||||
def test_parse_value_never_crashes(s):
|
|
||||||
"""parse_value never raises on arbitrary input."""
|
|
||||||
result = parse_value(s)
|
|
||||||
assert isinstance(result, (int, bool, str))
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
@given(st.text().filter(lambda s: not s.lstrip("-").isdigit() and s.lower() not in ("true", "false")))
|
|
||||||
def test_parse_value_non_numeric_non_bool_returns_str(s):
|
|
||||||
"""parse_value returns the original string when it is neither an integer nor a boolean."""
|
|
||||||
assert parse_value(s) == s
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# parse_options_file
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
def _write_options(tmp_path: Path, content: str) -> Path:
|
|
||||||
p = tmp_path / "test.options"
|
|
||||||
p.write_text(textwrap.dedent(content))
|
|
||||||
return p
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_parse_wildcard(tmp_path):
|
|
||||||
"""Wildcard pattern (no dot) lands in the wildcard dict."""
|
|
||||||
f = _write_options(tmp_path, "*macaddr max_size:6 fixed_length:true\n")
|
|
||||||
specific, wildcard = parse_options_file(f)
|
|
||||||
assert "macaddr" in wildcard
|
|
||||||
assert wildcard["macaddr"] == {"max_size": 6, "fixed_length": True}
|
|
||||||
assert specific == {}
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_parse_specific(tmp_path):
|
|
||||||
"""Single-dot pattern lands in the specific dict with a 2-tuple key."""
|
|
||||||
f = _write_options(tmp_path, "*User.long_name max_size:40\n")
|
|
||||||
specific, wildcard = parse_options_file(f)
|
|
||||||
assert ("User", "long_name") in specific
|
|
||||||
assert specific[("User", "long_name")] == {"max_size": 40}
|
|
||||||
assert wildcard == {}
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_parse_multilevel(tmp_path):
|
|
||||||
"""Three-part pattern (Route.Link.uid) produces a 3-tuple key."""
|
|
||||||
f = _write_options(tmp_path, "*Route.Link.uid max_size:48\n")
|
|
||||||
specific, _ = parse_options_file(f)
|
|
||||||
assert ("Route", "Link", "uid") in specific
|
|
||||||
assert specific[("Route", "Link", "uid")] == {"max_size": 48}
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_parse_strips_inline_comments(tmp_path):
|
|
||||||
"""Text after # is ignored."""
|
|
||||||
f = _write_options(tmp_path, "*id max_size:16 # node id strings\n")
|
|
||||||
_, wildcard = parse_options_file(f)
|
|
||||||
assert wildcard["id"] == {"max_size": 16}
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_parse_skips_comment_only_lines(tmp_path):
|
|
||||||
"""Lines that are entirely comments produce no entries."""
|
|
||||||
f = _write_options(tmp_path, "# this is a comment\n*id max_size:16\n")
|
|
||||||
_, wildcard = parse_options_file(f)
|
|
||||||
assert list(wildcard.keys()) == ["id"]
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_parse_skips_blank_lines(tmp_path):
|
|
||||||
"""Blank lines are silently ignored."""
|
|
||||||
f = _write_options(tmp_path, "\n\n*id max_size:16\n\n")
|
|
||||||
_, wildcard = parse_options_file(f)
|
|
||||||
assert "id" in wildcard
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_parse_skips_non_python_options(tmp_path):
|
|
||||||
"""Options not in FIELD_OPTIONS (e.g. anonymous_oneof) are dropped."""
|
|
||||||
f = _write_options(tmp_path, "*MeshPacket.payload_variant anonymous_oneof:true\n")
|
|
||||||
specific, wildcard = parse_options_file(f)
|
|
||||||
# anonymous_oneof is not in FIELD_OPTIONS → no entry should be produced
|
|
||||||
assert specific == {}
|
|
||||||
assert wildcard == {}
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_parse_merges_repeated_patterns(tmp_path):
|
|
||||||
"""Two lines for the same pattern are merged."""
|
|
||||||
f = _write_options(
|
|
||||||
tmp_path,
|
|
||||||
"*SecurityConfig.admin_key max_size:32\n"
|
|
||||||
"*SecurityConfig.admin_key max_count:3\n",
|
|
||||||
)
|
|
||||||
specific, _ = parse_options_file(f)
|
|
||||||
assert specific[("SecurityConfig", "admin_key")] == {"max_size": 32, "max_count": 3}
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_parse_int_and_bool_values(tmp_path):
|
|
||||||
"""int_size parses as int; fixed_length parses as bool."""
|
|
||||||
f = _write_options(tmp_path, "*Data.payload max_size:233 fixed_length:false\n")
|
|
||||||
specific, _ = parse_options_file(f)
|
|
||||||
opts = specific[("Data", "payload")]
|
|
||||||
assert opts["max_size"] == 233
|
|
||||||
assert opts["fixed_length"] is False
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# message_path_matches
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_message_path_matches_simple():
|
|
||||||
"""A single-element path matches the current message on the stack."""
|
|
||||||
stack = [("message", "User")]
|
|
||||||
assert message_path_matches(stack, ("User",))
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_message_path_matches_nested():
|
|
||||||
"""Both a 1-element and 2-element path match correctly against a nested stack."""
|
|
||||||
stack = [("message", "Config"), ("message", "DeviceConfig")]
|
|
||||||
assert message_path_matches(stack, ("DeviceConfig",))
|
|
||||||
assert message_path_matches(stack, ("Config", "DeviceConfig"))
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_message_path_matches_with_oneof_in_stack():
|
|
||||||
"""oneof frames in the stack are skipped when looking for messages."""
|
|
||||||
stack = [("message", "MeshPacket"), ("oneof", "payload_variant")]
|
|
||||||
assert message_path_matches(stack, ("MeshPacket",))
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_message_path_no_match():
|
|
||||||
"""A path with the wrong message name does not match."""
|
|
||||||
stack = [("message", "User")]
|
|
||||||
assert not message_path_matches(stack, ("Route",))
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_message_path_multilevel_partial_match():
|
|
||||||
"""A 2-element path must match the last 2 message names on the stack."""
|
|
||||||
stack = [("message", "Route"), ("message", "Link")]
|
|
||||||
assert message_path_matches(stack, ("Route", "Link"))
|
|
||||||
assert not message_path_matches(stack, ("Other", "Link"))
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# format_nanopb_opts
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_format_max_size():
|
|
||||||
"""max_size is rendered as an integer literal."""
|
|
||||||
assert format_nanopb_opts({"max_size": 40}) == "(nanopb).max_size = 40"
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_format_int_size_as_enum():
|
|
||||||
"""int_size numeric values are rendered as IS_8/IS_16/IS_32/IS_64 enum names."""
|
|
||||||
assert format_nanopb_opts({"int_size": 8}) == "(nanopb).int_size = IS_8"
|
|
||||||
assert format_nanopb_opts({"int_size": 16}) == "(nanopb).int_size = IS_16"
|
|
||||||
assert format_nanopb_opts({"int_size": 32}) == "(nanopb).int_size = IS_32"
|
|
||||||
assert format_nanopb_opts({"int_size": 64}) == "(nanopb).int_size = IS_64"
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_format_bool_true():
|
|
||||||
"""True is rendered as the proto literal 'true'."""
|
|
||||||
assert format_nanopb_opts({"fixed_length": True}) == "(nanopb).fixed_length = true"
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_format_bool_false():
|
|
||||||
"""False is rendered as the proto literal 'false'."""
|
|
||||||
assert format_nanopb_opts({"fixed_length": False}) == "(nanopb).fixed_length = false"
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# inject_into_proto — helpers
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
_NANOPB_IMPORT_PATH = "meshtastic/protobuf/nanopb.proto"
|
|
||||||
|
|
||||||
|
|
||||||
def _inject(proto_src: str, specific=None, wildcard=None) -> str:
|
|
||||||
"""Run inject_into_proto with empty dicts as defaults."""
|
|
||||||
return inject_into_proto(
|
|
||||||
textwrap.dedent(proto_src),
|
|
||||||
specific or {},
|
|
||||||
wildcard or {},
|
|
||||||
_NANOPB_IMPORT_PATH,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# inject_into_proto — option injection
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_inject_adds_option_to_plain_field():
|
|
||||||
"""A field with no existing options gets a nanopb annotation."""
|
|
||||||
proto = """\
|
|
||||||
syntax = "proto3";
|
|
||||||
import "meshtastic/protobuf/channel.proto";
|
|
||||||
message User {
|
|
||||||
string long_name = 1;
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
result = _inject(proto, specific={("User", "long_name"): {"max_size": 40}})
|
|
||||||
assert "long_name = 1 [(nanopb).max_size = 40];" in result
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_inject_merges_with_existing_options():
|
|
||||||
"""nanopb annotation is appended after existing field options."""
|
|
||||||
proto = """\
|
|
||||||
syntax = "proto3";
|
|
||||||
import "meshtastic/protobuf/channel.proto";
|
|
||||||
message User {
|
|
||||||
bytes macaddr = 4 [deprecated = true];
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
result = _inject(proto, wildcard={"macaddr": {"max_size": 6}})
|
|
||||||
assert "[deprecated = true, (nanopb).max_size = 6];" in result
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_inject_int_size_uses_enum_name():
|
|
||||||
"""int_size values are written as IS_N enum names, not raw integers."""
|
|
||||||
proto = """\
|
|
||||||
syntax = "proto3";
|
|
||||||
import "meshtastic/protobuf/mesh.proto";
|
|
||||||
message Foo {
|
|
||||||
uint32 hop_limit = 9;
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
result = _inject(proto, specific={("Foo", "hop_limit"): {"int_size": 8}})
|
|
||||||
assert "(nanopb).int_size = IS_8" in result
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_inject_wildcard_applied_across_messages():
|
|
||||||
"""A wildcard option hits the matching field in every message."""
|
|
||||||
proto = """\
|
|
||||||
syntax = "proto3";
|
|
||||||
import "meshtastic/protobuf/mesh.proto";
|
|
||||||
message A {
|
|
||||||
bytes macaddr = 1;
|
|
||||||
}
|
|
||||||
message B {
|
|
||||||
bytes macaddr = 2;
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
result = _inject(proto, wildcard={"macaddr": {"max_size": 6}})
|
|
||||||
assert result.count("(nanopb).max_size = 6") == 2
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_inject_specific_not_leaking_to_other_messages():
|
|
||||||
"""A message-specific option does NOT apply to a different message's field."""
|
|
||||||
proto = """\
|
|
||||||
syntax = "proto3";
|
|
||||||
import "meshtastic/protobuf/mesh.proto";
|
|
||||||
message User {
|
|
||||||
string id = 1;
|
|
||||||
}
|
|
||||||
message Other {
|
|
||||||
string id = 1;
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
result = _inject(proto, specific={("User", "id"): {"max_size": 16}})
|
|
||||||
# Easier: count annotations — should be exactly one
|
|
||||||
assert result.count("(nanopb).max_size = 16") == 1
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_inject_nested_message():
|
|
||||||
"""A 2-part specific key only hits the field in the correct nested message."""
|
|
||||||
proto = """\
|
|
||||||
syntax = "proto3";
|
|
||||||
import "meshtastic/protobuf/mesh.proto";
|
|
||||||
message Route {
|
|
||||||
message Link {
|
|
||||||
string uid = 1;
|
|
||||||
}
|
|
||||||
string uid = 2;
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
# Route.Link.uid → key = ('Route', 'Link', 'uid')
|
|
||||||
result = _inject(proto, specific={("Route", "Link", "uid"): {"max_size": 48}})
|
|
||||||
lines = result.splitlines()
|
|
||||||
# Only the uid inside Link should have the annotation
|
|
||||||
assert result.count("(nanopb).max_size = 48") == 1
|
|
||||||
# Confirm it's the inner one (it has 4 spaces more indent than outer uid)
|
|
||||||
annotated = next(l for l in lines if "(nanopb).max_size = 48" in l)
|
|
||||||
plain = next(l for l in lines if "uid = 2" in l)
|
|
||||||
assert annotated.index("uid") > plain.index("uid")
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_inject_skips_enum_body_values():
|
|
||||||
"""Enum value lines must not be treated as field declarations."""
|
|
||||||
proto = """\
|
|
||||||
syntax = "proto3";
|
|
||||||
message Foo {
|
|
||||||
enum Role {
|
|
||||||
CLIENT = 0;
|
|
||||||
ROUTER = 2;
|
|
||||||
}
|
|
||||||
Role role = 1;
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
# Wildcard for 'role' should only hit the field, not enum values
|
|
||||||
result = _inject(proto, wildcard={"role": {"max_size": 8}})
|
|
||||||
assert result.count("(nanopb)") == 1
|
|
||||||
assert "(nanopb)" not in next(l for l in result.splitlines() if "CLIENT" in l)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_inject_optional_qualifier_preserved():
|
|
||||||
"""The 'optional' qualifier is kept when a field gets an annotation."""
|
|
||||||
proto = """\
|
|
||||||
syntax = "proto3";
|
|
||||||
import "meshtastic/protobuf/mesh.proto";
|
|
||||||
message Foo {
|
|
||||||
optional uint32 altitude = 3;
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
result = _inject(proto, specific={("Foo", "altitude"): {"int_size": 16}})
|
|
||||||
assert "optional uint32 altitude = 3 [(nanopb).int_size = IS_16];" in result
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_inject_repeated_qualifier_preserved():
|
|
||||||
"""The 'repeated' qualifier is kept when a field gets an annotation."""
|
|
||||||
proto = """\
|
|
||||||
syntax = "proto3";
|
|
||||||
import "meshtastic/protobuf/mesh.proto";
|
|
||||||
message Foo {
|
|
||||||
repeated int32 snr = 2;
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
result = _inject(proto, specific={("Foo", "snr"): {"max_count": 8}})
|
|
||||||
assert "repeated int32 snr = 2 [(nanopb).max_count = 8];" in result
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_inject_multiple_options_on_one_field():
|
|
||||||
"""Multiple options from the same pattern are all injected on one field."""
|
|
||||||
proto = """\
|
|
||||||
syntax = "proto3";
|
|
||||||
import "meshtastic/protobuf/mesh.proto";
|
|
||||||
message Foo {
|
|
||||||
repeated int32 snr = 1;
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
result = _inject(proto, specific={("Foo", "snr"): {"max_count": 8, "int_size": 8}})
|
|
||||||
assert "(nanopb).max_count = 8" in result
|
|
||||||
assert "(nanopb).int_size = IS_8" in result
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# inject_into_proto — import insertion
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_inject_adds_nanopb_import_when_absent():
|
|
||||||
"""nanopb.proto import is added when the file has other imports."""
|
|
||||||
proto = """\
|
|
||||||
syntax = "proto3";
|
|
||||||
import "meshtastic/protobuf/mesh.proto";
|
|
||||||
message Foo {
|
|
||||||
string name = 1;
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
result = _inject(proto, wildcard={"name": {"max_size": 30}})
|
|
||||||
assert NANOPB_IMPORT in result
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_inject_no_duplicate_nanopb_import():
|
|
||||||
"""nanopb.proto import is NOT added a second time if already present."""
|
|
||||||
proto = """\
|
|
||||||
syntax = "proto3";
|
|
||||||
import "meshtastic/protobuf/mesh.proto";
|
|
||||||
import "meshtastic/protobuf/nanopb.proto";
|
|
||||||
message Foo {
|
|
||||||
string name = 1;
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
result = _inject(proto, wildcard={"name": {"max_size": 30}})
|
|
||||||
assert result.count(NANOPB_IMPORT) == 1
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_inject_import_placed_after_existing_imports():
|
|
||||||
"""nanopb import appears after the last existing import, not at the top."""
|
|
||||||
proto = """\
|
|
||||||
syntax = "proto3";
|
|
||||||
import "meshtastic/protobuf/mesh.proto";
|
|
||||||
message Foo {
|
|
||||||
string name = 1;
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
result = _inject(proto, wildcard={"name": {"max_size": 30}})
|
|
||||||
lines = result.splitlines()
|
|
||||||
mesh_idx = next(i for i, l in enumerate(lines) if "mesh.proto" in l)
|
|
||||||
nanopb_idx = next(i for i, l in enumerate(lines) if "nanopb.proto" in l)
|
|
||||||
assert nanopb_idx == mesh_idx + 1
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_inject_import_after_syntax_when_no_existing_imports():
|
|
||||||
"""When a proto has no imports, nanopb import goes AFTER the syntax line,
|
|
||||||
not before it (regression test for the last_import_idx == -1 bug)."""
|
|
||||||
proto = """\
|
|
||||||
syntax = "proto3";
|
|
||||||
message XModem {
|
|
||||||
uint32 seq = 2;
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
result = _inject(proto, specific={("XModem", "seq"): {"int_size": 16}})
|
|
||||||
lines = result.splitlines()
|
|
||||||
syntax_idx = next(i for i, l in enumerate(lines) if l.strip().startswith("syntax"))
|
|
||||||
nanopb_idx = next(i for i, l in enumerate(lines) if "nanopb.proto" in l)
|
|
||||||
assert nanopb_idx > syntax_idx, "nanopb import must come after the syntax line"
|
|
||||||
# syntax line must still be first non-blank line
|
|
||||||
first_non_blank = next(l.strip() for l in lines if l.strip())
|
|
||||||
assert first_non_blank.startswith("syntax")
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_inject_noop_when_no_options():
|
|
||||||
"""Proto file is returned unchanged when there are no options to inject."""
|
|
||||||
proto = 'syntax = "proto3";\nmessage Foo { string x = 1; }\n'
|
|
||||||
result = _inject(proto)
|
|
||||||
assert result == proto
|
|
||||||
|
|
||||||
|
|
||||||
# ===========================================================================
|
|
||||||
# Part 2 — Descriptor integration tests
|
|
||||||
# Verify that regen-protobufs.sh produced _pb2.py files with nanopb options
|
|
||||||
# embedded in the serialized descriptors.
|
|
||||||
# ===========================================================================
|
|
||||||
|
|
||||||
|
|
||||||
def _field_opts(descriptor, *path):
|
|
||||||
"""Walk a descriptor by field/nested-type path and return its nanopb opts.
|
|
||||||
|
|
||||||
Elements of *path that are message names are looked up in nested_types_by_name;
|
|
||||||
the final element is looked up in fields_by_name.
|
|
||||||
"""
|
|
||||||
desc = descriptor
|
|
||||||
for step in path[:-1]:
|
|
||||||
desc = desc.nested_types_by_name[step]
|
|
||||||
field = desc.fields_by_name[path[-1]]
|
|
||||||
return field.GetOptions().Extensions[nanopb_pb2.nanopb]
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_descriptor_user_long_name():
|
|
||||||
"""User.long_name has max_size = 40 from mesh.options."""
|
|
||||||
opts = _field_opts(mesh_pb2.DESCRIPTOR.message_types_by_name["User"], "long_name")
|
|
||||||
assert opts.max_size == 40
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_descriptor_user_short_name():
|
|
||||||
"""User.short_name has max_size = 5 from mesh.options."""
|
|
||||||
opts = _field_opts(mesh_pb2.DESCRIPTOR.message_types_by_name["User"], "short_name")
|
|
||||||
assert opts.max_size == 5
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_descriptor_wildcard_macaddr():
|
|
||||||
"""Wildcard option from mesh.options applied to User.macaddr."""
|
|
||||||
opts = _field_opts(mesh_pb2.DESCRIPTOR.message_types_by_name["User"], "macaddr")
|
|
||||||
assert opts.max_size == 6
|
|
||||||
assert opts.fixed_length is True
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_descriptor_meshpacket_hop_limit():
|
|
||||||
"""MeshPacket.hop_limit has int_size = IS_8 from mesh.options."""
|
|
||||||
opts = _field_opts(mesh_pb2.DESCRIPTOR.message_types_by_name["MeshPacket"], "hop_limit")
|
|
||||||
assert opts.int_size == nanopb_pb2.IS_8
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_descriptor_routediscovery_snr_towards():
|
|
||||||
"""RouteDiscovery.snr_towards has max_count = 8 and int_size = IS_8 from mesh.options."""
|
|
||||||
opts = _field_opts(
|
|
||||||
mesh_pb2.DESCRIPTOR.message_types_by_name["RouteDiscovery"], "snr_towards"
|
|
||||||
)
|
|
||||||
assert opts.max_count == 8
|
|
||||||
assert opts.int_size == nanopb_pb2.IS_8
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_descriptor_data_payload():
|
|
||||||
"""Data.payload has max_size = 233 from mesh.options."""
|
|
||||||
opts = _field_opts(mesh_pb2.DESCRIPTOR.message_types_by_name["Data"], "payload")
|
|
||||||
assert opts.max_size == 233
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_descriptor_nested_deviceconfig_tzdef():
|
|
||||||
"""Config.DeviceConfig.tzdef — option on a field inside a nested message."""
|
|
||||||
config = config_pb2.DESCRIPTOR.message_types_by_name["Config"]
|
|
||||||
opts = _field_opts(config, "DeviceConfig", "tzdef")
|
|
||||||
assert opts.max_size == 65
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_descriptor_nested_securityconfig_admin_key():
|
|
||||||
"""Config.SecurityConfig.admin_key — two options merged from two .options lines."""
|
|
||||||
config = config_pb2.DESCRIPTOR.message_types_by_name["Config"]
|
|
||||||
opts = _field_opts(config, "SecurityConfig", "admin_key")
|
|
||||||
assert opts.max_size == 32
|
|
||||||
assert opts.max_count == 3
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_descriptor_multilevel_nested_route_link_uid():
|
|
||||||
"""Route.Link.uid — three-level nested pattern from atak.options."""
|
|
||||||
route = atak_pb2.DESCRIPTOR.message_types_by_name["Route"]
|
|
||||||
opts = _field_opts(route, "Link", "uid")
|
|
||||||
assert opts.max_size == 48
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_descriptor_telemetry_environment_one_wire_temperature():
|
|
||||||
"""EnvironmentMetrics.one_wire_temperature has max_count = 8 from telemetry.options."""
|
|
||||||
env = telemetry_pb2.DESCRIPTOR.message_types_by_name["EnvironmentMetrics"]
|
|
||||||
opts = _field_opts(env, "one_wire_temperature")
|
|
||||||
assert opts.max_count == 8
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -11,12 +11,8 @@ from ..protobuf import mesh_pb2, config_pb2
|
|||||||
from .. import BROADCAST_ADDR, LOCAL_ADDR
|
from .. import BROADCAST_ADDR, LOCAL_ADDR
|
||||||
from ..mesh_interface import MeshInterface, _timeago
|
from ..mesh_interface import MeshInterface, _timeago
|
||||||
from ..node import Node
|
from ..node import Node
|
||||||
try:
|
from ..slog import LogSet
|
||||||
# Depends upon the powermon group, not installed by default
|
from ..powermon import SimPowerSupply
|
||||||
from ..slog import LogSet
|
|
||||||
from ..powermon import SimPowerSupply
|
|
||||||
except ImportError:
|
|
||||||
pytest.skip("Can't import LogSet or SimPowerSupply", allow_module_level=True)
|
|
||||||
|
|
||||||
# TODO
|
# TODO
|
||||||
# from ..config import Config
|
# from ..config import Config
|
||||||
@@ -525,28 +521,6 @@ def test_getMyNodeInfo():
|
|||||||
myinfo = iface.getMyNodeInfo()
|
myinfo = iface.getMyNodeInfo()
|
||||||
assert myinfo == anode
|
assert myinfo == anode
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
@pytest.mark.usefixtures("reset_mt_config")
|
|
||||||
def test_getCannedMessage():
|
|
||||||
"""Test MeshInterface.getCannedMessage()"""
|
|
||||||
iface = MeshInterface(noProto=True)
|
|
||||||
node = MagicMock()
|
|
||||||
node.get_canned_message.return_value = "Hi|Bye|Yes"
|
|
||||||
iface.localNode = node
|
|
||||||
result = iface.getCannedMessage()
|
|
||||||
assert result == "Hi|Bye|Yes"
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
@pytest.mark.usefixtures("reset_mt_config")
|
|
||||||
def test_getRingtone():
|
|
||||||
"""Test MeshInterface.getRingtone()"""
|
|
||||||
iface = MeshInterface(noProto=True)
|
|
||||||
node = MagicMock()
|
|
||||||
node.get_ringtone.return_value = "foo,bar"
|
|
||||||
iface.localNode = node
|
|
||||||
result = iface.getRingtone()
|
|
||||||
assert result == "foo,bar"
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@pytest.mark.usefixtures("reset_mt_config")
|
@pytest.mark.usefixtures("reset_mt_config")
|
||||||
@@ -565,6 +539,7 @@ def test_generatePacketId(capsys):
|
|||||||
assert err == ""
|
assert err == ""
|
||||||
assert pytest_wrapped_e.type == MeshInterface.MeshInterfaceError
|
assert pytest_wrapped_e.type == MeshInterface.MeshInterfaceError
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@pytest.mark.usefixtures("reset_mt_config")
|
@pytest.mark.usefixtures("reset_mt_config")
|
||||||
def test_fixupPosition_empty_pos():
|
def test_fixupPosition_empty_pos():
|
||||||
@@ -672,21 +647,15 @@ def test_getOrCreateByNum(iface_with_nodes):
|
|||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
def test_exit_with_exception(caplog):
|
def test_exit_with_exception(caplog):
|
||||||
"""Test __exit__()"""
|
"""Test __exit__()"""
|
||||||
|
iface = MeshInterface(noProto=True)
|
||||||
with caplog.at_level(logging.ERROR):
|
with caplog.at_level(logging.ERROR):
|
||||||
try:
|
iface.__exit__("foo", "bar", "baz")
|
||||||
with MeshInterface(noProto=True):
|
assert re.search(
|
||||||
raise ValueError("Something went wrong")
|
r"An exception of type foo with value bar has occurred",
|
||||||
except:
|
caplog.text,
|
||||||
assert re.search(
|
re.MULTILINE,
|
||||||
r"An exception of type <class \'ValueError\'> with value Something went wrong has occurred",
|
)
|
||||||
caplog.text,
|
assert re.search(r"Traceback: baz", caplog.text, re.MULTILINE)
|
||||||
re.MULTILINE,
|
|
||||||
)
|
|
||||||
assert re.search(
|
|
||||||
r"Traceback:\n.*in test_exit_with_exception\n {4}raise ValueError\(\"Something went wrong\"\)",
|
|
||||||
caplog.text,
|
|
||||||
re.MULTILINE
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@@ -757,43 +726,3 @@ def test_timeago_fuzz(seconds):
|
|||||||
"""Fuzz _timeago to ensure it works with any integer"""
|
"""Fuzz _timeago to ensure it works with any integer"""
|
||||||
val = _timeago(seconds)
|
val = _timeago(seconds)
|
||||||
assert re.match(r"(now|\d+ (secs?|mins?|hours?|days?|months?|years?))", val)
|
assert re.match(r"(now|\d+ (secs?|mins?|hours?|days?|months?|years?))", val)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
@pytest.mark.usefixtures("reset_mt_config")
|
|
||||||
def test_onResponseTraceRoute_routing_error(capsys):
|
|
||||||
"""Test that onResponseTraceRoute handles ROUTING_APP error packets correctly."""
|
|
||||||
iface = MeshInterface(noProto=True)
|
|
||||||
|
|
||||||
packet = {
|
|
||||||
"decoded": {
|
|
||||||
"portnum": "ROUTING_APP",
|
|
||||||
"routing": {"errorReason": "MAX_RETRANSMIT"},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
iface.onResponseTraceRoute(packet)
|
|
||||||
|
|
||||||
assert iface._acknowledgment.receivedTraceRoute is True
|
|
||||||
out, _ = capsys.readouterr()
|
|
||||||
assert "Traceroute failed: MAX_RETRANSMIT" in out
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
@pytest.mark.usefixtures("reset_mt_config")
|
|
||||||
def test_onResponseTraceRoute_routing_none(capsys):
|
|
||||||
"""Test that onResponseTraceRoute does not print error for ROUTING_APP with NONE errorReason."""
|
|
||||||
iface = MeshInterface(noProto=True)
|
|
||||||
|
|
||||||
packet = {
|
|
||||||
"decoded": {
|
|
||||||
"portnum": "ROUTING_APP",
|
|
||||||
"routing": {"errorReason": "NONE"},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
iface.onResponseTraceRoute(packet)
|
|
||||||
|
|
||||||
assert iface._acknowledgment.receivedTraceRoute is True
|
|
||||||
out, _ = capsys.readouterr()
|
|
||||||
assert "Traceroute failed" not in out
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user