mirror of
https://github.com/meshtastic/python.git
synced 2026-06-02 12:45:00 -04:00
Merge branch 'master' into review/pr-901
This commit is contained in:
206
.github/copilot-instructions.md
vendored
Normal file
206
.github/copilot-instructions.md
vendored
Normal file
@@ -0,0 +1,206 @@
|
||||
# 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
|
||||
33
.github/workflows/update_protobufs.yml
vendored
33
.github/workflows/update_protobufs.yml
vendored
@@ -1,6 +1,9 @@
|
||||
name: "Update protobufs"
|
||||
on: workflow_dispatch
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
update-protobufs:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -9,23 +12,34 @@ jobs:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: true
|
||||
|
||||
- name: Update Submodule
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.12"
|
||||
|
||||
- name: Install Poetry
|
||||
run: |
|
||||
git pull --recurse-submodules
|
||||
python -m pip install --upgrade pip
|
||||
python -m pip install poetry
|
||||
|
||||
- name: Update protobuf submodule
|
||||
run: |
|
||||
git submodule sync --recursive
|
||||
git submodule update --init --recursive
|
||||
git submodule update --remote --recursive
|
||||
|
||||
- name: Download nanopb
|
||||
run: |
|
||||
wget https://jpa.kapsi.fi/nanopb/download/nanopb-0.4.8-linux-x86.tar.gz
|
||||
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
|
||||
tar xvzf nanopb-0.4.8-linux-x86.tar.gz
|
||||
mv nanopb-0.4.8-linux-x86 nanopb-0.4.8
|
||||
|
||||
- name: Install poetry (needed by regen-protobufs.sh)
|
||||
- name: Install Python dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip3 install poetry
|
||||
poetry install --with dev
|
||||
|
||||
- name: Re-generate protocol buffers
|
||||
run: |
|
||||
@@ -38,4 +52,9 @@ jobs:
|
||||
git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}
|
||||
git add protobufs
|
||||
git add meshtastic/protobuf
|
||||
git commit -m "Update protobuf submodule" && git push || echo "No changes to commit"
|
||||
if [[ -n "$(git status --porcelain)" ]]; then
|
||||
git commit -m "Update protobufs"
|
||||
git push
|
||||
else
|
||||
echo "No changes to commit"
|
||||
fi
|
||||
|
||||
332
bin/inject_nanopb_options.py
Normal file
332
bin/inject_nanopb_options.py
Normal file
@@ -0,0 +1,332 @@
|
||||
#!/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())
|
||||
@@ -28,6 +28,7 @@ OUTDIR=${TMPDIR}/out
|
||||
PYIDIR=${TMPDIR}/out
|
||||
mkdir -p "${OUTDIR}" "${INDIR}" "${PYIDIR}"
|
||||
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
|
||||
@@ -45,6 +46,19 @@ $SEDCMD 's/^import "meshtastic\//import "meshtastic\/protobuf\//' "${INDIR}/"*.p
|
||||
|
||||
$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
|
||||
./nanopb-0.4.8/generator-bin/protoc -I=$TMPDIR/in --python_out "${OUTDIR}" "--mypy_out=${PYIDIR}" $INDIR/*.proto
|
||||
|
||||
|
||||
@@ -7,8 +7,7 @@ import time
|
||||
|
||||
from pubsub import pub
|
||||
|
||||
import meshtastic
|
||||
import meshtastic.tcp_interface
|
||||
from meshtastic.tcp_interface import TCPInterface
|
||||
|
||||
# simple arg check
|
||||
if len(sys.argv) < 2:
|
||||
@@ -29,11 +28,15 @@ def onConnection(interface, topic=pub.AUTO_TOPIC): # pylint: disable=unused-arg
|
||||
|
||||
pub.subscribe(onReceive, "meshtastic.receive")
|
||||
pub.subscribe(onConnection, "meshtastic.connection.established")
|
||||
|
||||
iface=None
|
||||
try:
|
||||
iface = meshtastic.tcp_interface.TCPInterface(hostname=sys.argv[1])
|
||||
iface = 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)
|
||||
raise
|
||||
finally:
|
||||
if iface:
|
||||
iface.close()
|
||||
|
||||
73
examples/replymessage.py
Normal file
73
examples/replymessage.py
Normal file
@@ -0,0 +1,73 @@
|
||||
"""Reply message demo script.
|
||||
To run: python examples/replymessage.py
|
||||
To run with TCP: python examples/replymessage.py --host 192.168.1.5
|
||||
To run with BLE: python examples/replymessage.py --ble 24:62:AB:DD:DF:3A
|
||||
"""
|
||||
|
||||
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: # pylint: disable=unused-argument
|
||||
"""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:
|
||||
try:
|
||||
if iface:
|
||||
iface.close()
|
||||
except AttributeError:
|
||||
pass
|
||||
73
examples/textchat.py
Normal file
73
examples/textchat.py
Normal file
@@ -0,0 +1,73 @@
|
||||
"""Simple text chat demo for meshtastic.
|
||||
To run: python examples/textchat.py
|
||||
To run with TCP: python examples/textchat.py --host 192.168.1.5
|
||||
To run with BLE: python examples/textchat.py --ble 24:62:AB:DD:DF:3A
|
||||
"""
|
||||
|
||||
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:
|
||||
try:
|
||||
if iface:
|
||||
iface.close()
|
||||
except AttributeError:
|
||||
pass
|
||||
@@ -179,8 +179,30 @@ def _onPositionReceive(iface, asDict):
|
||||
logger.debug(f"p:{p}")
|
||||
p = iface._fixupPosition(p)
|
||||
logger.debug(f"after fixup p:{p}")
|
||||
# update node DB as needed
|
||||
iface._getOrCreateByNum(asDict["from"])["position"] = p
|
||||
# For the local node, only accept position updates with equal
|
||||
# or better precision. The local GPS is authoritative, and
|
||||
# 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):
|
||||
|
||||
@@ -37,6 +37,7 @@ try:
|
||||
except ImportError as e:
|
||||
have_test = False
|
||||
|
||||
import meshtastic.ota
|
||||
import meshtastic.util
|
||||
import meshtastic.serial_interface
|
||||
import meshtastic.tcp_interface
|
||||
@@ -60,7 +61,7 @@ except ImportError as e:
|
||||
have_powermon = False
|
||||
powermon_exception = e
|
||||
meter = None
|
||||
from meshtastic.protobuf import channel_pb2, config_pb2, portnums_pb2, mesh_pb2
|
||||
from meshtastic.protobuf import admin_pb2, channel_pb2, config_pb2, portnums_pb2, mesh_pb2
|
||||
from meshtastic.version import get_active_version
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -85,12 +86,17 @@ def onReceive(packet, interface) -> None:
|
||||
if d is not None and args and args.reply:
|
||||
msg = d.get("text")
|
||||
if msg:
|
||||
rxSnr = packet["rxSnr"]
|
||||
hopLimit = packet["hopLimit"]
|
||||
print(f"message: {msg}")
|
||||
reply = f"got msg '{msg}' with rxSnr: {rxSnr} and hopLimit: {hopLimit}"
|
||||
print("Sending reply: ", reply)
|
||||
interface.sendText(reply)
|
||||
rxChannel = packet.get("channel", 0)
|
||||
targetChannel = int(args.ch_index or 0)
|
||||
if rxChannel == targetChannel:
|
||||
rxSnr = packet["rxSnr"]
|
||||
hopLimit = packet["hopLimit"]
|
||||
print(f"message: {msg}")
|
||||
reply = f"got msg '{msg}' with rxSnr: {rxSnr} and hopLimit: {hopLimit}"
|
||||
print(f"Received channel {rxChannel}. Sending reply: {reply}")
|
||||
interface.sendText(reply,channelIndex=rxChannel)
|
||||
else:
|
||||
print(f"Ignored message on channel {rxChannel} (waiting for channel {targetChannel})")
|
||||
|
||||
except Exception as ex:
|
||||
print(f"Warning: Error processing received packet: {ex}.")
|
||||
@@ -158,11 +164,11 @@ def getPref(node, comp_name) -> bool:
|
||||
config_values = getattr(config, config_type.name)
|
||||
if not wholeField:
|
||||
pref_value = getattr(config_values, pref.name)
|
||||
repeated = pref.label == pref.LABEL_REPEATED
|
||||
repeated = _is_repeated_field(pref)
|
||||
_printSetting(config_type, uni_name, pref_value, repeated)
|
||||
else:
|
||||
for field in config_values.ListFields():
|
||||
repeated = field[0].label == field[0].LABEL_REPEATED
|
||||
repeated = _is_repeated_field(field[0])
|
||||
_printSetting(config_type, field[0].name, field[1], repeated)
|
||||
else:
|
||||
# Always show whole field for remote node
|
||||
@@ -253,7 +259,7 @@ def setPref(config, comp_name, raw_val) -> bool:
|
||||
return False
|
||||
|
||||
# repeating fields need to be handled with append, not setattr
|
||||
if pref.label != pref.LABEL_REPEATED:
|
||||
if not _is_repeated_field(pref):
|
||||
try:
|
||||
if config_type.message_type is not None:
|
||||
config_values = getattr(config_part, config_type.name)
|
||||
@@ -452,6 +458,41 @@ def onConnected(interface):
|
||||
waitForAckNak = True
|
||||
interface.getNode(args.dest, False, **getNode_kwargs).rebootOTA()
|
||||
|
||||
if args.ota_update:
|
||||
closeNow = True
|
||||
waitForAckNak = True
|
||||
|
||||
if not isinstance(interface, meshtastic.tcp_interface.TCPInterface):
|
||||
meshtastic.util.our_exit(
|
||||
"Error: OTA update currently requires a TCP connection to the node (use --host)."
|
||||
)
|
||||
|
||||
ota = meshtastic.ota.ESP32WiFiOTA(args.ota_update, interface.hostname)
|
||||
|
||||
print(f"Triggering OTA update on {interface.hostname}...")
|
||||
interface.getNode(args.dest, False, **getNode_kwargs).startOTA(
|
||||
ota_mode=admin_pb2.OTAMode.OTA_WIFI,
|
||||
ota_file_hash=ota.hash_bytes()
|
||||
)
|
||||
|
||||
print("Waiting for device to reboot into OTA mode...")
|
||||
time.sleep(5)
|
||||
|
||||
retries = 5
|
||||
while retries > 0:
|
||||
try:
|
||||
ota.update()
|
||||
break
|
||||
|
||||
except Exception as e:
|
||||
retries -= 1
|
||||
if retries == 0:
|
||||
meshtastic.util.our_exit(f"\nOTA update failed: {e}")
|
||||
|
||||
time.sleep(2)
|
||||
|
||||
print("\nOTA update completed successfully!")
|
||||
|
||||
if args.enter_dfu:
|
||||
closeNow = True
|
||||
waitForAckNak = True
|
||||
@@ -1131,6 +1172,14 @@ def subscribe() -> None:
|
||||
|
||||
# pub.subscribe(onNode, "meshtastic.node")
|
||||
|
||||
def _is_repeated_field(field_desc) -> bool:
|
||||
"""Return True if the protobuf field is repeated.
|
||||
Protobuf 6.31.0 and later use an is_repeated property, while older versions compare against the label field.
|
||||
"""
|
||||
if hasattr(field_desc, "is_repeated"):
|
||||
return bool(field_desc.is_repeated)
|
||||
return field_desc.label == field_desc.LABEL_REPEATED
|
||||
|
||||
def set_missing_flags_false(config_dict: dict, true_defaults: set[tuple[str, str]]) -> None:
|
||||
"""Ensure that missing default=True keys are present in the config_dict and set to False."""
|
||||
for path in true_defaults:
|
||||
@@ -1883,7 +1932,10 @@ def addRemoteActionArgs(parser: argparse.ArgumentParser) -> argparse.ArgumentPar
|
||||
)
|
||||
|
||||
group.add_argument(
|
||||
"--reply", help="Reply to received messages", action="store_true"
|
||||
"--reply",
|
||||
help="Reply to received messages on the channel they were received. "
|
||||
"If '--ch-index' is set, only messages on that channel are replied to.",
|
||||
action="store_true",
|
||||
)
|
||||
|
||||
return parser
|
||||
@@ -1904,10 +1956,18 @@ def addRemoteAdminArgs(parser: argparse.ArgumentParser) -> argparse.ArgumentPars
|
||||
|
||||
group.add_argument(
|
||||
"--reboot-ota",
|
||||
help="Tell the destination node to reboot into factory firmware (ESP32)",
|
||||
help="Tell the destination node to reboot into factory firmware (ESP32, firmware version <2.7.18)",
|
||||
action="store_true",
|
||||
)
|
||||
|
||||
group.add_argument(
|
||||
"--ota-update",
|
||||
help="Perform an OTA update on the local node (ESP32, firmware version >=2.7.18, WiFi/TCP only for now). "
|
||||
"Specify the path to the firmware file.",
|
||||
metavar="FIRMWARE_FILE",
|
||||
action="store",
|
||||
)
|
||||
|
||||
group.add_argument(
|
||||
"--enter-dfu",
|
||||
help="Tell the destination node to enter DFU mode (NRF52)",
|
||||
|
||||
@@ -419,6 +419,7 @@ class MeshInterface: # pylint: disable=R0902
|
||||
channelIndex: int = 0,
|
||||
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
|
||||
will also be shown on the device.
|
||||
@@ -436,6 +437,7 @@ class MeshInterface: # pylint: disable=R0902
|
||||
portNum -- the application portnum (similar to IP port numbers)
|
||||
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
|
||||
and can be used to track future message acks/naks.
|
||||
@@ -449,7 +451,8 @@ class MeshInterface: # pylint: disable=R0902
|
||||
wantResponse=wantResponse,
|
||||
onResponse=onResponse,
|
||||
channelIndex=channelIndex,
|
||||
replyId=replyId
|
||||
replyId=replyId,
|
||||
hopLimit=hopLimit,
|
||||
)
|
||||
|
||||
|
||||
@@ -459,6 +462,7 @@ class MeshInterface: # pylint: disable=R0902
|
||||
destinationId: Union[int, str] = BROADCAST_ADDR,
|
||||
onResponse: Optional[Callable[[dict], Any]] = None,
|
||||
channelIndex: int = 0,
|
||||
hopLimit: Optional[int]=None,
|
||||
):
|
||||
"""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
|
||||
@@ -470,6 +474,7 @@ class MeshInterface: # pylint: disable=R0902
|
||||
Keyword Arguments:
|
||||
destinationId {nodeId or nodeNum} -- where to send this
|
||||
message (default: {BROADCAST_ADDR})
|
||||
hopLimit {int} -- hop limit to use
|
||||
|
||||
Returns the sent packet. The id field will be populated in this packet
|
||||
and can be used to track future message acks/naks.
|
||||
@@ -483,7 +488,8 @@ class MeshInterface: # pylint: disable=R0902
|
||||
wantResponse=False,
|
||||
onResponse=onResponse,
|
||||
channelIndex=channelIndex,
|
||||
priority=mesh_pb2.MeshPacket.Priority.ALERT
|
||||
priority=mesh_pb2.MeshPacket.Priority.ALERT,
|
||||
hopLimit=hopLimit,
|
||||
)
|
||||
|
||||
def sendMqttClientProxyMessage(self, topic: str, data: bytes):
|
||||
@@ -585,6 +591,7 @@ class MeshInterface: # pylint: disable=R0902
|
||||
wantAck: bool = False,
|
||||
wantResponse: bool = False,
|
||||
channelIndex: int = 0,
|
||||
hopLimit: Optional[int]=None,
|
||||
):
|
||||
"""
|
||||
Send a position packet to some other node (normally a broadcast)
|
||||
@@ -621,6 +628,7 @@ class MeshInterface: # pylint: disable=R0902
|
||||
wantResponse=wantResponse,
|
||||
onResponse=onResponse,
|
||||
channelIndex=channelIndex,
|
||||
hopLimit=hopLimit,
|
||||
)
|
||||
if wantResponse:
|
||||
self.waitForPosition()
|
||||
@@ -673,7 +681,8 @@ class MeshInterface: # pylint: disable=R0902
|
||||
hopLimit=hopLimit,
|
||||
)
|
||||
# extend timeout based on number of nodes, limit by configured hopLimit
|
||||
waitFactor = min(len(self.nodes) - 1 if self.nodes else 0, hopLimit)
|
||||
nodes_based_factor = (len(self.nodes) - 1) if self.nodes else (hopLimit + 1)
|
||||
waitFactor = max(1, min(nodes_based_factor, hopLimit + 1))
|
||||
self.waitForTraceRoute(waitFactor)
|
||||
|
||||
def onResponseTraceRoute(self, p: dict):
|
||||
@@ -726,7 +735,8 @@ class MeshInterface: # pylint: disable=R0902
|
||||
destinationId: Union[int, str] = BROADCAST_ADDR,
|
||||
wantResponse: bool = False,
|
||||
channelIndex: int = 0,
|
||||
telemetryType: str = "device_metrics"
|
||||
telemetryType: str = "device_metrics",
|
||||
hopLimit: Optional[int]=None,
|
||||
):
|
||||
"""Send telemetry and optionally ask for a response"""
|
||||
r = telemetry_pb2.Telemetry()
|
||||
@@ -773,6 +783,7 @@ class MeshInterface: # pylint: disable=R0902
|
||||
wantResponse=wantResponse,
|
||||
onResponse=onResponse,
|
||||
channelIndex=channelIndex,
|
||||
hopLimit=hopLimit,
|
||||
)
|
||||
if wantResponse:
|
||||
self.waitForTelemetry()
|
||||
@@ -842,6 +853,7 @@ class MeshInterface: # pylint: disable=R0902
|
||||
wantAck: bool = True,
|
||||
wantResponse: bool = False,
|
||||
channelIndex: int = 0,
|
||||
hopLimit: Optional[int]=None,
|
||||
): # pylint: disable=R0913
|
||||
"""
|
||||
Send a waypoint packet to some other node (normally a broadcast)
|
||||
@@ -882,6 +894,7 @@ class MeshInterface: # pylint: disable=R0902
|
||||
wantResponse=wantResponse,
|
||||
onResponse=onResponse,
|
||||
channelIndex=channelIndex,
|
||||
hopLimit=hopLimit,
|
||||
)
|
||||
if wantResponse:
|
||||
self.waitForWaypoint()
|
||||
@@ -894,6 +907,7 @@ class MeshInterface: # pylint: disable=R0902
|
||||
wantAck: bool = True,
|
||||
wantResponse: bool = False,
|
||||
channelIndex: int = 0,
|
||||
hopLimit: Optional[int]=None,
|
||||
):
|
||||
"""
|
||||
Send a waypoint deletion packet to some other node (normally a broadcast)
|
||||
@@ -920,6 +934,7 @@ class MeshInterface: # pylint: disable=R0902
|
||||
wantResponse=wantResponse,
|
||||
onResponse=onResponse,
|
||||
channelIndex=channelIndex,
|
||||
hopLimit=hopLimit,
|
||||
)
|
||||
if wantResponse:
|
||||
self.waitForWaypoint()
|
||||
@@ -1455,6 +1470,10 @@ class MeshInterface: # pylint: disable=R0902
|
||||
self.localNode.moduleConfig.paxcounter.CopyFrom(
|
||||
fromRadio.moduleConfig.paxcounter
|
||||
)
|
||||
elif fromRadio.moduleConfig.HasField("traffic_management"):
|
||||
self.localNode.moduleConfig.traffic_management.CopyFrom(
|
||||
fromRadio.moduleConfig.traffic_management
|
||||
)
|
||||
|
||||
else:
|
||||
logger.debug("Unexpected FromRadio payload")
|
||||
|
||||
@@ -170,11 +170,10 @@ class Node:
|
||||
p.get_config_request = configType
|
||||
|
||||
else:
|
||||
msgIndex = configType.index
|
||||
if configType.containing_type.name == "LocalConfig":
|
||||
p.get_config_request = msgIndex
|
||||
p.get_config_request = admin_pb2.AdminMessage.ConfigType.Value(configType.name.upper() + "_CONFIG")
|
||||
else:
|
||||
p.get_module_config_request = msgIndex
|
||||
p.get_module_config_request = configType.index
|
||||
|
||||
self._sendAdmin(p, wantResponse=True, onResponse=onResponse)
|
||||
if onResponse:
|
||||
@@ -245,6 +244,8 @@ class Node:
|
||||
p.set_module_config.ambient_lighting.CopyFrom(self.moduleConfig.ambient_lighting)
|
||||
elif config_name == "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:
|
||||
our_exit(f"Error: No valid config with name {config_name}")
|
||||
|
||||
@@ -654,7 +655,7 @@ class Node:
|
||||
return self._sendAdmin(p, onResponse=onResponse)
|
||||
|
||||
def rebootOTA(self, secs: int = 10):
|
||||
"""Tell the node to reboot into factory firmware."""
|
||||
"""Tell the node to reboot into factory firmware (firmware < 2.7.18)."""
|
||||
self.ensureSessionKey()
|
||||
p = admin_pb2.AdminMessage()
|
||||
p.reboot_ota_seconds = secs
|
||||
@@ -667,6 +668,22 @@ class Node:
|
||||
onResponse = self.onAckNak
|
||||
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):
|
||||
"""Tell the node to enter DFU mode (NRF52)."""
|
||||
self.ensureSessionKey()
|
||||
@@ -711,10 +728,10 @@ class Node:
|
||||
self.ensureSessionKey()
|
||||
p = admin_pb2.AdminMessage()
|
||||
if full:
|
||||
p.factory_reset_device = True
|
||||
p.factory_reset_device = 1
|
||||
logger.info(f"Telling node to factory reset (full device reset)")
|
||||
else:
|
||||
p.factory_reset_config = True
|
||||
p.factory_reset_config = 1
|
||||
logger.info(f"Telling node to factory reset (config reset)")
|
||||
|
||||
# If sending to a remote node, wait for ACK/NAK
|
||||
|
||||
128
meshtastic/ota.py
Normal file
128
meshtastic/ota.py
Normal file
@@ -0,0 +1,128 @@
|
||||
"""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
|
||||
93
meshtastic/protobuf/admin_pb2.py
generated
93
meshtastic/protobuf/admin_pb2.py
generated
File diff suppressed because one or more lines are too long
331
meshtastic/protobuf/admin_pb2.pyi
generated
331
meshtastic/protobuf/admin_pb2.pyi
generated
@@ -228,6 +228,14 @@ class AdminMessage(google.protobuf.message.Message):
|
||||
"""
|
||||
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):
|
||||
"""
|
||||
@@ -290,6 +298,14 @@ class AdminMessage(google.protobuf.message.Message):
|
||||
"""
|
||||
TODO: REPLACE
|
||||
"""
|
||||
TRAFFICMANAGEMENT_CONFIG: AdminMessage.ModuleConfigType.ValueType # 14
|
||||
"""
|
||||
Traffic management module config
|
||||
"""
|
||||
TAK_CONFIG: AdminMessage.ModuleConfigType.ValueType # 15
|
||||
"""
|
||||
TAK module config
|
||||
"""
|
||||
|
||||
class _BackupLocation:
|
||||
ValueType = typing.NewType("ValueType", builtins.int)
|
||||
@@ -439,6 +455,8 @@ class AdminMessage(google.protobuf.message.Message):
|
||||
FACTORY_RESET_CONFIG_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
|
||||
"""
|
||||
The node generates this key and sends it with any get_x_response packets.
|
||||
@@ -720,6 +738,25 @@ class AdminMessage(google.protobuf.message.Message):
|
||||
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__(
|
||||
self,
|
||||
*,
|
||||
@@ -780,13 +817,77 @@ class AdminMessage(google.protobuf.message.Message):
|
||||
factory_reset_config: builtins.int = ...,
|
||||
nodedb_reset: builtins.bool = ...,
|
||||
ota_request: global___AdminMessage.OTAEvent | None = ...,
|
||||
sensor_config: global___SensorConfig | None = ...,
|
||||
lockdown_auth: global___LockdownAuth | 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", "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", "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 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", "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", "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 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"] | 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 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 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: ...
|
||||
|
||||
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
|
||||
class HamParameters(google.protobuf.message.Message):
|
||||
"""
|
||||
@@ -977,3 +1078,227 @@ class KeyVerificationAdmin(google.protobuf.message.Message):
|
||||
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
|
||||
|
||||
9
meshtastic/protobuf/apponly_pb2.py
generated
9
meshtastic/protobuf/apponly_pb2.py
generated
@@ -13,9 +13,10 @@ _sym_db = _symbol_database.Default()
|
||||
|
||||
from meshtastic.protobuf import channel_pb2 as meshtastic_dot_protobuf_dot_channel__pb2
|
||||
from meshtastic.protobuf import config_pb2 as meshtastic_dot_protobuf_dot_config__pb2
|
||||
from meshtastic.protobuf import 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\"\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.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\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')
|
||||
|
||||
_globals = globals()
|
||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||
@@ -23,6 +24,8 @@ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.apponly
|
||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
DESCRIPTOR._options = None
|
||||
DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\rAppOnlyProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||
_globals['_CHANNELSET']._serialized_start=128
|
||||
_globals['_CHANNELSET']._serialized_end=257
|
||||
_CHANNELSET.fields_by_name['settings']._options = None
|
||||
_CHANNELSET.fields_by_name['settings']._serialized_options = b'\222?\002\020\010'
|
||||
_globals['_CHANNELSET']._serialized_start=162
|
||||
_globals['_CHANNELSET']._serialized_end=298
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
|
||||
247
meshtastic/protobuf/atak_pb2.py
generated
247
meshtastic/protobuf/atak_pb2.py
generated
File diff suppressed because one or more lines are too long
3380
meshtastic/protobuf/atak_pb2.pyi
generated
3380
meshtastic/protobuf/atak_pb2.pyi
generated
File diff suppressed because it is too large
Load Diff
9
meshtastic/protobuf/cannedmessages_pb2.py
generated
9
meshtastic/protobuf/cannedmessages_pb2.py
generated
@@ -11,9 +11,10 @@ from google.protobuf.internal import builder as _builder
|
||||
_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\"-\n\x19\x43\x61nnedMessageModuleConfig\x12\x10\n\x08messages\x18\x01 \x01(\tBo\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\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')
|
||||
|
||||
_globals = globals()
|
||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||
@@ -21,6 +22,8 @@ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.cannedm
|
||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
DESCRIPTOR._options = None
|
||||
DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\031CannedMessageConfigProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||
_globals['_CANNEDMESSAGEMODULECONFIG']._serialized_start=65
|
||||
_globals['_CANNEDMESSAGEMODULECONFIG']._serialized_end=110
|
||||
_CANNEDMESSAGEMODULECONFIG.fields_by_name['messages']._options = None
|
||||
_CANNEDMESSAGEMODULECONFIG.fields_by_name['messages']._serialized_options = b'\222?\003\010\311\001'
|
||||
_globals['_CANNEDMESSAGEMODULECONFIG']._serialized_start=99
|
||||
_globals['_CANNEDMESSAGEMODULECONFIG']._serialized_end=152
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
|
||||
25
meshtastic/protobuf/channel_pb2.py
generated
25
meshtastic/protobuf/channel_pb2.py
generated
@@ -11,9 +11,10 @@ from google.protobuf.internal import builder as _builder
|
||||
_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\"\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\">\n\x0eModuleSettings\x12\x1a\n\x12position_precision\x18\x01 \x01(\r\x12\x10\n\x08is_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\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\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')
|
||||
|
||||
_globals = globals()
|
||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||
@@ -23,12 +24,18 @@ if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\rChannelProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||
_CHANNELSETTINGS.fields_by_name['channel_num']._options = None
|
||||
_CHANNELSETTINGS.fields_by_name['channel_num']._serialized_options = b'\030\001'
|
||||
_globals['_CHANNELSETTINGS']._serialized_start=59
|
||||
_globals['_CHANNELSETTINGS']._serialized_end=252
|
||||
_globals['_MODULESETTINGS']._serialized_start=254
|
||||
_globals['_MODULESETTINGS']._serialized_end=316
|
||||
_globals['_CHANNEL']._serialized_start=319
|
||||
_globals['_CHANNEL']._serialized_end=498
|
||||
_globals['_CHANNEL_ROLE']._serialized_start=450
|
||||
_globals['_CHANNEL_ROLE']._serialized_end=498
|
||||
_CHANNELSETTINGS.fields_by_name['psk']._options = None
|
||||
_CHANNELSETTINGS.fields_by_name['psk']._serialized_options = b'\222?\002\010 '
|
||||
_CHANNELSETTINGS.fields_by_name['name']._options = None
|
||||
_CHANNELSETTINGS.fields_by_name['name']._serialized_options = b'\222?\002\010\014'
|
||||
_CHANNEL.fields_by_name['index']._options = None
|
||||
_CHANNEL.fields_by_name['index']._serialized_options = b'\222?\0028\010'
|
||||
_globals['_CHANNELSETTINGS']._serialized_start=93
|
||||
_globals['_CHANNELSETTINGS']._serialized_end=300
|
||||
_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)
|
||||
|
||||
15
meshtastic/protobuf/clientonly_pb2.py
generated
15
meshtastic/protobuf/clientonly_pb2.py
generated
@@ -13,9 +13,10 @@ _sym_db = _symbol_database.Default()
|
||||
|
||||
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 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\"\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_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\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')
|
||||
|
||||
_globals = globals()
|
||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||
@@ -23,6 +24,14 @@ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.cliento
|
||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
DESCRIPTOR._options = None
|
||||
DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\020ClientOnlyProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||
_globals['_DEVICEPROFILE']._serialized_start=131
|
||||
_globals['_DEVICEPROFILE']._serialized_end=583
|
||||
_DEVICEPROFILE.fields_by_name['long_name']._options = None
|
||||
_DEVICEPROFILE.fields_by_name['long_name']._serialized_options = b'\222?\002\010('
|
||||
_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)
|
||||
|
||||
139
meshtastic/protobuf/config_pb2.py
generated
139
meshtastic/protobuf/config_pb2.py
generated
File diff suppressed because one or more lines are too long
163
meshtastic/protobuf/config_pb2.pyi
generated
163
meshtastic/protobuf/config_pb2.pyi
generated
@@ -1010,6 +1010,10 @@ class Config(google.protobuf.message.Message):
|
||||
"""
|
||||
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):
|
||||
"""
|
||||
@@ -1036,6 +1040,10 @@ class Config(google.protobuf.message.Message):
|
||||
"""
|
||||
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:
|
||||
ValueType = typing.NewType("ValueType", builtins.int)
|
||||
@@ -1164,6 +1172,7 @@ class Config(google.protobuf.message.Message):
|
||||
COMPASS_ORIENTATION_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
|
||||
"""
|
||||
Number of seconds the screen stays on after pressing the user button or receiving a message
|
||||
@@ -1222,6 +1231,10 @@ class Config(google.protobuf.message.Message):
|
||||
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__(
|
||||
self,
|
||||
*,
|
||||
@@ -1238,8 +1251,9 @@ class Config(google.protobuf.message.Message):
|
||||
compass_orientation: global___Config.DisplayConfig.CompassOrientation.ValueType = ...,
|
||||
use_12h_clock: builtins.bool = ...,
|
||||
use_long_node_name: builtins.bool = ...,
|
||||
enable_message_bubbles: builtins.bool = ...,
|
||||
) -> 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", "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", "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: ...
|
||||
|
||||
@typing.final
|
||||
class LoRaConfig(google.protobuf.message.Message):
|
||||
@@ -1363,6 +1377,31 @@ class Config(google.protobuf.message.Message):
|
||||
"""
|
||||
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): ...
|
||||
UNSET: Config.LoRaConfig.RegionCode.ValueType # 0
|
||||
@@ -1473,6 +1512,31 @@ class Config(google.protobuf.message.Message):
|
||||
"""
|
||||
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:
|
||||
ValueType = typing.NewType("ValueType", builtins.int)
|
||||
@@ -1525,6 +1589,31 @@ class Config(google.protobuf.message.Message):
|
||||
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):
|
||||
"""
|
||||
@@ -1577,6 +1666,64 @@ class Config(google.protobuf.message.Message):
|
||||
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
|
||||
MODEM_PRESET_FIELD_NUMBER: builtins.int
|
||||
@@ -1596,6 +1743,8 @@ class Config(google.protobuf.message.Message):
|
||||
IGNORE_INCOMING_FIELD_NUMBER: builtins.int
|
||||
IGNORE_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
|
||||
"""
|
||||
When enabled, the `modem_preset` fields will be adhered to, else the `bandwidth`/`spread_factor`/`coding_rate`
|
||||
@@ -1693,6 +1842,14 @@ class Config(google.protobuf.message.Message):
|
||||
"""
|
||||
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
|
||||
def ignore_incoming(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.int]:
|
||||
"""
|
||||
@@ -1722,8 +1879,10 @@ class Config(google.protobuf.message.Message):
|
||||
ignore_incoming: collections.abc.Iterable[builtins.int] | None = ...,
|
||||
ignore_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: ...
|
||||
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: ...
|
||||
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: ...
|
||||
|
||||
@typing.final
|
||||
class BluetoothConfig(google.protobuf.message.Message):
|
||||
|
||||
29
meshtastic/protobuf/connection_status_pb2.py
generated
29
meshtastic/protobuf/connection_status_pb2.py
generated
@@ -11,9 +11,10 @@ from google.protobuf.internal import builder as _builder
|
||||
_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\"\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\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\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')
|
||||
|
||||
_globals = globals()
|
||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||
@@ -21,16 +22,18 @@ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.connect
|
||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
DESCRIPTOR._options = None
|
||||
DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\020ConnStatusProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||
_globals['_DEVICECONNECTIONSTATUS']._serialized_start=69
|
||||
_globals['_DEVICECONNECTIONSTATUS']._serialized_end=410
|
||||
_globals['_WIFICONNECTIONSTATUS']._serialized_start=412
|
||||
_globals['_WIFICONNECTIONSTATUS']._serialized_end=524
|
||||
_globals['_ETHERNETCONNECTIONSTATUS']._serialized_start=526
|
||||
_globals['_ETHERNETCONNECTIONSTATUS']._serialized_end=614
|
||||
_globals['_NETWORKCONNECTIONSTATUS']._serialized_start=616
|
||||
_globals['_NETWORKCONNECTIONSTATUS']._serialized_end=739
|
||||
_globals['_BLUETOOTHCONNECTIONSTATUS']._serialized_start=741
|
||||
_globals['_BLUETOOTHCONNECTIONSTATUS']._serialized_end=817
|
||||
_globals['_SERIALCONNECTIONSTATUS']._serialized_start=819
|
||||
_globals['_SERIALCONNECTIONSTATUS']._serialized_end=879
|
||||
_WIFICONNECTIONSTATUS.fields_by_name['ssid']._options = None
|
||||
_WIFICONNECTIONSTATUS.fields_by_name['ssid']._serialized_options = b'\222?\002\010!'
|
||||
_globals['_DEVICECONNECTIONSTATUS']._serialized_start=103
|
||||
_globals['_DEVICECONNECTIONSTATUS']._serialized_end=444
|
||||
_globals['_WIFICONNECTIONSTATUS']._serialized_start=446
|
||||
_globals['_WIFICONNECTIONSTATUS']._serialized_end=565
|
||||
_globals['_ETHERNETCONNECTIONSTATUS']._serialized_start=567
|
||||
_globals['_ETHERNETCONNECTIONSTATUS']._serialized_end=655
|
||||
_globals['_NETWORKCONNECTIONSTATUS']._serialized_start=657
|
||||
_globals['_NETWORKCONNECTIONSTATUS']._serialized_end=780
|
||||
_globals['_BLUETOOTHCONNECTIONSTATUS']._serialized_start=782
|
||||
_globals['_BLUETOOTHCONNECTIONSTATUS']._serialized_end=858
|
||||
_globals['_SERIALCONNECTIONSTATUS']._serialized_start=860
|
||||
_globals['_SERIALCONNECTIONSTATUS']._serialized_end=920
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
|
||||
63
meshtastic/protobuf/device_ui_pb2.py
generated
63
meshtastic/protobuf/device_ui_pb2.py
generated
@@ -11,9 +11,10 @@ from google.protobuf.internal import builder as _builder
|
||||
_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\"\xff\x05\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\x12\x36\n\x0c\x63ompass_mode\x18\x10 \x01(\x0e\x32 .meshtastic.protobuf.CompassMode\x12\x18\n\x10screen_rgb_color\x18\x11 \x01(\r\x12\x1b\n\x13is_clockface_analog\x18\x12 \x01(\x08\x12K\n\ngps_format\x18\x13 \x01(\x0e\x32\x37.meshtastic.protobuf.DeviceUIConfig.GpsCoordinateFormat\"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\"\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\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\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')
|
||||
|
||||
_globals = globals()
|
||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||
@@ -21,22 +22,46 @@ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.device_
|
||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
DESCRIPTOR._options = None
|
||||
DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\016DeviceUIProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||
_globals['_COMPASSMODE']._serialized_start=1278
|
||||
_globals['_COMPASSMODE']._serialized_end=1340
|
||||
_globals['_THEME']._serialized_start=1342
|
||||
_globals['_THEME']._serialized_end=1379
|
||||
_globals['_LANGUAGE']._serialized_start=1382
|
||||
_globals['_LANGUAGE']._serialized_end=1702
|
||||
_globals['_DEVICEUICONFIG']._serialized_start=61
|
||||
_globals['_DEVICEUICONFIG']._serialized_end=828
|
||||
_globals['_DEVICEUICONFIG_GPSCOORDINATEFORMAT']._serialized_start=742
|
||||
_globals['_DEVICEUICONFIG_GPSCOORDINATEFORMAT']._serialized_end=828
|
||||
_globals['_NODEFILTER']._serialized_start=831
|
||||
_globals['_NODEFILTER']._serialized_end=998
|
||||
_globals['_NODEHIGHLIGHT']._serialized_start=1000
|
||||
_globals['_NODEHIGHLIGHT']._serialized_end=1126
|
||||
_globals['_GEOPOINT']._serialized_start=1128
|
||||
_globals['_GEOPOINT']._serialized_end=1189
|
||||
_globals['_MAP']._serialized_start=1191
|
||||
_globals['_MAP']._serialized_end=1276
|
||||
_DEVICEUICONFIG.fields_by_name['screen_brightness']._options = None
|
||||
_DEVICEUICONFIG.fields_by_name['screen_brightness']._serialized_options = b'\222?\0028\010'
|
||||
_DEVICEUICONFIG.fields_by_name['screen_timeout']._options = None
|
||||
_DEVICEUICONFIG.fields_by_name['screen_timeout']._serialized_options = b'\222?\0028\020'
|
||||
_DEVICEUICONFIG.fields_by_name['ring_tone_id']._options = None
|
||||
_DEVICEUICONFIG.fields_by_name['ring_tone_id']._serialized_options = b'\222?\0028\010'
|
||||
_DEVICEUICONFIG.fields_by_name['calibration_data']._options = None
|
||||
_DEVICEUICONFIG.fields_by_name['calibration_data']._serialized_options = b'\222?\002\010\020'
|
||||
_DEVICEUICONFIG.fields_by_name['compass_mode']._options = None
|
||||
_DEVICEUICONFIG.fields_by_name['compass_mode']._serialized_options = b'\222?\0028\010'
|
||||
_DEVICEUICONFIG.fields_by_name['gps_format']._options = None
|
||||
_DEVICEUICONFIG.fields_by_name['gps_format']._serialized_options = b'\222?\0028\010'
|
||||
_NODEFILTER.fields_by_name['hops_away']._options = None
|
||||
_NODEFILTER.fields_by_name['hops_away']._serialized_options = b'\222?\0028\010'
|
||||
_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)
|
||||
|
||||
44
meshtastic/protobuf/deviceonly_pb2.py
generated
44
meshtastic/protobuf/deviceonly_pb2.py
generated
@@ -19,7 +19,7 @@ from meshtastic.protobuf import telemetry_pb2 as meshtastic_dot_protobuf_dot_tel
|
||||
from meshtastic.protobuf import nanopb_pb2 as meshtastic_dot_protobuf_dot_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\"\x94\x02\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\x12\x1c\n\x0fis_unmessagable\x18\t \x01(\x08H\x00\x88\x01\x01\x42\x12\n\x10_is_unmessagable\"\xf0\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(\r\x12\x10\n\x08\x62itfield\x18\r \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.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 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')
|
||||
|
||||
_globals = globals()
|
||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||
@@ -28,25 +28,43 @@ if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
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>'
|
||||
_USERLITE.fields_by_name['macaddr']._options = None
|
||||
_USERLITE.fields_by_name['macaddr']._serialized_options = b'\030\001'
|
||||
_USERLITE.fields_by_name['macaddr']._serialized_options = b'\030\001\222?\004\010\006x\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']._serialized_options = b'\030\001'
|
||||
_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['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']._serialized_options = b'\222?\'\222\001$std::vector<meshtastic_NodeInfoLite>'
|
||||
_CHANNELFILE.fields_by_name['channels']._options = None
|
||||
_CHANNELFILE.fields_by_name['channels']._serialized_options = b'\222?\002\020\010'
|
||||
_globals['_POSITIONLITE']._serialized_start=271
|
||||
_globals['_POSITIONLITE']._serialized_end=424
|
||||
_globals['_USERLITE']._serialized_start=427
|
||||
_globals['_USERLITE']._serialized_end=703
|
||||
_globals['_NODEINFOLITE']._serialized_start=706
|
||||
_globals['_NODEINFOLITE']._serialized_end=1074
|
||||
_globals['_DEVICESTATE']._serialized_start=1077
|
||||
_globals['_DEVICESTATE']._serialized_end=1494
|
||||
_globals['_NODEDATABASE']._serialized_start=1496
|
||||
_globals['_NODEDATABASE']._serialized_end=1621
|
||||
_globals['_CHANNELFILE']._serialized_start=1623
|
||||
_globals['_CHANNELFILE']._serialized_end=1701
|
||||
_globals['_BACKUPPREFERENCES']._serialized_start=1704
|
||||
_globals['_BACKUPPREFERENCES']._serialized_end=1966
|
||||
_globals['_USERLITE']._serialized_end=731
|
||||
_globals['_NODEINFOLITE']._serialized_start=734
|
||||
_globals['_NODEINFOLITE']._serialized_end=1123
|
||||
_globals['_DEVICESTATE']._serialized_start=1126
|
||||
_globals['_DEVICESTATE']._serialized_end=1557
|
||||
_globals['_NODEDATABASE']._serialized_start=1559
|
||||
_globals['_NODEDATABASE']._serialized_end=1684
|
||||
_globals['_CHANNELFILE']._serialized_start=1686
|
||||
_globals['_CHANNELFILE']._serialized_end=1771
|
||||
_globals['_BACKUPPREFERENCES']._serialized_start=1774
|
||||
_globals['_BACKUPPREFERENCES']._serialized_end=2036
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
|
||||
17
meshtastic/protobuf/interdevice_pb2.py
generated
17
meshtastic/protobuf/interdevice_pb2.py
generated
@@ -11,9 +11,10 @@ from google.protobuf.internal import builder as _builder
|
||||
_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\"s\n\nSensorData\x12.\n\x04type\x18\x01 \x01(\x0e\x32 .meshtastic.protobuf.MessageType\x12\x15\n\x0b\x66loat_value\x18\x02 \x01(\x02H\x00\x12\x16\n\x0cuint32_value\x18\x03 \x01(\rH\x00\x42\x06\n\x04\x64\x61ta\"_\n\x12InterdeviceMessage\x12\x0e\n\x04nmea\x18\x01 \x01(\tH\x00\x12\x31\n\x06sensor\x18\x02 \x01(\x0b\x32\x1f.meshtastic.protobuf.SensorDataH\x00\x42\x06\n\x04\x64\x61ta*\xd5\x01\n\x0bMessageType\x12\x07\n\x03\x41\x43K\x10\x00\x12\x15\n\x10\x43OLLECT_INTERVAL\x10\xa0\x01\x12\x0c\n\x07\x42\x45\x45P_ON\x10\xa1\x01\x12\r\n\x08\x42\x45\x45P_OFF\x10\xa2\x01\x12\r\n\x08SHUTDOWN\x10\xa3\x01\x12\r\n\x08POWER_ON\x10\xa4\x01\x12\x0f\n\nSCD41_TEMP\x10\xb0\x01\x12\x13\n\x0eSCD41_HUMIDITY\x10\xb1\x01\x12\x0e\n\tSCD41_CO2\x10\xb2\x01\x12\x0f\n\nAHT20_TEMP\x10\xb3\x01\x12\x13\n\x0e\x41HT20_HUMIDITY\x10\xb4\x01\x12\x0f\n\nTVOC_INDEX\x10\xb5\x01\x42g\n\x14org.meshtastic.protoB\x11InterdeviceProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
|
||||
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)
|
||||
@@ -21,10 +22,12 @@ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.interde
|
||||
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'
|
||||
_globals['_MESSAGETYPE']._serialized_start=277
|
||||
_globals['_MESSAGETYPE']._serialized_end=490
|
||||
_globals['_SENSORDATA']._serialized_start=62
|
||||
_globals['_SENSORDATA']._serialized_end=177
|
||||
_globals['_INTERDEVICEMESSAGE']._serialized_start=179
|
||||
_globals['_INTERDEVICEMESSAGE']._serialized_end=274
|
||||
_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)
|
||||
|
||||
4
meshtastic/protobuf/localonly_pb2.py
generated
4
meshtastic/protobuf/localonly_pb2.py
generated
@@ -15,7 +15,7 @@ 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
|
||||
|
||||
|
||||
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\"\xbe\x08\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\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\"\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')
|
||||
|
||||
_globals = globals()
|
||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||
@@ -26,5 +26,5 @@ if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
_globals['_LOCALCONFIG']._serialized_start=136
|
||||
_globals['_LOCALCONFIG']._serialized_end=642
|
||||
_globals['_LOCALMODULECONFIG']._serialized_start=645
|
||||
_globals['_LOCALMODULECONFIG']._serialized_end=1731
|
||||
_globals['_LOCALMODULECONFIG']._serialized_end=1876
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
|
||||
20
meshtastic/protobuf/localonly_pb2.pyi
generated
20
meshtastic/protobuf/localonly_pb2.pyi
generated
@@ -120,6 +120,8 @@ class LocalModuleConfig(google.protobuf.message.Message):
|
||||
DETECTION_SENSOR_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: builtins.int
|
||||
"""
|
||||
@@ -211,6 +213,18 @@ class LocalModuleConfig(google.protobuf.message.Message):
|
||||
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__(
|
||||
self,
|
||||
*,
|
||||
@@ -228,9 +242,11 @@ class LocalModuleConfig(google.protobuf.message.Message):
|
||||
detection_sensor: meshtastic.protobuf.module_config_pb2.ModuleConfig.DetectionSensorConfig | 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 = ...,
|
||||
) -> 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", "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", "telemetry", b"telemetry", "version", b"version"]) -> 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 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: ...
|
||||
|
||||
global___LocalModuleConfig = LocalModuleConfig
|
||||
|
||||
311
meshtastic/protobuf/mesh_pb2.py
generated
311
meshtastic/protobuf/mesh_pb2.py
generated
File diff suppressed because one or more lines are too long
351
meshtastic/protobuf/mesh_pb2.pyi
generated
351
meshtastic/protobuf/mesh_pb2.pyi
generated
@@ -547,6 +547,53 @@ class _HardwareModelEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._
|
||||
"""
|
||||
LilyGo T5 S3 ePaper Pro (V1 and V2)
|
||||
"""
|
||||
TBEAM_BPF: _HardwareModel.ValueType # 124
|
||||
"""
|
||||
LilyGo T-Beam BPF (144-148Mhz)
|
||||
"""
|
||||
MINI_EPAPER_S3: _HardwareModel.ValueType # 125
|
||||
"""
|
||||
LilyGo T-Mini E-paper S3 Kit
|
||||
"""
|
||||
TDISPLAY_S3_PRO: _HardwareModel.ValueType # 126
|
||||
"""
|
||||
LilyGo T-Display S3 Pro LR1121
|
||||
"""
|
||||
HELTEC_MESH_NODE_T096: _HardwareModel.ValueType # 127
|
||||
"""
|
||||
Heltec Mesh Node T096 board features an nRF52840 CPU and a TFT screen.
|
||||
"""
|
||||
TRACKER_T1000_E_PRO: _HardwareModel.ValueType # 128
|
||||
"""
|
||||
Seeed studio T1000-E Pro tracker card. NRF52840 w/ LR2021 radio,
|
||||
GPS, button, buzzer, and sensors.
|
||||
"""
|
||||
THINKNODE_M7: _HardwareModel.ValueType # 129
|
||||
"""
|
||||
Elecrow ThinkNode M7, M8 and M9
|
||||
"""
|
||||
THINKNODE_M8: _HardwareModel.ValueType # 130
|
||||
THINKNODE_M9: _HardwareModel.ValueType # 131
|
||||
HELTEC_V4_R8: _HardwareModel.ValueType # 132
|
||||
"""
|
||||
The Heltec-V4-R8 uses an ESP32S3R8 chip, plus an SX1262.
|
||||
"""
|
||||
HELTEC_MESH_NODE_T1: _HardwareModel.ValueType # 133
|
||||
"""
|
||||
The HELTEC_MESH_NODE_T1 uses an NRF52840 chip, plus an SX1262.
|
||||
"""
|
||||
STATION_G3: _HardwareModel.ValueType # 134
|
||||
"""
|
||||
B&Q Consulting Station G3: TBD
|
||||
"""
|
||||
T_IMPULSE_PLUS: _HardwareModel.ValueType # 135
|
||||
"""
|
||||
Lilygo T-Impulse-Plus
|
||||
"""
|
||||
T_ECHO_CARD: _HardwareModel.ValueType # 136
|
||||
"""
|
||||
Lilygo T-Echo Card
|
||||
"""
|
||||
PRIVATE_HW: _HardwareModel.ValueType # 255
|
||||
"""
|
||||
------------------------------------------------------------------------------------------------------------------------------------------
|
||||
@@ -1077,6 +1124,53 @@ T5_S3_EPAPER_PRO: HardwareModel.ValueType # 123
|
||||
"""
|
||||
LilyGo T5 S3 ePaper Pro (V1 and V2)
|
||||
"""
|
||||
TBEAM_BPF: HardwareModel.ValueType # 124
|
||||
"""
|
||||
LilyGo T-Beam BPF (144-148Mhz)
|
||||
"""
|
||||
MINI_EPAPER_S3: HardwareModel.ValueType # 125
|
||||
"""
|
||||
LilyGo T-Mini E-paper S3 Kit
|
||||
"""
|
||||
TDISPLAY_S3_PRO: HardwareModel.ValueType # 126
|
||||
"""
|
||||
LilyGo T-Display S3 Pro LR1121
|
||||
"""
|
||||
HELTEC_MESH_NODE_T096: HardwareModel.ValueType # 127
|
||||
"""
|
||||
Heltec Mesh Node T096 board features an nRF52840 CPU and a TFT screen.
|
||||
"""
|
||||
TRACKER_T1000_E_PRO: HardwareModel.ValueType # 128
|
||||
"""
|
||||
Seeed studio T1000-E Pro tracker card. NRF52840 w/ LR2021 radio,
|
||||
GPS, button, buzzer, and sensors.
|
||||
"""
|
||||
THINKNODE_M7: HardwareModel.ValueType # 129
|
||||
"""
|
||||
Elecrow ThinkNode M7, M8 and M9
|
||||
"""
|
||||
THINKNODE_M8: HardwareModel.ValueType # 130
|
||||
THINKNODE_M9: HardwareModel.ValueType # 131
|
||||
HELTEC_V4_R8: HardwareModel.ValueType # 132
|
||||
"""
|
||||
The Heltec-V4-R8 uses an ESP32S3R8 chip, plus an SX1262.
|
||||
"""
|
||||
HELTEC_MESH_NODE_T1: HardwareModel.ValueType # 133
|
||||
"""
|
||||
The HELTEC_MESH_NODE_T1 uses an NRF52840 chip, plus an SX1262.
|
||||
"""
|
||||
STATION_G3: HardwareModel.ValueType # 134
|
||||
"""
|
||||
B&Q Consulting Station G3: TBD
|
||||
"""
|
||||
T_IMPULSE_PLUS: HardwareModel.ValueType # 135
|
||||
"""
|
||||
Lilygo T-Impulse-Plus
|
||||
"""
|
||||
T_ECHO_CARD: HardwareModel.ValueType # 136
|
||||
"""
|
||||
Lilygo T-Echo Card
|
||||
"""
|
||||
PRIVATE_HW: HardwareModel.ValueType # 255
|
||||
"""
|
||||
------------------------------------------------------------------------------------------------------------------------------------------
|
||||
@@ -2374,6 +2468,126 @@ class StoreForwardPlusPlus(google.protobuf.message.Message):
|
||||
|
||||
global___StoreForwardPlusPlus = StoreForwardPlusPlus
|
||||
|
||||
@typing.final
|
||||
class RemoteShell(google.protobuf.message.Message):
|
||||
"""
|
||||
The actual over-the-mesh message doing RemoteShell
|
||||
"""
|
||||
|
||||
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
||||
|
||||
class _OpCode:
|
||||
ValueType = typing.NewType("ValueType", builtins.int)
|
||||
V: typing_extensions.TypeAlias = ValueType
|
||||
|
||||
class _OpCodeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[RemoteShell._OpCode.ValueType], builtins.type):
|
||||
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
|
||||
OP_UNSET: RemoteShell._OpCode.ValueType # 0
|
||||
OPEN: RemoteShell._OpCode.ValueType # 1
|
||||
"""Client -> server"""
|
||||
INPUT: RemoteShell._OpCode.ValueType # 2
|
||||
RESIZE: RemoteShell._OpCode.ValueType # 3
|
||||
CLOSE: RemoteShell._OpCode.ValueType # 4
|
||||
PING: RemoteShell._OpCode.ValueType # 5
|
||||
ACK: RemoteShell._OpCode.ValueType # 6
|
||||
OPEN_OK: RemoteShell._OpCode.ValueType # 64
|
||||
"""Server -> client"""
|
||||
OUTPUT: RemoteShell._OpCode.ValueType # 65
|
||||
CLOSED: RemoteShell._OpCode.ValueType # 66
|
||||
ERROR: RemoteShell._OpCode.ValueType # 67
|
||||
PONG: RemoteShell._OpCode.ValueType # 68
|
||||
|
||||
class OpCode(_OpCode, metaclass=_OpCodeEnumTypeWrapper):
|
||||
"""
|
||||
Frame op code for PTY session control and stream transport.
|
||||
|
||||
Values 1-63 are client->server requests.
|
||||
Values 64-127 are server->client responses/events.
|
||||
"""
|
||||
|
||||
OP_UNSET: RemoteShell.OpCode.ValueType # 0
|
||||
OPEN: RemoteShell.OpCode.ValueType # 1
|
||||
"""Client -> server"""
|
||||
INPUT: RemoteShell.OpCode.ValueType # 2
|
||||
RESIZE: RemoteShell.OpCode.ValueType # 3
|
||||
CLOSE: RemoteShell.OpCode.ValueType # 4
|
||||
PING: RemoteShell.OpCode.ValueType # 5
|
||||
ACK: RemoteShell.OpCode.ValueType # 6
|
||||
OPEN_OK: RemoteShell.OpCode.ValueType # 64
|
||||
"""Server -> client"""
|
||||
OUTPUT: RemoteShell.OpCode.ValueType # 65
|
||||
CLOSED: RemoteShell.OpCode.ValueType # 66
|
||||
ERROR: RemoteShell.OpCode.ValueType # 67
|
||||
PONG: RemoteShell.OpCode.ValueType # 68
|
||||
|
||||
OP_FIELD_NUMBER: builtins.int
|
||||
SESSION_ID_FIELD_NUMBER: builtins.int
|
||||
SEQ_FIELD_NUMBER: builtins.int
|
||||
ACK_SEQ_FIELD_NUMBER: builtins.int
|
||||
PAYLOAD_FIELD_NUMBER: builtins.int
|
||||
COLS_FIELD_NUMBER: builtins.int
|
||||
ROWS_FIELD_NUMBER: builtins.int
|
||||
FLAGS_FIELD_NUMBER: builtins.int
|
||||
LAST_TX_SEQ_FIELD_NUMBER: builtins.int
|
||||
LAST_RX_SEQ_FIELD_NUMBER: builtins.int
|
||||
op: global___RemoteShell.OpCode.ValueType
|
||||
"""
|
||||
Structured frame operation.
|
||||
"""
|
||||
session_id: builtins.int
|
||||
"""
|
||||
Logical PTY session identifier.
|
||||
"""
|
||||
seq: builtins.int
|
||||
"""
|
||||
Monotonic sequence number for this frame.
|
||||
"""
|
||||
ack_seq: builtins.int
|
||||
"""
|
||||
Cumulative ack sequence number.
|
||||
"""
|
||||
payload: builtins.bytes
|
||||
"""
|
||||
Opaque bytes payload for INPUT/OUTPUT/ERROR and other frame bodies.
|
||||
"""
|
||||
cols: builtins.int
|
||||
"""
|
||||
Terminal size columns used for OPEN/RESIZE signaling.
|
||||
"""
|
||||
rows: builtins.int
|
||||
"""
|
||||
Terminal size rows used for OPEN/RESIZE signaling.
|
||||
"""
|
||||
flags: builtins.int
|
||||
"""
|
||||
Bit flags for protocol extensions.
|
||||
"""
|
||||
last_tx_seq: builtins.int
|
||||
"""
|
||||
The last sequence number TX'd.
|
||||
"""
|
||||
last_rx_seq: builtins.int
|
||||
"""
|
||||
The last sequence number RX'd.
|
||||
"""
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
op: global___RemoteShell.OpCode.ValueType = ...,
|
||||
session_id: builtins.int = ...,
|
||||
seq: builtins.int = ...,
|
||||
ack_seq: builtins.int = ...,
|
||||
payload: builtins.bytes = ...,
|
||||
cols: builtins.int = ...,
|
||||
rows: builtins.int = ...,
|
||||
flags: builtins.int = ...,
|
||||
last_tx_seq: builtins.int = ...,
|
||||
last_rx_seq: builtins.int = ...,
|
||||
) -> None: ...
|
||||
def ClearField(self, field_name: typing.Literal["ack_seq", b"ack_seq", "cols", b"cols", "flags", b"flags", "last_rx_seq", b"last_rx_seq", "last_tx_seq", b"last_tx_seq", "op", b"op", "payload", b"payload", "rows", b"rows", "seq", b"seq", "session_id", b"session_id"]) -> None: ...
|
||||
|
||||
global___RemoteShell = RemoteShell
|
||||
|
||||
@typing.final
|
||||
class Waypoint(google.protobuf.message.Message):
|
||||
"""
|
||||
@@ -3282,6 +3496,7 @@ class FromRadio(google.protobuf.message.Message):
|
||||
FILEINFO_FIELD_NUMBER: builtins.int
|
||||
CLIENTNOTIFICATION_FIELD_NUMBER: builtins.int
|
||||
DEVICEUICONFIG_FIELD_NUMBER: builtins.int
|
||||
LOCKDOWN_STATUS_FIELD_NUMBER: builtins.int
|
||||
id: builtins.int
|
||||
"""
|
||||
The packet id, used to allow the phone to request missing read packets from the FIFO,
|
||||
@@ -3387,6 +3602,16 @@ class FromRadio(google.protobuf.message.Message):
|
||||
Persistent data for device-ui
|
||||
"""
|
||||
|
||||
@property
|
||||
def lockdown_status(self) -> global___LockdownStatus:
|
||||
"""
|
||||
Lockdown state notification for hardened firmware builds.
|
||||
Sent post-config (so unauthorized clients learn they must
|
||||
provision/unlock) and after each LockdownAuth admin command
|
||||
to report success or failure. Replaces the earlier scheme of
|
||||
encoding state as magic-string prefixes inside ClientNotification.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
@@ -3407,13 +3632,133 @@ class FromRadio(google.protobuf.message.Message):
|
||||
fileInfo: global___FileInfo | None = ...,
|
||||
clientNotification: global___ClientNotification | None = ...,
|
||||
deviceuiConfig: meshtastic.protobuf.device_ui_pb2.DeviceUIConfig | None = ...,
|
||||
lockdown_status: global___LockdownStatus | None = ...,
|
||||
) -> None: ...
|
||||
def HasField(self, field_name: typing.Literal["channel", b"channel", "clientNotification", b"clientNotification", "config", b"config", "config_complete_id", b"config_complete_id", "deviceuiConfig", b"deviceuiConfig", "fileInfo", b"fileInfo", "log_record", b"log_record", "metadata", b"metadata", "moduleConfig", b"moduleConfig", "mqttClientProxyMessage", b"mqttClientProxyMessage", "my_info", b"my_info", "node_info", b"node_info", "packet", b"packet", "payload_variant", b"payload_variant", "queueStatus", b"queueStatus", "rebooted", b"rebooted", "xmodemPacket", b"xmodemPacket"]) -> builtins.bool: ...
|
||||
def ClearField(self, field_name: typing.Literal["channel", b"channel", "clientNotification", b"clientNotification", "config", b"config", "config_complete_id", b"config_complete_id", "deviceuiConfig", b"deviceuiConfig", "fileInfo", b"fileInfo", "id", b"id", "log_record", b"log_record", "metadata", b"metadata", "moduleConfig", b"moduleConfig", "mqttClientProxyMessage", b"mqttClientProxyMessage", "my_info", b"my_info", "node_info", b"node_info", "packet", b"packet", "payload_variant", b"payload_variant", "queueStatus", b"queueStatus", "rebooted", b"rebooted", "xmodemPacket", b"xmodemPacket"]) -> None: ...
|
||||
def WhichOneof(self, oneof_group: typing.Literal["payload_variant", b"payload_variant"]) -> typing.Literal["packet", "my_info", "node_info", "config", "log_record", "config_complete_id", "rebooted", "moduleConfig", "channel", "queueStatus", "xmodemPacket", "metadata", "mqttClientProxyMessage", "fileInfo", "clientNotification", "deviceuiConfig"] | None: ...
|
||||
def HasField(self, field_name: typing.Literal["channel", b"channel", "clientNotification", b"clientNotification", "config", b"config", "config_complete_id", b"config_complete_id", "deviceuiConfig", b"deviceuiConfig", "fileInfo", b"fileInfo", "lockdown_status", b"lockdown_status", "log_record", b"log_record", "metadata", b"metadata", "moduleConfig", b"moduleConfig", "mqttClientProxyMessage", b"mqttClientProxyMessage", "my_info", b"my_info", "node_info", b"node_info", "packet", b"packet", "payload_variant", b"payload_variant", "queueStatus", b"queueStatus", "rebooted", b"rebooted", "xmodemPacket", b"xmodemPacket"]) -> builtins.bool: ...
|
||||
def ClearField(self, field_name: typing.Literal["channel", b"channel", "clientNotification", b"clientNotification", "config", b"config", "config_complete_id", b"config_complete_id", "deviceuiConfig", b"deviceuiConfig", "fileInfo", b"fileInfo", "id", b"id", "lockdown_status", b"lockdown_status", "log_record", b"log_record", "metadata", b"metadata", "moduleConfig", b"moduleConfig", "mqttClientProxyMessage", b"mqttClientProxyMessage", "my_info", b"my_info", "node_info", b"node_info", "packet", b"packet", "payload_variant", b"payload_variant", "queueStatus", b"queueStatus", "rebooted", b"rebooted", "xmodemPacket", b"xmodemPacket"]) -> None: ...
|
||||
def WhichOneof(self, oneof_group: typing.Literal["payload_variant", b"payload_variant"]) -> typing.Literal["packet", "my_info", "node_info", "config", "log_record", "config_complete_id", "rebooted", "moduleConfig", "channel", "queueStatus", "xmodemPacket", "metadata", "mqttClientProxyMessage", "fileInfo", "clientNotification", "deviceuiConfig", "lockdown_status"] | None: ...
|
||||
|
||||
global___FromRadio = FromRadio
|
||||
|
||||
@typing.final
|
||||
class LockdownStatus(google.protobuf.message.Message):
|
||||
"""
|
||||
Lockdown state report from firmware to client (for hardened builds
|
||||
with MESHTASTIC_LOCKDOWN). Sent immediately after config_complete_id
|
||||
to inform a freshly-connected unauthorized client what it must do,
|
||||
and again in response to each LockdownAuth admin command.
|
||||
"""
|
||||
|
||||
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
||||
|
||||
class _State:
|
||||
ValueType = typing.NewType("ValueType", builtins.int)
|
||||
V: typing_extensions.TypeAlias = ValueType
|
||||
|
||||
class _StateEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[LockdownStatus._State.ValueType], builtins.type):
|
||||
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
|
||||
STATE_UNSPECIFIED: LockdownStatus._State.ValueType # 0
|
||||
"""Default; should not be sent."""
|
||||
NEEDS_PROVISION: LockdownStatus._State.ValueType # 1
|
||||
"""
|
||||
No passphrase has ever been provisioned on this device.
|
||||
Client should prompt the operator to set one.
|
||||
"""
|
||||
LOCKED: LockdownStatus._State.ValueType # 2
|
||||
"""
|
||||
Storage is locked or this client has not authenticated yet.
|
||||
lock_reason carries a machine-readable detail string.
|
||||
Client should present (or auto-replay) a passphrase via
|
||||
AdminMessage.lockdown_auth.
|
||||
"""
|
||||
UNLOCKED: LockdownStatus._State.ValueType # 3
|
||||
"""
|
||||
Passphrase accepted; client is now authorized for this connection.
|
||||
boots_remaining and valid_until_epoch describe the active session
|
||||
token's TTL.
|
||||
"""
|
||||
UNLOCK_FAILED: LockdownStatus._State.ValueType # 4
|
||||
"""
|
||||
Passphrase rejected. backoff_seconds is non-zero when rate-limited.
|
||||
"""
|
||||
|
||||
class State(_State, metaclass=_StateEnumTypeWrapper): ...
|
||||
STATE_UNSPECIFIED: LockdownStatus.State.ValueType # 0
|
||||
"""Default; should not be sent."""
|
||||
NEEDS_PROVISION: LockdownStatus.State.ValueType # 1
|
||||
"""
|
||||
No passphrase has ever been provisioned on this device.
|
||||
Client should prompt the operator to set one.
|
||||
"""
|
||||
LOCKED: LockdownStatus.State.ValueType # 2
|
||||
"""
|
||||
Storage is locked or this client has not authenticated yet.
|
||||
lock_reason carries a machine-readable detail string.
|
||||
Client should present (or auto-replay) a passphrase via
|
||||
AdminMessage.lockdown_auth.
|
||||
"""
|
||||
UNLOCKED: LockdownStatus.State.ValueType # 3
|
||||
"""
|
||||
Passphrase accepted; client is now authorized for this connection.
|
||||
boots_remaining and valid_until_epoch describe the active session
|
||||
token's TTL.
|
||||
"""
|
||||
UNLOCK_FAILED: LockdownStatus.State.ValueType # 4
|
||||
"""
|
||||
Passphrase rejected. backoff_seconds is non-zero when rate-limited.
|
||||
"""
|
||||
|
||||
STATE_FIELD_NUMBER: builtins.int
|
||||
LOCK_REASON_FIELD_NUMBER: builtins.int
|
||||
BOOTS_REMAINING_FIELD_NUMBER: builtins.int
|
||||
VALID_UNTIL_EPOCH_FIELD_NUMBER: builtins.int
|
||||
BACKOFF_SECONDS_FIELD_NUMBER: builtins.int
|
||||
state: global___LockdownStatus.State.ValueType
|
||||
"""Current lockdown state being reported."""
|
||||
lock_reason: builtins.str
|
||||
"""
|
||||
For LOCKED: machine-readable reason. Known values:
|
||||
"needs_auth" — storage already unlocked, client must auth
|
||||
"token_missing" — no boot token on flash
|
||||
"token_expired" — boot token wall-clock TTL elapsed
|
||||
"token_boots_zero" — boot token boot-count TTL exhausted
|
||||
"token_hmac_fail" — token tampered or wrong device
|
||||
"token_dek_fail" — token DEK decrypt failed
|
||||
"token_wrong_size" — token file corrupted
|
||||
"token_bad_magic" — token file corrupted
|
||||
"not_provisioned" — should generally use NEEDS_PROVISION state instead
|
||||
Other values may be added; clients should treat unknown values as
|
||||
"locked, ask for passphrase".
|
||||
"""
|
||||
boots_remaining: builtins.int
|
||||
"""
|
||||
For UNLOCKED: remaining boots on the issued session token.
|
||||
Decrements by 1 on each subsequent boot.
|
||||
"""
|
||||
valid_until_epoch: builtins.int
|
||||
"""
|
||||
For UNLOCKED: wall-clock expiry of the issued session token,
|
||||
absolute Unix-epoch seconds. 0 = no time limit.
|
||||
"""
|
||||
backoff_seconds: builtins.int
|
||||
"""
|
||||
For UNLOCK_FAILED: seconds the client must wait before another
|
||||
passphrase attempt will be accepted. 0 = wrong passphrase, no
|
||||
backoff (immediate retry allowed but advisable to prompt user).
|
||||
"""
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
state: global___LockdownStatus.State.ValueType = ...,
|
||||
lock_reason: builtins.str = ...,
|
||||
boots_remaining: builtins.int = ...,
|
||||
valid_until_epoch: builtins.int = ...,
|
||||
backoff_seconds: builtins.int = ...,
|
||||
) -> None: ...
|
||||
def ClearField(self, field_name: typing.Literal["backoff_seconds", b"backoff_seconds", "boots_remaining", b"boots_remaining", "lock_reason", b"lock_reason", "state", b"state", "valid_until_epoch", b"valid_until_epoch"]) -> None: ...
|
||||
|
||||
global___LockdownStatus = LockdownStatus
|
||||
|
||||
@typing.final
|
||||
class ClientNotification(google.protobuf.message.Message):
|
||||
"""
|
||||
|
||||
150
meshtastic/protobuf/module_config_pb2.py
generated
150
meshtastic/protobuf/module_config_pb2.py
generated
File diff suppressed because one or more lines are too long
152
meshtastic/protobuf/module_config_pb2.pyi
generated
152
meshtastic/protobuf/module_config_pb2.pyi
generated
@@ -9,6 +9,7 @@ import google.protobuf.descriptor
|
||||
import google.protobuf.internal.containers
|
||||
import google.protobuf.internal.enum_type_wrapper
|
||||
import google.protobuf.message
|
||||
import meshtastic.protobuf.atak_pb2
|
||||
import sys
|
||||
import typing
|
||||
|
||||
@@ -112,7 +113,7 @@ class ModuleConfig(google.protobuf.message.Message):
|
||||
"""
|
||||
json_enabled: builtins.bool
|
||||
"""
|
||||
Whether to send / consume json packets on MQTT
|
||||
Deprecated: JSON packet support on MQTT was removed, and this field is ignored.
|
||||
"""
|
||||
tls_enabled: builtins.bool
|
||||
"""
|
||||
@@ -492,6 +493,105 @@ class ModuleConfig(google.protobuf.message.Message):
|
||||
) -> 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
|
||||
class SerialConfig(google.protobuf.message.Message):
|
||||
"""
|
||||
@@ -1202,6 +1302,34 @@ class ModuleConfig(google.protobuf.message.Message):
|
||||
) -> 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
|
||||
SERIAL_FIELD_NUMBER: builtins.int
|
||||
EXTERNAL_NOTIFICATION_FIELD_NUMBER: builtins.int
|
||||
@@ -1216,6 +1344,8 @@ class ModuleConfig(google.protobuf.message.Message):
|
||||
DETECTION_SENSOR_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
|
||||
def mqtt(self) -> global___ModuleConfig.MQTTConfig:
|
||||
"""
|
||||
@@ -1300,6 +1430,18 @@ class ModuleConfig(google.protobuf.message.Message):
|
||||
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__(
|
||||
self,
|
||||
*,
|
||||
@@ -1317,10 +1459,12 @@ class ModuleConfig(google.protobuf.message.Message):
|
||||
detection_sensor: global___ModuleConfig.DetectionSensorConfig | None = ...,
|
||||
paxcounter: global___ModuleConfig.PaxcounterConfig | None = ...,
|
||||
statusmessage: global___ModuleConfig.StatusMessageConfig | None = ...,
|
||||
traffic_management: global___ModuleConfig.TrafficManagementConfig | None = ...,
|
||||
tak: global___ModuleConfig.TAKConfig | 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", "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", "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"] | 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 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 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: ...
|
||||
|
||||
global___ModuleConfig = ModuleConfig
|
||||
|
||||
|
||||
19
meshtastic/protobuf/mqtt_pb2.py
generated
19
meshtastic/protobuf/mqtt_pb2.py
generated
@@ -13,9 +13,10 @@ _sym_db = _symbol_database.Default()
|
||||
|
||||
from meshtastic.protobuf import config_pb2 as meshtastic_dot_protobuf_dot_config__pb2
|
||||
from meshtastic.protobuf import mesh_pb2 as meshtastic_dot_protobuf_dot_mesh__pb2
|
||||
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\"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\"\x83\x04\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(\r\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\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')
|
||||
|
||||
_globals = globals()
|
||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||
@@ -23,8 +24,16 @@ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.mqtt_pb
|
||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
DESCRIPTOR._options = None
|
||||
DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\nMQTTProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||
_globals['_SERVICEENVELOPE']._serialized_start=121
|
||||
_globals['_SERVICEENVELOPE']._serialized_end=227
|
||||
_globals['_MAPREPORT']._serialized_start=230
|
||||
_globals['_MAPREPORT']._serialized_end=745
|
||||
_MAPREPORT.fields_by_name['long_name']._options = None
|
||||
_MAPREPORT.fields_by_name['long_name']._serialized_options = b'\222?\002\010('
|
||||
_MAPREPORT.fields_by_name['short_name']._options = None
|
||||
_MAPREPORT.fields_by_name['short_name']._serialized_options = b'\222?\002\010\005'
|
||||
_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)
|
||||
|
||||
4
meshtastic/protobuf/portnums_pb2.py
generated
4
meshtastic/protobuf/portnums_pb2.py
generated
@@ -13,7 +13,7 @@ _sym_db = _symbol_database.Default()
|
||||
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\"meshtastic/protobuf/portnums.proto\x12\x13meshtastic.protobuf*\xab\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\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\x18\n\x14RETICULUM_TUNNEL_APP\x10L\x12\x0f\n\x0b\x43\x41YENNE_APP\x10M\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*\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')
|
||||
|
||||
_globals = globals()
|
||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||
@@ -22,5 +22,5 @@ if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
DESCRIPTOR._options = None
|
||||
DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\010PortnumsZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||
_globals['_PORTNUM']._serialized_start=60
|
||||
_globals['_PORTNUM']._serialized_end=743
|
||||
_globals['_PORTNUM']._serialized_end=825
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
|
||||
42
meshtastic/protobuf/portnums_pb2.pyi
generated
42
meshtastic/protobuf/portnums_pb2.pyi
generated
@@ -101,6 +101,10 @@ class _PortNumEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTy
|
||||
"""
|
||||
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
|
||||
"""
|
||||
Provides a 'ping' service that replies to any packet it receives.
|
||||
@@ -197,6 +201,11 @@ class _PortNumEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTy
|
||||
"""
|
||||
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
|
||||
@@ -208,6 +217,18 @@ class _PortNumEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTy
|
||||
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 applications should use portnums >= 256.
|
||||
@@ -319,6 +340,10 @@ 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
|
||||
"""
|
||||
Provides a 'ping' service that replies to any packet it receives.
|
||||
@@ -415,6 +440,11 @@ POWERSTRESS_APP: PortNum.ValueType # 74
|
||||
"""
|
||||
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
|
||||
@@ -426,6 +456,18 @@ App for transporting Cayenne Low Power Payload, popular for LoRaWAN sensor nodes
|
||||
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 applications should use portnums >= 256.
|
||||
|
||||
9
meshtastic/protobuf/rtttl_pb2.py
generated
9
meshtastic/protobuf/rtttl_pb2.py
generated
@@ -11,9 +11,10 @@ from google.protobuf.internal import builder as _builder
|
||||
_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\"\x1f\n\x0bRTTTLConfig\x12\x10\n\x08ringtone\x18\x01 \x01(\tBg\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\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')
|
||||
|
||||
_globals = globals()
|
||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||
@@ -21,6 +22,8 @@ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.rtttl_p
|
||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
DESCRIPTOR._options = None
|
||||
DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\021RTTTLConfigProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||
_globals['_RTTTLCONFIG']._serialized_start=56
|
||||
_globals['_RTTTLCONFIG']._serialized_end=87
|
||||
_RTTTLCONFIG.fields_by_name['ringtone']._options = None
|
||||
_RTTTLCONFIG.fields_by_name['ringtone']._serialized_options = b'\222?\003\010\347\001'
|
||||
_globals['_RTTTLCONFIG']._serialized_start=90
|
||||
_globals['_RTTTLCONFIG']._serialized_end=129
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
|
||||
39
meshtastic/protobuf/serial_hal_pb2.py
generated
Normal file
39
meshtastic/protobuf/serial_hal_pb2.py
generated
Normal file
@@ -0,0 +1,39 @@
|
||||
# -*- 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
Normal file
140
meshtastic/protobuf/serial_hal_pb2.pyi
generated
Normal file
@@ -0,0 +1,140 @@
|
||||
"""
|
||||
@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
|
||||
25
meshtastic/protobuf/storeforward_pb2.py
generated
25
meshtastic/protobuf/storeforward_pb2.py
generated
@@ -11,9 +11,10 @@ from google.protobuf.internal import builder as _builder
|
||||
_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\"\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\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\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')
|
||||
|
||||
_globals = globals()
|
||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||
@@ -21,14 +22,16 @@ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.storefo
|
||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
DESCRIPTOR._options = None
|
||||
DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\025StoreAndForwardProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||
_globals['_STOREANDFORWARD']._serialized_start=64
|
||||
_globals['_STOREANDFORWARD']._serialized_end=1024
|
||||
_globals['_STOREANDFORWARD_STATISTICS']._serialized_start=366
|
||||
_globals['_STOREANDFORWARD_STATISTICS']._serialized_end=571
|
||||
_globals['_STOREANDFORWARD_HISTORY']._serialized_start=573
|
||||
_globals['_STOREANDFORWARD_HISTORY']._serialized_end=646
|
||||
_globals['_STOREANDFORWARD_HEARTBEAT']._serialized_start=648
|
||||
_globals['_STOREANDFORWARD_HEARTBEAT']._serialized_end=694
|
||||
_globals['_STOREANDFORWARD_REQUESTRESPONSE']._serialized_start=697
|
||||
_globals['_STOREANDFORWARD_REQUESTRESPONSE']._serialized_end=1013
|
||||
_STOREANDFORWARD.fields_by_name['text']._options = None
|
||||
_STOREANDFORWARD.fields_by_name['text']._serialized_options = b'\222?\003\010\351\001'
|
||||
_globals['_STOREANDFORWARD']._serialized_start=98
|
||||
_globals['_STOREANDFORWARD']._serialized_end=1066
|
||||
_globals['_STOREANDFORWARD_STATISTICS']._serialized_start=408
|
||||
_globals['_STOREANDFORWARD_STATISTICS']._serialized_end=613
|
||||
_globals['_STOREANDFORWARD_HISTORY']._serialized_start=615
|
||||
_globals['_STOREANDFORWARD_HISTORY']._serialized_end=688
|
||||
_globals['_STOREANDFORWARD_HEARTBEAT']._serialized_start=690
|
||||
_globals['_STOREANDFORWARD_HEARTBEAT']._serialized_end=736
|
||||
_globals['_STOREANDFORWARD_REQUESTRESPONSE']._serialized_start=739
|
||||
_globals['_STOREANDFORWARD_REQUESTRESPONSE']._serialized_end=1055
|
||||
# @@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
219
meshtastic/protobuf/telemetry_pb2.pyi
generated
219
meshtastic/protobuf/telemetry_pb2.pyi
generated
@@ -4,7 +4,9 @@ isort:skip_file
|
||||
"""
|
||||
|
||||
import builtins
|
||||
import collections.abc
|
||||
import google.protobuf.descriptor
|
||||
import google.protobuf.internal.containers
|
||||
import google.protobuf.internal.enum_type_wrapper
|
||||
import google.protobuf.message
|
||||
import sys
|
||||
@@ -53,7 +55,7 @@ class _TelemetrySensorTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wra
|
||||
"""
|
||||
SHTC3: _TelemetrySensorType.ValueType # 7
|
||||
"""
|
||||
High accuracy temperature and humidity
|
||||
TODO - REMOVE High accuracy temperature and humidity
|
||||
"""
|
||||
LPS22: _TelemetrySensorType.ValueType # 8
|
||||
"""
|
||||
@@ -73,7 +75,7 @@ class _TelemetrySensorTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wra
|
||||
"""
|
||||
SHT31: _TelemetrySensorType.ValueType # 12
|
||||
"""
|
||||
High accuracy temperature and humidity
|
||||
TODO - REMOVE High accuracy temperature and humidity
|
||||
"""
|
||||
PMSA003I: _TelemetrySensorType.ValueType # 13
|
||||
"""
|
||||
@@ -93,7 +95,7 @@ class _TelemetrySensorTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wra
|
||||
"""
|
||||
SHT4X: _TelemetrySensorType.ValueType # 17
|
||||
"""
|
||||
Sensirion High accuracy temperature and humidity
|
||||
TODO - REMOVE Sensirion High accuracy temperature and humidity
|
||||
"""
|
||||
VEML7700: _TelemetrySensorType.ValueType # 18
|
||||
"""
|
||||
@@ -207,6 +209,38 @@ class _TelemetrySensorTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wra
|
||||
"""
|
||||
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):
|
||||
"""
|
||||
@@ -243,7 +277,7 @@ High accuracy temperature and pressure
|
||||
"""
|
||||
SHTC3: TelemetrySensorType.ValueType # 7
|
||||
"""
|
||||
High accuracy temperature and humidity
|
||||
TODO - REMOVE High accuracy temperature and humidity
|
||||
"""
|
||||
LPS22: TelemetrySensorType.ValueType # 8
|
||||
"""
|
||||
@@ -263,7 +297,7 @@ QMC5883L: TelemetrySensorType.ValueType # 11
|
||||
"""
|
||||
SHT31: TelemetrySensorType.ValueType # 12
|
||||
"""
|
||||
High accuracy temperature and humidity
|
||||
TODO - REMOVE High accuracy temperature and humidity
|
||||
"""
|
||||
PMSA003I: TelemetrySensorType.ValueType # 13
|
||||
"""
|
||||
@@ -283,7 +317,7 @@ RCWL-9620 Doppler Radar Distance Sensor, used for water level detection
|
||||
"""
|
||||
SHT4X: TelemetrySensorType.ValueType # 17
|
||||
"""
|
||||
Sensirion High accuracy temperature and humidity
|
||||
TODO - REMOVE Sensirion High accuracy temperature and humidity
|
||||
"""
|
||||
VEML7700: TelemetrySensorType.ValueType # 18
|
||||
"""
|
||||
@@ -397,6 +431,38 @@ 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
|
||||
|
||||
@typing.final
|
||||
@@ -486,6 +552,7 @@ class EnvironmentMetrics(google.protobuf.message.Message):
|
||||
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 measured
|
||||
@@ -576,6 +643,12 @@ class EnvironmentMetrics(google.protobuf.message.Message):
|
||||
"""
|
||||
Soil temperature measured (*C)
|
||||
"""
|
||||
@property
|
||||
def one_wire_temperature(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.float]:
|
||||
"""
|
||||
One-wire temperature (*C)
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
@@ -601,9 +674,10 @@ class EnvironmentMetrics(google.protobuf.message.Message):
|
||||
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: ...
|
||||
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 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", "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", "_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: ...
|
||||
@typing.overload
|
||||
def WhichOneof(self, oneof_group: typing.Literal["_barometric_pressure", b"_barometric_pressure"]) -> typing.Literal["barometric_pressure"] | None: ...
|
||||
@typing.overload
|
||||
@@ -1121,6 +1195,64 @@ class LocalStats(google.protobuf.message.Message):
|
||||
|
||||
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
|
||||
class HealthMetrics(google.protobuf.message.Message):
|
||||
"""
|
||||
@@ -1256,6 +1388,7 @@ class Telemetry(google.protobuf.message.Message):
|
||||
LOCAL_STATS_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
|
||||
"""
|
||||
Seconds since 1970 - or 0 for unknown/unset
|
||||
@@ -1302,6 +1435,12 @@ class Telemetry(google.protobuf.message.Message):
|
||||
Linux host metrics
|
||||
"""
|
||||
|
||||
@property
|
||||
def traffic_management_stats(self) -> global___TrafficManagementStats:
|
||||
"""
|
||||
Traffic management statistics
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
@@ -1313,10 +1452,11 @@ class Telemetry(google.protobuf.message.Message):
|
||||
local_stats: global___LocalStats | None = ...,
|
||||
health_metrics: global___HealthMetrics | None = ...,
|
||||
host_metrics: global___HostMetrics | None = ...,
|
||||
traffic_management_stats: global___TrafficManagementStats | 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", "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", "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"] | 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 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 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: ...
|
||||
|
||||
global___Telemetry = Telemetry
|
||||
|
||||
@@ -1347,3 +1487,62 @@ class Nau7802Config(google.protobuf.message.Message):
|
||||
def ClearField(self, field_name: typing.Literal["calibrationFactor", b"calibrationFactor", "zeroOffset", b"zeroOffset"]) -> None: ...
|
||||
|
||||
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
|
||||
|
||||
17
meshtastic/protobuf/xmodem_pb2.py
generated
17
meshtastic/protobuf/xmodem_pb2.py
generated
@@ -11,9 +11,10 @@ from google.protobuf.internal import builder as _builder
|
||||
_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\"\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\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\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')
|
||||
|
||||
_globals = globals()
|
||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||
@@ -21,8 +22,14 @@ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.xmodem_
|
||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
DESCRIPTOR._options = None
|
||||
DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\014XmodemProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||
_globals['_XMODEM']._serialized_start=58
|
||||
_globals['_XMODEM']._serialized_end=249
|
||||
_globals['_XMODEM_CONTROL']._serialized_start=166
|
||||
_globals['_XMODEM_CONTROL']._serialized_end=249
|
||||
_XMODEM.fields_by_name['seq']._options = None
|
||||
_XMODEM.fields_by_name['seq']._serialized_options = b'\222?\0028\020'
|
||||
_XMODEM.fields_by_name['crc16']._options = None
|
||||
_XMODEM.fields_by_name['crc16']._serialized_options = b'\222?\0028\020'
|
||||
_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)
|
||||
|
||||
@@ -35,8 +35,6 @@ class SerialInterface(StreamInterface):
|
||||
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
|
||||
|
||||
if self.devPath is None:
|
||||
@@ -52,22 +50,28 @@ class SerialInterface(StreamInterface):
|
||||
else:
|
||||
self.devPath = ports[0]
|
||||
|
||||
StreamInterface.__init__(
|
||||
self, debugOut=debugOut, noProto=noProto, connectNow=connectNow, noNodes=noNodes, timeout=timeout
|
||||
)
|
||||
|
||||
def connect(self) -> None:
|
||||
logger.debug(f"Connecting to {self.devPath}")
|
||||
dev_path = self.devPath
|
||||
if dev_path is None:
|
||||
raise RuntimeError("Serial device path is not set")
|
||||
|
||||
if sys.platform != "win32":
|
||||
with open(self.devPath, encoding="utf8") as f:
|
||||
with open(dev_path, encoding="utf8") as f:
|
||||
self._set_hupcl_with_termios(f)
|
||||
time.sleep(0.1)
|
||||
|
||||
self.stream = serial.Serial(
|
||||
self.devPath, 115200, exclusive=True, timeout=0.5, write_timeout=0
|
||||
dev_path, 115200, exclusive=True, timeout=0.5, write_timeout=0
|
||||
)
|
||||
self.stream.flush() # type: ignore[attr-defined]
|
||||
time.sleep(0.1)
|
||||
|
||||
StreamInterface.__init__(
|
||||
self, debugOut=debugOut, noProto=noProto, connectNow=connectNow, noNodes=noNodes, timeout=timeout
|
||||
)
|
||||
super().connect()
|
||||
|
||||
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
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
"""Stream Interface base class
|
||||
"""
|
||||
import contextlib
|
||||
import io
|
||||
import logging
|
||||
import threading
|
||||
@@ -39,15 +40,14 @@ class StreamInterface(MeshInterface):
|
||||
timeout -- How long to wait for replies (default: 300 seconds)
|
||||
|
||||
Raises:
|
||||
Exception: [description]
|
||||
Exception: [description]
|
||||
RuntimeError: Raised if StreamInterface is instantiated when noProto is false.
|
||||
"""
|
||||
|
||||
if not hasattr(self, "stream") and not noProto:
|
||||
raise Exception( # pylint: disable=W0719
|
||||
if not noProto and type(self) == StreamInterface: # pylint: disable=C0123
|
||||
raise RuntimeError(
|
||||
"StreamInterface is now abstract (to update existing code create SerialInterface instead)"
|
||||
)
|
||||
self.stream: Optional[serial.Serial] # only serial uses this, TCPInterface overrides the relevant methods instead
|
||||
self.stream: Optional[serial.Serial] = None # only serial uses this, TCPInterface overrides the relevant methods instead
|
||||
self._rxBuf = bytes() # empty
|
||||
self._wantExit = False
|
||||
|
||||
@@ -61,9 +61,17 @@ class StreamInterface(MeshInterface):
|
||||
|
||||
# Start the reader thread after superclass constructor completes init
|
||||
if connectNow:
|
||||
self.connect()
|
||||
if not noProto:
|
||||
self.waitForConfig()
|
||||
try:
|
||||
self.connect()
|
||||
if not noProto:
|
||||
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:
|
||||
"""Connect to our radio
|
||||
@@ -136,7 +144,16 @@ class StreamInterface(MeshInterface):
|
||||
# reader thread to close things for us
|
||||
self._wantExit = True
|
||||
if self._rxThread != threading.current_thread():
|
||||
self._rxThread.join() # wait for it to exit
|
||||
try:
|
||||
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):
|
||||
"""Handle a byte that is part of a log message from the device."""
|
||||
|
||||
@@ -33,20 +33,12 @@ class TCPInterface(StreamInterface):
|
||||
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.portNumber: int = portNumber
|
||||
|
||||
self.socket: Optional[socket.socket] = None
|
||||
self.reconnectLock = threading.Lock()
|
||||
|
||||
if connectNow:
|
||||
self.myConnect()
|
||||
else:
|
||||
self.socket = None
|
||||
|
||||
super().__init__(
|
||||
debugOut=debugOut,
|
||||
noProto=noProto,
|
||||
@@ -77,8 +69,13 @@ class TCPInterface(StreamInterface):
|
||||
if self.socket is not None:
|
||||
self.socket.shutdown(socket.SHUT_RDWR)
|
||||
|
||||
def connect(self) -> None:
|
||||
"""Connect the interface"""
|
||||
self.myConnect()
|
||||
super().connect()
|
||||
|
||||
def myConnect(self) -> None:
|
||||
"""Connect to socket."""
|
||||
"""Connect to socket (without attempting to start the interface's receive thread)"""
|
||||
logger.debug(f"Connecting to {self.hostname}") # type: ignore[str-bytes-safe]
|
||||
server_address = (self.hostname, self.portNumber)
|
||||
self.socket = socket.create_connection(server_address)
|
||||
|
||||
646
meshtastic/tests/test_inject_nanopb_options.py
Normal file
646
meshtastic/tests/test_inject_nanopb_options.py
Normal file
@@ -0,0 +1,646 @@
|
||||
"""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
|
||||
@@ -6,6 +6,8 @@ import os
|
||||
import platform
|
||||
import re
|
||||
import sys
|
||||
import tempfile
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import mock_open, MagicMock, patch
|
||||
|
||||
import pytest
|
||||
@@ -1721,11 +1723,9 @@ def test_main_onReceive_with_sendtext(caplog, capsys):
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.usefixtures("reset_mt_config")
|
||||
def test_main_onReceive_with_text(caplog, capsys):
|
||||
"""Test onReceive with text"""
|
||||
args = MagicMock()
|
||||
args.sendtext.return_value = "foo"
|
||||
mt_config.args = args
|
||||
def test_main_onReceive_with_text_replies_on_target_channel(caplog, capsys):
|
||||
"""Test onReceive replies when channel matches --ch-index (default 0)."""
|
||||
mt_config.args = SimpleNamespace(reply=True, ch_index=0, sendtext=None)
|
||||
|
||||
# Note: 'TEXT_MESSAGE_APP' value is 1
|
||||
# Note: Some of this is faked below.
|
||||
@@ -1751,6 +1751,83 @@ def test_main_onReceive_with_text(caplog, capsys):
|
||||
assert re.search(r"in onReceive", caplog.text, re.MULTILINE)
|
||||
out, err = capsys.readouterr()
|
||||
assert re.search(r"Sending reply", out, re.MULTILINE)
|
||||
iface.sendText.assert_called_once_with(
|
||||
"got msg 'faked' with rxSnr: 6.0 and hopLimit: 3", channelIndex=0
|
||||
)
|
||||
assert err == ""
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.usefixtures("reset_mt_config")
|
||||
def test_main_onReceive_with_text_ignores_non_target_channel(caplog, capsys):
|
||||
"""Test onReceive does not reply when packet channel differs from --ch-index."""
|
||||
mt_config.args = SimpleNamespace(reply=True, ch_index=1, sendtext=None)
|
||||
|
||||
packet = {
|
||||
"to": 4294967295,
|
||||
"decoded": {"portnum": 1, "payload": "hello", "text": "faked"},
|
||||
"id": 334776977,
|
||||
"hop_limit": 3,
|
||||
"want_ack": True,
|
||||
"rxSnr": 6.0,
|
||||
"hopLimit": 3,
|
||||
"raw": "faked",
|
||||
"fromId": "!28b5465c",
|
||||
"toId": "^all",
|
||||
"channel": 0,
|
||||
}
|
||||
|
||||
iface = MagicMock(autospec=SerialInterface)
|
||||
iface.myInfo.my_node_num = 4294967295
|
||||
|
||||
with patch("meshtastic.serial_interface.SerialInterface", return_value=iface):
|
||||
with caplog.at_level(logging.DEBUG):
|
||||
onReceive(packet, iface)
|
||||
assert re.search(r"in onReceive", caplog.text, re.MULTILINE)
|
||||
out, err = capsys.readouterr()
|
||||
assert re.search(
|
||||
r"Ignored message on channel 0 \(waiting for channel 1\)", out, re.MULTILINE
|
||||
)
|
||||
iface.sendText.assert_not_called()
|
||||
assert err == ""
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.usefixtures("reset_mt_config")
|
||||
def test_main_onReceive_with_text_replies_on_explicit_matching_channel(caplog, capsys):
|
||||
"""Test onReceive replies when explicit packet channel matches --ch-index."""
|
||||
mt_config.args = SimpleNamespace(reply=True, ch_index=2, sendtext=None)
|
||||
|
||||
packet = {
|
||||
"to": 4294967295,
|
||||
"decoded": {"portnum": 1, "payload": "hello", "text": "faked"},
|
||||
"id": 334776977,
|
||||
"hop_limit": 3,
|
||||
"want_ack": True,
|
||||
"rxSnr": 6.0,
|
||||
"hopLimit": 3,
|
||||
"raw": "faked",
|
||||
"fromId": "!28b5465c",
|
||||
"toId": "^all",
|
||||
"channel": 2,
|
||||
}
|
||||
|
||||
iface = MagicMock(autospec=SerialInterface)
|
||||
iface.myInfo.my_node_num = 4294967295
|
||||
|
||||
with patch("meshtastic.serial_interface.SerialInterface", return_value=iface):
|
||||
with caplog.at_level(logging.DEBUG):
|
||||
onReceive(packet, iface)
|
||||
assert re.search(r"in onReceive", caplog.text, re.MULTILINE)
|
||||
out, err = capsys.readouterr()
|
||||
assert re.search(
|
||||
r"Received channel 2\. Sending reply: got msg 'faked' with rxSnr: 6.0 and hopLimit: 3",
|
||||
out,
|
||||
re.MULTILINE,
|
||||
)
|
||||
iface.sendText.assert_called_once_with(
|
||||
"got msg 'faked' with rxSnr: 6.0 and hopLimit: 3", channelIndex=2
|
||||
)
|
||||
assert err == ""
|
||||
|
||||
|
||||
@@ -2900,3 +2977,68 @@ def test_main_set_ham_empty_string(capsys):
|
||||
out, _ = capsys.readouterr()
|
||||
assert "ERROR: Ham radio callsign cannot be empty or contain only whitespace characters" in out
|
||||
assert excinfo.value.code == 1
|
||||
|
||||
|
||||
# OTA-related tests
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.usefixtures("reset_mt_config")
|
||||
def test_main_ota_update_file_not_found(capsys):
|
||||
"""Test --ota-update with non-existent file"""
|
||||
sys.argv = [
|
||||
"",
|
||||
"--ota-update",
|
||||
"/nonexistent/firmware.bin",
|
||||
"--host",
|
||||
"192.168.1.100",
|
||||
]
|
||||
mt_config.args = sys.argv
|
||||
|
||||
with pytest.raises(SystemExit) as pytest_wrapped_e:
|
||||
main()
|
||||
|
||||
assert pytest_wrapped_e.type == SystemExit
|
||||
assert pytest_wrapped_e.value.code == 1
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.usefixtures("reset_mt_config")
|
||||
@patch("meshtastic.ota.ESP32WiFiOTA")
|
||||
@patch("meshtastic.__main__.meshtastic.util.our_exit")
|
||||
def test_main_ota_update_retries(mock_our_exit, mock_ota_class, capsys):
|
||||
"""Test --ota-update retries on failure"""
|
||||
# Create a temporary firmware file
|
||||
with tempfile.NamedTemporaryFile(mode="wb", delete=False) as f:
|
||||
f.write(b"fake firmware data")
|
||||
firmware_file = f.name
|
||||
|
||||
try:
|
||||
sys.argv = ["", "--ota-update", firmware_file, "--host", "192.168.1.100"]
|
||||
mt_config.args = sys.argv
|
||||
|
||||
# Mock the OTA class to fail all 5 retries
|
||||
mock_ota = MagicMock()
|
||||
mock_ota_class.return_value = mock_ota
|
||||
mock_ota.hash_bytes.return_value = b"\x00" * 32
|
||||
mock_ota.hash_hex.return_value = "a" * 64
|
||||
mock_ota.update.side_effect = Exception("Connection failed")
|
||||
|
||||
# Mock isinstance to return True
|
||||
with patch("meshtastic.__main__.isinstance", return_value=True):
|
||||
with patch("meshtastic.tcp_interface.TCPInterface") as mock_tcp:
|
||||
mock_iface = MagicMock()
|
||||
mock_iface.hostname = "192.168.1.100"
|
||||
mock_iface.localNode = MagicMock(autospec=Node)
|
||||
mock_tcp.return_value = mock_iface
|
||||
|
||||
with patch("time.sleep"):
|
||||
main()
|
||||
|
||||
# Should have exhausted all retries and called our_exit
|
||||
# Note: our_exit might be called twice - once for TCP check, once for failure
|
||||
assert mock_our_exit.call_count >= 1
|
||||
# Check the last call was for OTA failure
|
||||
last_call_args = mock_our_exit.call_args[0][0]
|
||||
assert "OTA update failed" in last_call_args
|
||||
|
||||
finally:
|
||||
os.unlink(firmware_file)
|
||||
|
||||
22
meshtastic/tests/test_mesh_interface_traffic_management.py
Normal file
22
meshtastic/tests/test_mesh_interface_traffic_management.py
Normal file
@@ -0,0 +1,22 @@
|
||||
"""Meshtastic unit tests for traffic management handling in mesh_interface.py."""
|
||||
|
||||
import pytest
|
||||
|
||||
from ..mesh_interface import MeshInterface
|
||||
from ..protobuf import mesh_pb2
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.usefixtures("reset_mt_config")
|
||||
def test_handleFromRadio_with_traffic_management_module_config():
|
||||
"""Test _handleFromRadio with moduleConfig.traffic_management."""
|
||||
iface = MeshInterface(noProto=True)
|
||||
from_radio = mesh_pb2.FromRadio()
|
||||
from_radio.moduleConfig.traffic_management.enabled = True
|
||||
from_radio.moduleConfig.traffic_management.rate_limit_enabled = True
|
||||
|
||||
iface._handleFromRadio(from_radio.SerializeToString())
|
||||
|
||||
assert iface.localNode.moduleConfig.traffic_management.enabled is True
|
||||
assert iface.localNode.moduleConfig.traffic_management.rate_limit_enabled is True
|
||||
iface.close()
|
||||
@@ -1,4 +1,5 @@
|
||||
"""Meshtastic unit tests for node.py"""
|
||||
# pylint: disable=C0302
|
||||
|
||||
import logging
|
||||
import re
|
||||
@@ -261,6 +262,36 @@ def test_shutdown(caplog):
|
||||
assert re.search(r"Telling node to shutdown", caplog.text, re.MULTILINE)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_factoryReset_config_uses_int_field():
|
||||
"""Test factoryReset(config) sets int32 protobuf field with an int value."""
|
||||
iface = MagicMock(autospec=MeshInterface)
|
||||
anode = Node(iface, 1234567890, noProto=True)
|
||||
|
||||
amesg = admin_pb2.AdminMessage()
|
||||
with patch("meshtastic.node.admin_pb2.AdminMessage", return_value=amesg):
|
||||
with patch.object(anode, "_sendAdmin") as mock_send_admin:
|
||||
anode.factoryReset(full=False)
|
||||
|
||||
assert amesg.factory_reset_config == 1
|
||||
mock_send_admin.assert_called_once_with(amesg, onResponse=anode.onAckNak)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_factoryReset_full_sets_device_field():
|
||||
"""Test factoryReset(full=True) sets the full-device reset protobuf field."""
|
||||
iface = MagicMock(autospec=MeshInterface)
|
||||
anode = Node(iface, 1234567890, noProto=True)
|
||||
|
||||
amesg = admin_pb2.AdminMessage()
|
||||
with patch("meshtastic.node.admin_pb2.AdminMessage", return_value=amesg):
|
||||
with patch.object(anode, "_sendAdmin") as mock_send_admin:
|
||||
anode.factoryReset(full=True)
|
||||
|
||||
assert amesg.factory_reset_device == 1
|
||||
mock_send_admin.assert_called_once_with(amesg, onResponse=anode.onAckNak)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_setURL_empty_url(capsys):
|
||||
"""Test reboot"""
|
||||
@@ -794,6 +825,30 @@ def test_writeConfig_with_no_radioConfig(capsys):
|
||||
assert err == ""
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.usefixtures("reset_mt_config")
|
||||
def test_writeConfig_traffic_management():
|
||||
"""Test writeConfig with traffic_management module config."""
|
||||
iface = MagicMock(autospec=SerialInterface)
|
||||
anode = Node(iface, 123, noProto=True)
|
||||
anode.moduleConfig.traffic_management.enabled = True
|
||||
anode.moduleConfig.traffic_management.rate_limit_enabled = True
|
||||
|
||||
sent_admin = []
|
||||
|
||||
def capture_send(p, *args, **kwargs): # pylint: disable=W0613
|
||||
sent_admin.append(p)
|
||||
|
||||
with patch.object(anode, "_sendAdmin", side_effect=capture_send):
|
||||
anode.writeConfig("traffic_management")
|
||||
|
||||
assert len(sent_admin) == 1
|
||||
assert sent_admin[0].HasField("set_module_config")
|
||||
assert sent_admin[0].set_module_config.HasField("traffic_management")
|
||||
assert sent_admin[0].set_module_config.traffic_management.enabled is True
|
||||
assert sent_admin[0].set_module_config.traffic_management.rate_limit_enabled is True
|
||||
|
||||
|
||||
# TODO
|
||||
# @pytest.mark.unit
|
||||
# def test_writeConfig(caplog):
|
||||
@@ -1550,6 +1605,41 @@ def test_setOwner_valid_names(caplog):
|
||||
assert re.search(r'p.set_owner.short_name:VN:', caplog.text, re.MULTILINE)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_start_ota_local_node():
|
||||
"""Test startOTA on local node"""
|
||||
iface = MagicMock(autospec=MeshInterface)
|
||||
anode = Node(iface, 1234567890, noProto=True)
|
||||
# Set up as local node
|
||||
iface.localNode = anode
|
||||
|
||||
amesg = admin_pb2.AdminMessage()
|
||||
with patch("meshtastic.admin_pb2.AdminMessage", return_value=amesg):
|
||||
with patch.object(anode, "_sendAdmin") as mock_send_admin:
|
||||
test_hash = b"\x01\x02\x03" * 8 # 24 bytes hash
|
||||
anode.startOTA(ota_mode=admin_pb2.OTAMode.OTA_WIFI, ota_file_hash=test_hash)
|
||||
|
||||
# Verify the OTA request was set correctly
|
||||
assert amesg.ota_request.reboot_ota_mode == admin_pb2.OTAMode.OTA_WIFI
|
||||
assert amesg.ota_request.ota_hash == test_hash
|
||||
mock_send_admin.assert_called_once_with(amesg)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_start_ota_remote_node_raises_error():
|
||||
"""Test startOTA on remote node raises ValueError"""
|
||||
iface = MagicMock(autospec=MeshInterface)
|
||||
local_node = Node(iface, 1234567890, noProto=True)
|
||||
remote_node = Node(iface, 9876543210, noProto=True)
|
||||
iface.localNode = local_node
|
||||
|
||||
test_hash = b"\x01\x02\x03" * 8
|
||||
with pytest.raises(ValueError, match="startOTA only possible in local node"):
|
||||
remote_node.startOTA(
|
||||
ota_mode=admin_pb2.OTAMode.OTA_WIFI, ota_file_hash=test_hash
|
||||
)
|
||||
|
||||
|
||||
# TODO
|
||||
# @pytest.mark.unitslow
|
||||
# def test_waitForConfig():
|
||||
|
||||
455
meshtastic/tests/test_ota.py
Normal file
455
meshtastic/tests/test_ota.py
Normal file
@@ -0,0 +1,455 @@
|
||||
"""Meshtastic unit tests for ota.py"""
|
||||
|
||||
import hashlib
|
||||
import logging
|
||||
import os
|
||||
import socket
|
||||
import tempfile
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from meshtastic.ota import (
|
||||
_file_sha256,
|
||||
ESP32WiFiOTA,
|
||||
OTAError,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_file_sha256():
|
||||
"""Test _file_sha256 calculates correct hash"""
|
||||
# Create a temporary file with known content
|
||||
with tempfile.NamedTemporaryFile(mode="wb", delete=False) as f:
|
||||
test_data = b"Hello, World!"
|
||||
f.write(test_data)
|
||||
temp_file = f.name
|
||||
|
||||
try:
|
||||
result = _file_sha256(temp_file)
|
||||
expected_hash = hashlib.sha256(test_data).hexdigest()
|
||||
assert result.hexdigest() == expected_hash
|
||||
finally:
|
||||
os.unlink(temp_file)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_file_sha256_large_file():
|
||||
"""Test _file_sha256 handles files larger than chunk size"""
|
||||
# Create a temporary file with more than 4096 bytes
|
||||
with tempfile.NamedTemporaryFile(mode="wb", delete=False) as f:
|
||||
test_data = b"A" * 8192 # More than 4096 bytes
|
||||
f.write(test_data)
|
||||
temp_file = f.name
|
||||
|
||||
try:
|
||||
result = _file_sha256(temp_file)
|
||||
expected_hash = hashlib.sha256(test_data).hexdigest()
|
||||
assert result.hexdigest() == expected_hash
|
||||
finally:
|
||||
os.unlink(temp_file)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_esp32_wifi_ota_init_file_not_found():
|
||||
"""Test ESP32WiFiOTA raises FileNotFoundError for non-existent file"""
|
||||
with pytest.raises(FileNotFoundError, match="does not exist"):
|
||||
ESP32WiFiOTA("/nonexistent/firmware.bin", "192.168.1.1")
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_esp32_wifi_ota_init_success():
|
||||
"""Test ESP32WiFiOTA initializes correctly with valid file"""
|
||||
with tempfile.NamedTemporaryFile(mode="wb", delete=False) as f:
|
||||
f.write(b"fake firmware data")
|
||||
temp_file = f.name
|
||||
|
||||
try:
|
||||
ota = ESP32WiFiOTA(temp_file, "192.168.1.1", 3232)
|
||||
assert ota._filename == temp_file
|
||||
assert ota._hostname == "192.168.1.1"
|
||||
assert ota._port == 3232
|
||||
assert ota._socket is None
|
||||
# Verify hash is calculated
|
||||
assert ota._file_hash is not None
|
||||
assert len(ota.hash_hex()) == 64 # SHA256 hex is 64 chars
|
||||
finally:
|
||||
os.unlink(temp_file)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_esp32_wifi_ota_init_default_port():
|
||||
"""Test ESP32WiFiOTA uses default port 3232"""
|
||||
with tempfile.NamedTemporaryFile(mode="wb", delete=False) as f:
|
||||
f.write(b"fake firmware data")
|
||||
temp_file = f.name
|
||||
|
||||
try:
|
||||
ota = ESP32WiFiOTA(temp_file, "192.168.1.1")
|
||||
assert ota._port == 3232
|
||||
finally:
|
||||
os.unlink(temp_file)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_esp32_wifi_ota_hash_bytes():
|
||||
"""Test hash_bytes returns correct bytes"""
|
||||
with tempfile.NamedTemporaryFile(mode="wb", delete=False) as f:
|
||||
test_data = b"firmware data"
|
||||
f.write(test_data)
|
||||
temp_file = f.name
|
||||
|
||||
try:
|
||||
ota = ESP32WiFiOTA(temp_file, "192.168.1.1")
|
||||
hash_bytes = ota.hash_bytes()
|
||||
expected_bytes = hashlib.sha256(test_data).digest()
|
||||
assert hash_bytes == expected_bytes
|
||||
assert len(hash_bytes) == 32 # SHA256 is 32 bytes
|
||||
finally:
|
||||
os.unlink(temp_file)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_esp32_wifi_ota_hash_hex():
|
||||
"""Test hash_hex returns correct hex string"""
|
||||
with tempfile.NamedTemporaryFile(mode="wb", delete=False) as f:
|
||||
test_data = b"firmware data"
|
||||
f.write(test_data)
|
||||
temp_file = f.name
|
||||
|
||||
try:
|
||||
ota = ESP32WiFiOTA(temp_file, "192.168.1.1")
|
||||
hash_hex = ota.hash_hex()
|
||||
expected_hex = hashlib.sha256(test_data).hexdigest()
|
||||
assert hash_hex == expected_hex
|
||||
assert len(hash_hex) == 64 # SHA256 hex is 64 chars
|
||||
finally:
|
||||
os.unlink(temp_file)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_esp32_wifi_ota_read_line_not_connected():
|
||||
"""Test _read_line raises ConnectionError when not connected"""
|
||||
with tempfile.NamedTemporaryFile(mode="wb", delete=False) as f:
|
||||
f.write(b"firmware")
|
||||
temp_file = f.name
|
||||
|
||||
try:
|
||||
ota = ESP32WiFiOTA(temp_file, "192.168.1.1")
|
||||
with pytest.raises(ConnectionError, match="Socket not connected"):
|
||||
ota._read_line()
|
||||
finally:
|
||||
os.unlink(temp_file)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_esp32_wifi_ota_read_line_connection_closed():
|
||||
"""Test _read_line raises ConnectionError when connection closed"""
|
||||
with tempfile.NamedTemporaryFile(mode="wb", delete=False) as f:
|
||||
f.write(b"firmware")
|
||||
temp_file = f.name
|
||||
|
||||
try:
|
||||
ota = ESP32WiFiOTA(temp_file, "192.168.1.1")
|
||||
mock_socket = MagicMock()
|
||||
# Simulate connection closed
|
||||
mock_socket.recv.return_value = b""
|
||||
ota._socket = mock_socket
|
||||
|
||||
with pytest.raises(ConnectionError, match="Connection closed"):
|
||||
ota._read_line()
|
||||
finally:
|
||||
os.unlink(temp_file)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_esp32_wifi_ota_read_line_success():
|
||||
"""Test _read_line successfully reads a line"""
|
||||
with tempfile.NamedTemporaryFile(mode="wb", delete=False) as f:
|
||||
f.write(b"firmware")
|
||||
temp_file = f.name
|
||||
|
||||
try:
|
||||
ota = ESP32WiFiOTA(temp_file, "192.168.1.1")
|
||||
mock_socket = MagicMock()
|
||||
# Simulate receiving "OK\n"
|
||||
mock_socket.recv.side_effect = [b"O", b"K", b"\n"]
|
||||
ota._socket = mock_socket
|
||||
|
||||
result = ota._read_line()
|
||||
assert result == "OK"
|
||||
finally:
|
||||
os.unlink(temp_file)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@patch("meshtastic.ota.socket.socket")
|
||||
def test_esp32_wifi_ota_update_success(mock_socket_class):
|
||||
"""Test update() with successful OTA"""
|
||||
with tempfile.NamedTemporaryFile(mode="wb", delete=False) as f:
|
||||
test_data = b"A" * 1024 # 1KB of data
|
||||
f.write(test_data)
|
||||
temp_file = f.name
|
||||
|
||||
try:
|
||||
# Setup mock socket
|
||||
mock_socket = MagicMock()
|
||||
mock_socket_class.return_value = mock_socket
|
||||
|
||||
ota = ESP32WiFiOTA(temp_file, "192.168.1.1")
|
||||
|
||||
# Mock _read_line to return appropriate responses
|
||||
# First call: ERASING, Second call: OK (ready), Third call: OK (complete)
|
||||
with patch.object(ota, "_read_line") as mock_read_line:
|
||||
mock_read_line.side_effect = [
|
||||
"ERASING", # Device is erasing flash
|
||||
"OK", # Device ready for firmware
|
||||
"OK", # Device finished successfully
|
||||
]
|
||||
|
||||
ota.update()
|
||||
|
||||
# Verify socket was created and connected
|
||||
mock_socket_class.assert_called_once_with(
|
||||
socket.AF_INET, socket.SOCK_STREAM
|
||||
)
|
||||
mock_socket.settimeout.assert_called_once_with(15)
|
||||
mock_socket.connect.assert_called_once_with(("192.168.1.1", 3232))
|
||||
|
||||
# Verify start command was sent
|
||||
start_cmd = f"OTA {len(test_data)} {ota.hash_hex()}\n".encode("utf-8")
|
||||
mock_socket.sendall.assert_any_call(start_cmd)
|
||||
|
||||
# Verify firmware was sent (at least one chunk)
|
||||
assert mock_socket.sendall.call_count >= 2
|
||||
|
||||
# Verify socket was closed
|
||||
mock_socket.close.assert_called_once()
|
||||
|
||||
finally:
|
||||
os.unlink(temp_file)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@patch("meshtastic.ota.socket.socket")
|
||||
def test_esp32_wifi_ota_update_with_progress_callback(mock_socket_class):
|
||||
"""Test update() with progress callback"""
|
||||
with tempfile.NamedTemporaryFile(mode="wb", delete=False) as f:
|
||||
test_data = b"A" * 1024 # 1KB of data
|
||||
f.write(test_data)
|
||||
temp_file = f.name
|
||||
|
||||
try:
|
||||
# Setup mock socket
|
||||
mock_socket = MagicMock()
|
||||
mock_socket_class.return_value = mock_socket
|
||||
|
||||
ota = ESP32WiFiOTA(temp_file, "192.168.1.1")
|
||||
|
||||
# Track progress callback calls
|
||||
progress_calls = []
|
||||
|
||||
def progress_callback(sent, total):
|
||||
progress_calls.append((sent, total))
|
||||
|
||||
# Mock _read_line
|
||||
with patch.object(ota, "_read_line") as mock_read_line:
|
||||
mock_read_line.side_effect = [
|
||||
"OK", # Device ready
|
||||
"OK", # Device finished
|
||||
]
|
||||
|
||||
ota.update(progress_callback=progress_callback)
|
||||
|
||||
# Verify progress callback was called
|
||||
assert len(progress_calls) > 0
|
||||
# First call should show some progress
|
||||
assert progress_calls[0][0] > 0
|
||||
# Total should be the firmware size
|
||||
assert progress_calls[0][1] == len(test_data)
|
||||
# Last call should show all bytes sent
|
||||
assert progress_calls[-1][0] == len(test_data)
|
||||
|
||||
finally:
|
||||
os.unlink(temp_file)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@patch("meshtastic.ota.socket.socket")
|
||||
def test_esp32_wifi_ota_update_device_error_on_start(mock_socket_class):
|
||||
"""Test update() raises OTAError when device reports error during start"""
|
||||
with tempfile.NamedTemporaryFile(mode="wb", delete=False) as f:
|
||||
f.write(b"firmware")
|
||||
temp_file = f.name
|
||||
|
||||
try:
|
||||
mock_socket = MagicMock()
|
||||
mock_socket_class.return_value = mock_socket
|
||||
|
||||
ota = ESP32WiFiOTA(temp_file, "192.168.1.1")
|
||||
|
||||
with patch.object(ota, "_read_line") as mock_read_line:
|
||||
mock_read_line.return_value = "ERR BAD_HASH"
|
||||
|
||||
with pytest.raises(OTAError, match="Device reported error"):
|
||||
ota.update()
|
||||
|
||||
finally:
|
||||
os.unlink(temp_file)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@patch("meshtastic.ota.socket.socket")
|
||||
def test_esp32_wifi_ota_update_device_error_on_finish(mock_socket_class):
|
||||
"""Test update() raises OTAError when device reports error after firmware sent"""
|
||||
with tempfile.NamedTemporaryFile(mode="wb", delete=False) as f:
|
||||
f.write(b"firmware")
|
||||
temp_file = f.name
|
||||
|
||||
try:
|
||||
mock_socket = MagicMock()
|
||||
mock_socket_class.return_value = mock_socket
|
||||
|
||||
ota = ESP32WiFiOTA(temp_file, "192.168.1.1")
|
||||
|
||||
with patch.object(ota, "_read_line") as mock_read_line:
|
||||
mock_read_line.side_effect = [
|
||||
"OK", # Device ready
|
||||
"ERR FLASH_ERR", # Error after firmware sent
|
||||
]
|
||||
|
||||
with pytest.raises(OTAError, match="OTA update failed"):
|
||||
ota.update()
|
||||
|
||||
finally:
|
||||
os.unlink(temp_file)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@patch("meshtastic.ota.socket.socket")
|
||||
def test_esp32_wifi_ota_update_socket_cleanup_on_error(mock_socket_class):
|
||||
"""Test that socket is properly cleaned up on error"""
|
||||
with tempfile.NamedTemporaryFile(mode="wb", delete=False) as f:
|
||||
f.write(b"firmware")
|
||||
temp_file = f.name
|
||||
|
||||
try:
|
||||
mock_socket = MagicMock()
|
||||
mock_socket_class.return_value = mock_socket
|
||||
|
||||
ota = ESP32WiFiOTA(temp_file, "192.168.1.1")
|
||||
|
||||
# Simulate connection error
|
||||
mock_socket.connect.side_effect = ConnectionRefusedError("Connection refused")
|
||||
|
||||
with pytest.raises(ConnectionRefusedError):
|
||||
ota.update()
|
||||
|
||||
# Verify socket was closed even on error
|
||||
mock_socket.close.assert_called_once()
|
||||
assert ota._socket is None
|
||||
|
||||
finally:
|
||||
os.unlink(temp_file)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@patch("meshtastic.ota.socket.socket")
|
||||
def test_esp32_wifi_ota_update_large_firmware(mock_socket_class):
|
||||
"""Test update() correctly chunks large firmware files"""
|
||||
with tempfile.NamedTemporaryFile(mode="wb", delete=False) as f:
|
||||
# Create a file larger than chunk_size (1024)
|
||||
test_data = b"B" * 3000
|
||||
f.write(test_data)
|
||||
temp_file = f.name
|
||||
|
||||
try:
|
||||
mock_socket = MagicMock()
|
||||
mock_socket_class.return_value = mock_socket
|
||||
|
||||
ota = ESP32WiFiOTA(temp_file, "192.168.1.1")
|
||||
|
||||
with patch.object(ota, "_read_line") as mock_read_line:
|
||||
mock_read_line.side_effect = [
|
||||
"OK", # Device ready
|
||||
"OK", # Device finished
|
||||
]
|
||||
|
||||
ota.update()
|
||||
|
||||
# Verify that all data was sent in chunks
|
||||
# 3000 bytes should be sent in ~3 chunks of 1024 bytes
|
||||
sendall_calls = [
|
||||
call
|
||||
for call in mock_socket.sendall.call_args_list
|
||||
if call[0][0]
|
||||
!= f"OTA {len(test_data)} {ota.hash_hex()}\n".encode("utf-8")
|
||||
]
|
||||
# Calculate total data sent (excluding the start command)
|
||||
total_sent = sum(len(call[0][0]) for call in sendall_calls)
|
||||
assert total_sent == len(test_data)
|
||||
|
||||
finally:
|
||||
os.unlink(temp_file)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@patch("meshtastic.ota.socket.socket")
|
||||
def test_esp32_wifi_ota_update_unexpected_response_warning(mock_socket_class, caplog):
|
||||
"""Test update() logs warning on unexpected response during startup"""
|
||||
with tempfile.NamedTemporaryFile(mode="wb", delete=False) as f:
|
||||
f.write(b"firmware")
|
||||
temp_file = f.name
|
||||
|
||||
try:
|
||||
mock_socket = MagicMock()
|
||||
mock_socket_class.return_value = mock_socket
|
||||
|
||||
ota = ESP32WiFiOTA(temp_file, "192.168.1.1")
|
||||
|
||||
with patch.object(ota, "_read_line") as mock_read_line:
|
||||
mock_read_line.side_effect = [
|
||||
"UNKNOWN", # Unexpected response
|
||||
"OK", # Then proceed
|
||||
"OK", # Device finished
|
||||
]
|
||||
|
||||
with caplog.at_level(logging.WARNING):
|
||||
ota.update()
|
||||
|
||||
# Check that warning was logged for unexpected response
|
||||
assert "Unexpected response" in caplog.text
|
||||
|
||||
finally:
|
||||
os.unlink(temp_file)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@patch("meshtastic.ota.socket.socket")
|
||||
def test_esp32_wifi_ota_update_unexpected_final_response(mock_socket_class, caplog):
|
||||
"""Test update() logs warning on unexpected final response after firmware upload"""
|
||||
with tempfile.NamedTemporaryFile(mode="wb", delete=False) as f:
|
||||
f.write(b"firmware")
|
||||
temp_file = f.name
|
||||
|
||||
try:
|
||||
mock_socket = MagicMock()
|
||||
mock_socket_class.return_value = mock_socket
|
||||
|
||||
ota = ESP32WiFiOTA(temp_file, "192.168.1.1")
|
||||
|
||||
with patch.object(ota, "_read_line") as mock_read_line:
|
||||
mock_read_line.side_effect = [
|
||||
"OK", # Device ready for firmware
|
||||
"UNKNOWN", # Unexpected final response (not OK, not ERR, not ACK)
|
||||
"OK", # Then succeed
|
||||
]
|
||||
|
||||
with caplog.at_level(logging.WARNING):
|
||||
ota.update()
|
||||
|
||||
# Check that warning was logged for unexpected final response
|
||||
assert "Unexpected final response" in caplog.text
|
||||
|
||||
finally:
|
||||
os.unlink(temp_file)
|
||||
@@ -15,7 +15,115 @@ def test_StreamInterface():
|
||||
"""Test that we cannot instantiate a StreamInterface based on noProto"""
|
||||
with pytest.raises(Exception) as pytest_wrapped_e:
|
||||
StreamInterface()
|
||||
assert pytest_wrapped_e.type == Exception
|
||||
assert pytest_wrapped_e.type == RuntimeError
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.usefixtures("reset_mt_config")
|
||||
def test_StreamInterface_close_safe_when_thread_never_started():
|
||||
"""close() must not raise RuntimeError when called before connect() has started the reader.
|
||||
|
||||
Hits the cleanup path used by __init__ when the handshake raises before the
|
||||
reader thread is started.
|
||||
"""
|
||||
iface = StreamInterface(noProto=True, connectNow=False)
|
||||
iface.stream = MagicMock()
|
||||
# _rxThread was created in __init__ but never .start()'d. close() should
|
||||
# detect that and skip join() instead of raising RuntimeError.
|
||||
iface.close()
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.usefixtures("reset_mt_config")
|
||||
def test_StreamInterface_close_when_thread_never_started_closes_stream():
|
||||
"""If no reader thread was started, close() should still close the stream."""
|
||||
iface = StreamInterface(noProto=True, connectNow=False)
|
||||
stream = MagicMock()
|
||||
iface.stream = stream
|
||||
iface.close()
|
||||
stream.close.assert_called_once()
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.usefixtures("reset_mt_config")
|
||||
def test_StreamInterface_init_cleans_up_when_connect_raises():
|
||||
"""If connect() raises during __init__, close() runs and the original exception propagates."""
|
||||
|
||||
cleanup_calls = []
|
||||
|
||||
class FailingConnectStream(StreamInterface):
|
||||
"""Subclass whose connect() raises, to exercise the __init__ cleanup path."""
|
||||
|
||||
def __init__(self):
|
||||
self.stream = MagicMock() # bypass StreamInterface abstract check
|
||||
super().__init__(noProto=False, connectNow=True)
|
||||
|
||||
def connect(self):
|
||||
raise RuntimeError("simulated handshake failure")
|
||||
|
||||
def close(self):
|
||||
cleanup_calls.append("close")
|
||||
super().close()
|
||||
|
||||
with pytest.raises(RuntimeError, match="simulated handshake failure"):
|
||||
FailingConnectStream()
|
||||
assert cleanup_calls == ["close"], "close() should be invoked exactly once on handshake failure"
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.usefixtures("reset_mt_config")
|
||||
def test_StreamInterface_init_cleans_up_when_waitForConfig_raises():
|
||||
"""If waitForConfig() raises after a successful connect(), close() runs and exception propagates."""
|
||||
|
||||
cleanup_calls = []
|
||||
|
||||
class FailingWaitStream(StreamInterface):
|
||||
"""Subclass whose waitForConfig() raises, to exercise the second leg of cleanup."""
|
||||
|
||||
def __init__(self):
|
||||
self.stream = MagicMock()
|
||||
super().__init__(noProto=False, connectNow=True)
|
||||
|
||||
def connect(self):
|
||||
# No-op connect — we are simulating handshake-stage failure, not connect-stage.
|
||||
pass
|
||||
|
||||
def waitForConfig(self):
|
||||
raise TimeoutError("simulated config-handshake timeout")
|
||||
|
||||
def close(self):
|
||||
cleanup_calls.append("close")
|
||||
super().close()
|
||||
|
||||
with pytest.raises(TimeoutError, match="simulated config-handshake timeout"):
|
||||
FailingWaitStream()
|
||||
assert cleanup_calls == ["close"], "close() should be invoked exactly once on handshake timeout"
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.usefixtures("reset_mt_config")
|
||||
def test_StreamInterface_init_cleanup_does_not_shadow_original_exception():
|
||||
"""If close() itself raises during __init__ cleanup, the original exception still propagates.
|
||||
|
||||
The cleanup uses contextlib.suppress(Exception) so that a secondary failure
|
||||
in close() doesn't replace the real reason for the failed handshake.
|
||||
"""
|
||||
|
||||
class CleanupRaisesStream(StreamInterface):
|
||||
"""Helper stream that raises during close() to verify exception precedence."""
|
||||
|
||||
def __init__(self):
|
||||
self.stream = MagicMock()
|
||||
super().__init__(noProto=False, connectNow=True)
|
||||
|
||||
def connect(self):
|
||||
raise RuntimeError("original handshake failure")
|
||||
|
||||
def close(self):
|
||||
raise RuntimeError("secondary close failure — should be suppressed")
|
||||
|
||||
with pytest.raises(RuntimeError, match="original handshake failure"):
|
||||
CleanupRaisesStream()
|
||||
|
||||
|
||||
# Note: This takes a bit, so moving from unit to slow
|
||||
|
||||
2
poetry.lock
generated
2
poetry.lock
generated
@@ -5941,4 +5941,4 @@ tunnel = ["pytap2"]
|
||||
[metadata]
|
||||
lock-version = "2.1"
|
||||
python-versions = "^3.9,<3.15"
|
||||
content-hash = "e87e2eaffca4ad13aa7e1b8622ec4b37b23a4efe1f4febe0ca87b92db5fe6d1e"
|
||||
content-hash = "674308d6eb7c3730031cc3e73c98b2413c7f59002a9317bfad387bc34a17c64d"
|
||||
|
||||
1
protobufs
Submodule
1
protobufs
Submodule
Submodule protobufs added at dd6c3f850a
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "meshtastic"
|
||||
version = "2.7.7"
|
||||
version = "2.7.8"
|
||||
description = "Python API & client shell for talking to Meshtastic devices"
|
||||
authors = ["Meshtastic Developers <contact@meshtastic.org>"]
|
||||
license = "GPL-3.0-only"
|
||||
@@ -15,7 +15,7 @@ requests = "^2.31.0"
|
||||
pyyaml = "^6.0.1"
|
||||
pypubsub = "^4.0.3"
|
||||
bleak = ">=0.22.3"
|
||||
packaging = "^24.0"
|
||||
packaging = ">=24.0"
|
||||
argcomplete = { version = "^3.5.2", optional = true }
|
||||
pyqrcode = { version = "^1.2.1", optional = true }
|
||||
dotmap = { version = "^1.3.30", optional = true }
|
||||
|
||||
Reference in New Issue
Block a user