diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..d83d076b --- /dev/null +++ b/.dockerignore @@ -0,0 +1,10 @@ +# This keeps Docker from including hostOS virtual environment folders +env/ +.venv/ + +# Database files and backups +*.db +*.db-shm +*.db-wal +backups/ +*.db.gz diff --git a/.github/workflows/container.yml b/.github/workflows/container.yml new file mode 100644 index 00000000..b2ace5d0 --- /dev/null +++ b/.github/workflows/container.yml @@ -0,0 +1,52 @@ +name: Build container + +on: + push: + +jobs: + docker: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Docker meta + id: meta + uses: docker/metadata-action@v5 + with: + # list of Docker images to use as base name for tags + images: | + ghcr.io/${{ github.repository }} + # generate Docker tags based on the following events/attributes + tags: | + type=ref,event=branch + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + type=match,pattern=v\d.\d.\d,value=latest + - name: Login to GitHub Container Registry + if: github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build and push + uses: docker/build-push-action@v6 + with: + context: . + file: ./Containerfile + push: ${{ github.event_name != 'pull_request' }} + labels: ${{ steps.meta.outputs.labels }} + tags: ${{ steps.meta.outputs.tags }} + platforms: linux/amd64,linux/arm64 + # optional cache (speeds up rebuilds) + cache-from: type=gha + cache-to: type=gha,mode=max \ No newline at end of file diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 00000000..01ce2d82 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,39 @@ +name: Ruff + +on: + pull_request: + paths: + - "**/*.py" + - "pyproject.toml" + - "ruff.toml" + - ".pre-commit-config.yaml" + +jobs: + ruff: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Cache Ruff + uses: actions/cache@v4 + with: + path: ~/.cache/ruff + key: ruff-${{ runner.os }}-${{ hashFiles('**/pyproject.toml', '**/ruff.toml') }} + + - name: Install Ruff + run: pip install "ruff==0.13.3" + + # Lint (with GitHub annotation format for inline PR messages) + - name: Ruff check + run: ruff check --output-format=github . + + # Fail PR if formatting is needed + - name: Ruff format (check-only) + run: ruff format --check . + + # TODO: Investigate only applying to changed files and possibly apply fixes \ No newline at end of file diff --git a/.gitignore b/.gitignore index 28df5ec6..edcd6325 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,48 @@ env/* __pycache__/* +meshview/__pycache__/* +alembic/__pycache__/* +meshtastic/protobuf/* + +# Database files packets.db +packets*.db +*.db +*.db-shm +*.db-wal + +# Database backups +backups/ +*.db.gz + +# Process files +meshview-db.pid +meshview-web.pid + +# Config and logs +/table_details.py +config.ini +*.log + +# Screenshots +screenshots/* + +# Python +python/nanopb +__pycache__/ +*.pyc +*.pyo +*.pyd +.Python + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# OS +.DS_Store +Thumbs.db +packets.db-journal diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/.gitmodules @@ -0,0 +1 @@ + diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..fef2f29b --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,8 @@ +repos: + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.13.3 # pin the latest you’re comfortable with + hooks: + - id: ruff + args: [--fix, --exit-non-zero-on-fix] # fail if it had to change files + - id: ruff-format + \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..a3c5373a --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,204 @@ +# AI Agent Guidelines for Meshview + +This document provides context and guidelines for AI coding assistants working on the Meshview project. + +## Project Overview + +Meshview is a real-time monitoring and diagnostic tool for Meshtastic mesh networks. It provides web-based visualization and analysis of network activity, including: + +- Real-time packet monitoring from MQTT streams +- Interactive map visualization of node locations +- Network topology graphs showing connectivity +- Message traffic analysis and conversation tracking +- Node statistics and telemetry data +- Packet inspection and traceroute analysis + +## Architecture + +### Core Components + +1. **MQTT Reader** (`meshview/mqtt_reader.py`) - Subscribes to MQTT topics and receives mesh packets +2. **Database Manager** (`meshview/database.py`, `startdb.py`) - Handles database initialization and migrations +3. **MQTT Store** (`meshview/mqtt_store.py`) - Processes and stores packets in the database +4. **Web Server** (`meshview/web.py`, `main.py`) - Serves the web interface and API endpoints +5. **API Layer** (`meshview/web_api/api.py`) - REST API endpoints for data access +6. **Models** (`meshview/models.py`) - SQLAlchemy database models +7. **Decode Payload** (`meshview/decode_payload.py`) - Protobuf message decoding + +### Technology Stack + +- **Python 3.13+** - Main language +- **aiohttp** - Async web framework +- **aiomqtt** - Async MQTT client +- **SQLAlchemy (async)** - ORM with async support +- **Alembic** - Database migrations +- **Jinja2** - Template engine +- **Protobuf** - Message serialization (Meshtastic protocol) +- **SQLite/PostgreSQL** - Database backends (SQLite default, PostgreSQL via asyncpg) + +### Key Patterns + +- **Async/Await** - All I/O operations are asynchronous +- **Database Migrations** - Use Alembic for schema changes (see `docs/Database-Changes-With-Alembic.md`) +- **Configuration** - INI file-based config (`config.ini`, see `sample.config.ini`) +- **Modular API** - API routes separated into `meshview/web_api/` module + +## Project Structure + +``` +meshview/ +├── alembic/ # Database migration scripts +├── docs/ # Technical documentation +├── meshview/ # Main application package +│ ├── static/ # Static web assets (HTML, JS, CSS) +│ ├── templates/ # Jinja2 HTML templates +│ ├── web_api/ # API route handlers +│ └── *.py # Core modules +├── main.py # Web server entry point +├── startdb.py # Database manager entry point +├── mvrun.py # Combined runner (starts both services) +├── config.ini # Runtime configuration +└── requirements.txt # Python dependencies +``` + +## Development Workflow + +### Setup + +1. Use Python 3.13+ virtual environment + +### Running + +- **Database**: `./env/bin/python startdb.py` +- **Web Server**: `./env/bin/python main.py` +- **Both**: `./env/bin/python mvrun.py` + + +## Code Style + +- **Line length**: 100 characters (see `pyproject.toml`) +- **Linting**: Ruff (configured in `pyproject.toml`) +- **Formatting**: Ruff formatter +- **Type hints**: Preferred but not strictly required +- **Async**: Use `async def` and `await` for I/O operations + +## Important Files + +### Configuration +- `config.ini` - Runtime configuration (server, MQTT, database, cleanup) +- `sample.config.ini` - Template configuration file +- `alembic.ini` - Alembic migration configuration + +### Database +- `meshview/models.py` - SQLAlchemy models (Packet, Node, Traceroute, etc.) +- `meshview/database.py` - Database initialization and session management +- `alembic/versions/` - Migration scripts + +### Core Logic +- `meshview/mqtt_reader.py` - MQTT subscription and message reception +- `meshview/mqtt_store.py` - Packet processing and storage +- `meshview/decode_payload.py` - Protobuf decoding +- `meshview/web.py` - Web server routes and handlers +- `meshview/web_api/api.py` - REST API endpoints + +### Templates +- `meshview/templates/` - Jinja2 HTML templates +- `meshview/static/` - Static files (HTML pages, JS, CSS) + +## Common Tasks + +### Adding a New API Endpoint + +1. Add route handler in `meshview/web_api/api.py` +2. Register route in `meshview/web.py` (if needed) +3. Update `docs/API_Documentation.md` if public API + +### Database Schema Changes + +1. Modify models in `meshview/models.py` +2. Create migration: `alembic revision --autogenerate -m "description"` +3. Review generated migration in `alembic/versions/` +4. Test migration: `alembic upgrade head` +5. **Never** modify existing migration files after they've been applied + +### Adding a New Web Page + +1. Create template in `meshview/templates/` +2. Add route in `meshview/web.py` +3. Add navigation link if needed (check existing templates for pattern) +4. Add static assets if needed in `meshview/static/` + +### Processing New Packet Types + +1. Check `meshview/decode_payload.py` for existing decoders +2. Add decoder function if new type +3. Update `meshview/mqtt_store.py` to handle new packet type +4. Update database models if new data needs storage + + +## Key Concepts + +### Meshtastic Protocol +- Uses Protobuf for message serialization +- Packets contain various message types (text, position, telemetry, etc.) +- MQTT topics follow pattern: `msh/{region}/{subregion}/#` + +### Database Schema +- **packet** - Raw packet data +- **node** - Mesh node information +- **traceroute** - Network path information +- **packet_seen** - Packet observation records + +### Real-time Updates +- Web pages use Server-Sent Events (SSE) for live updates +- Map and firehose pages auto-refresh based on config intervals +- API endpoints return JSON for programmatic access + +## Best Practices + +1. **Always use async/await** for database and network operations +2. **Use Alembic** for all database schema changes +3. **Follow existing patterns** - check similar code before adding new features +4. **Update documentation** - keep `docs/` and README current +5. **Test migrations** - verify migrations work both up and down +6. **Handle errors gracefully** - log errors, don't crash on bad packets +7. **Respect configuration** - use `config.ini` values, don't hardcode + +## Common Pitfalls + +- **Don't modify applied migrations** - create new ones instead +- **Don't block the event loop** - use async I/O, not sync +- **Don't forget timezone handling** - timestamps are stored in UTC +- **Don't hardcode paths** - use configuration values +- **Don't ignore MQTT reconnection** - handle connection failures gracefully + +## Resources + +- **Main README**: `README.md` - Installation and basic usage +- **Docker Guide**: `README-Docker.md` - Container deployment +- **API Docs**: `docs/API_Documentation.md` - API endpoint reference +- **Migration Guide**: `docs/Database-Changes-With-Alembic.md` - Database workflow +- **Contributing**: `CONTRIBUTING.md` - Contribution guidelines + +## Version Information + +- **Current Version**: 3.0.0 (November 2025) +- **Python Requirement**: 3.13+ +- **Key Features**: Alembic migrations, automated backups, Docker support, traceroute return paths + + +## Rules for robots +- Always run ruff check and ruff format after making changes (only on python changes) + + +--- + +When working on this project, prioritize: +1. Maintaining async patterns +2. Following existing code structure +3. Using proper database migrations +4. Keeping documentation updated +5. Testing changes thoroughly + + + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..528a8c7b --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,133 @@ +# Contributing to Meshview + +First off, thanks for taking the time to contribute! ❤️ +All types of contributions are encouraged and valued. See the [Table of Contents](#table-of-contents) for ways to help and details about how this project handles contributions. Please read the relevant section before getting started — it will make things smoother for both you and the maintainers. + +The Meshview community looks forward to your contributions. 🎉 + +> And if you like the project but don’t have time to contribute code, that’s fine! You can still support Meshview by: +> - ⭐ Starring the repo on GitHub +> - Talking about Meshview on social media +> - Referencing Meshview in your own project’s README +> - Mentioning Meshview at local meetups or to colleagues/friends + +--- + +## Table of Contents + +- [Code of Conduct](#code-of-conduct) +- [I Have a Question](#i-have-a-question) +- [I Want to Contribute](#i-want-to-contribute) + - [Reporting Bugs](#reporting-bugs) + - [Suggesting Enhancements](#suggesting-enhancements) + - [Your First Code Contribution](#your-first-code-contribution) + - [Improving the Documentation](#improving-the-documentation) +- [Styleguides](#styleguides) + - [Commit Messages](#commit-messages) +- [Join the Project Team](#join-the-project-team) + +--- + +## Code of Conduct + +Meshview is an open and welcoming community. We want everyone to feel safe, respected, and valued. + +### Our Standards +- Be respectful and considerate in all interactions. +- Welcome new contributors and help them learn. +- Provide constructive feedback, not personal attacks. +- Focus on collaboration and what benefits the community. + +Unacceptable behavior includes harassment, insults, hate speech, personal attacks, or publishing others’ private information without permission. + +--- + +## I Have a Question + +> Before asking, please read the [documentation](docs/README.md) if available. + +1. Search the [issues list](../../issues) to see if your question has already been asked. +2. If not, open a [new issue](../../issues/new) with the **question** label. +3. Provide as much context as possible (OS, Python version, database type, etc.). + +--- + +## I Want to Contribute + +### Legal Notice +By contributing to Meshview, you agree that: +- You authored the content yourself. +- You have the necessary rights to the content. +- Your contribution can be provided under the project’s license. + +--- + +### Reporting Bugs + +Before submitting a bug report: +- Make sure you’re using the latest Meshview version. +- Verify the issue is not due to a misconfigured environment (SQLite/MySQL, Python version, etc.). +- Search existing [bug reports](../../issues?q=label%3Abug). +- Collect relevant information: + - Steps to reproduce + - Error messages / stack traces + - OS, Python version, and database backend + - Any logs (`meshview-db.service`, `mqtt_reader.py`, etc.) + +How to report: +- Open a [new issue](../../issues/new). +- Use a **clear and descriptive title**. +- Include reproduction steps and expected vs. actual behavior. + +⚠️ Security issues should **not** be reported in public issues. Instead, email us at **meshview-maintainers@proton.me**. + +--- + +### Suggesting Enhancements + +Enhancements are tracked as [issues](../../issues). Before suggesting: +- Make sure the feature doesn’t already exist. +- Search for prior suggestions. +- Check that it fits Meshview’s scope (mesh packet analysis, visualization, telemetry, etc.). + +When submitting: +- Use a **clear and descriptive title**. +- Describe the current behavior and what you’d like to see instead. +- Include examples, screenshots, or mockups if relevant. +- Explain why it would be useful to most Meshview users. + +--- + +### Your First Code Contribution + +We love first-time contributors! 🚀 + +If you’d like to start coding: +1. Look for issues tagged with [good first issue](../../issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22). +2. Fork the repository and clone it locally. +3. Set up the development environment: +4. Run the app locally +5. Create a new branch, make your changes, commit, and push. +6. Open a pull request! + +--- + +### Improving the Documentation + +Docs are just as important as code. You can help by: +- Fixing typos or broken links. +- Clarifying confusing instructions. +- Adding examples (e.g., setting up Nginx as a reverse proxy, SQLite vs. MySQL setup). +- Writing or updating tutorials. + +--- + +## Join the Project Team + +Meshview is a community-driven project. If you consistently contribute (code, documentation, or community help), we’d love to invite you as a maintainer. + +Start by contributing regularly, engaging in issues/PRs, and helping others. + +--- + +✨ That’s it! Thanks again for being part of Meshview. Every contribution matters. \ No newline at end of file diff --git a/Containerfile b/Containerfile new file mode 100644 index 00000000..8ea19918 --- /dev/null +++ b/Containerfile @@ -0,0 +1,80 @@ +# Build Image +# Uses python:3.13-slim because no native dependencies are needed for meshview itself +# (everything is available as a wheel) + +FROM docker.io/python:3.13-slim AS meshview-build +RUN apt-get update && \ + apt-get install -y --no-install-recommends curl patch && \ + rm -rf /var/lib/apt/lists/* + +# Add a non-root user/group +ARG APP_USER=app +RUN useradd -m -u 10001 -s /bin/bash ${APP_USER} + +# Install uv and put it on PATH system-wide +RUN curl -LsSf https://astral.sh/uv/install.sh | sh \ + && install -m 0755 /root/.local/bin/uv /usr/local/bin/uv + +WORKDIR /app +RUN chown -R ${APP_USER}:${APP_USER} /app + +# Copy deps first for caching +COPY --chown=${APP_USER}:${APP_USER} pyproject.toml uv.lock* requirements*.txt ./ + +# Optional: wheels-only to avoid slow source builds +ENV UV_NO_BUILD=1 +RUN uv venv /opt/venv +# RUN uv sync --frozen +ENV VIRTUAL_ENV=/opt/venv +ENV PATH="$VIRTUAL_ENV/bin:$PATH" + +RUN uv pip install --no-cache-dir --upgrade pip \ + && if [ -f requirements.txt ]; then uv pip install --only-binary=:all: -r requirements.txt; fi + +# Copy app code +COPY --chown=${APP_USER}:${APP_USER} . . + +# Patch config +RUN patch sample.config.ini < container/config.patch + +# Clean +RUN rm -rf /app/.git* && \ + rm -rf /app/.pre-commit-config.yaml && \ + rm -rf /app/*.md && \ + rm -rf /app/COPYING && \ + rm -rf /app/Containerfile && \ + rm -rf /app/Dockerfile && \ + rm -rf /app/container && \ + rm -rf /app/docker && \ + rm -rf /app/docs && \ + rm -rf /app/pyproject.toml && \ + rm -rf /app/requirements.txt && \ + rm -rf /app/screenshots + +# Prepare /app and /opt to copy +RUN mkdir -p /meshview && \ + mv /app /opt /meshview + +# Use a clean container for install +FROM docker.io/python:3.13-slim +ARG APP_USER=app +COPY --from=meshview-build /meshview / +RUN apt-get update && \ + apt-get install -y --no-install-recommends graphviz && \ + rm -rf /var/lib/apt/lists/* && \ + useradd -m -u 10001 -s /bin/bash ${APP_USER} && \ + mkdir -p /etc/meshview /var/lib/meshview /var/log/meshview && \ + mv /app/sample.config.ini /etc/meshview/config.ini && \ + chown -R ${APP_USER}:${APP_USER} /var/lib/meshview /var/log/meshview + +# Drop privileges +USER ${APP_USER} + +WORKDIR /app + +ENTRYPOINT [ "/opt/venv/bin/python", "mvrun.py"] +CMD ["--pid_dir", "/tmp", "--py_exec", "/opt/venv/bin/python", "--config", "/etc/meshview/config.ini" ] + +EXPOSE 8081 +VOLUME [ "/etc/meshview", "/var/lib/meshview", "/var/log/meshview" ] + diff --git a/Dockerfile b/Dockerfile new file mode 120000 index 00000000..5240dc01 --- /dev/null +++ b/Dockerfile @@ -0,0 +1 @@ +Containerfile \ No newline at end of file diff --git a/README b/README deleted file mode 100644 index ed8da4c0..00000000 --- a/README +++ /dev/null @@ -1,19 +0,0 @@ -Meshview -======== - -This project watches a MQTT topic for meshtastic messages, imports them to a -database and has a web UI to view them. - -Example -------- -An example instance, https://meshview.armooo.net, is running with with data -from the MQTT topic msh/US/bayarea/#. - - -Running -------- -$ python3 -m venv env -$ ./env/bin/pip install -r requirements.txt -$ ./env/bin/python main.py - -Now you can hit http://localhost:8080/ diff --git a/README-Docker.md b/README-Docker.md new file mode 100644 index 00000000..5a929cac --- /dev/null +++ b/README-Docker.md @@ -0,0 +1,247 @@ +# Running MeshView with Docker + +MeshView container images are built automatically and published to GitHub Container Registry. + +## Quick Start + +Pull and run the latest image: + +```bash +docker pull ghcr.io/pablorevilla-meshtastic/meshview:latest + +docker run -d \ + --name meshview \ + -p 8081:8081 \ + -v ./config:/etc/meshview \ + -v ./data:/var/lib/meshview \ + -v ./logs:/var/log/meshview \ + ghcr.io/pablorevilla-meshtastic/meshview:latest +``` + +Access the web interface at: http://localhost:8081 + +## Volume Mounts + +The container uses three volumes for persistent data: + +| Volume | Purpose | Required | +|--------|---------|----------| +| `/etc/meshview` | Configuration files | Yes | +| `/var/lib/meshview` | Database storage | Recommended | +| `/var/log/meshview` | Log files | Optional | + +### Configuration Volume + +Mount a directory containing your `config.ini` file: + +```bash +-v /path/to/your/config:/etc/meshview +``` + +If no config is provided, the container will use the default `sample.config.ini`. + +### Database Volume + +Mount a directory to persist the SQLite database: + +```bash +-v /path/to/your/data:/var/lib/meshview +``` + +**Important:** Without this mount, your database will be lost when the container stops. + +### Logs Volume + +Mount a directory to access logs from the host: + +```bash +-v /path/to/your/logs:/var/log/meshview +``` + +## Complete Example + +Create a directory structure and run: + +```bash +# Create directories +mkdir -p meshview/{config,data,logs,backups} + +# Copy sample config (first time only) +docker run --rm ghcr.io/pablorevilla-meshtastic/meshview:latest \ + cat /etc/meshview/config.ini > meshview/config/config.ini + +# Edit config.ini with your MQTT settings +nano meshview/config/config.ini + +# Run the container +docker run -d \ + --name meshview \ + --restart unless-stopped \ + -p 8081:8081 \ + -v $(pwd)/meshview/config:/etc/meshview \ + -v $(pwd)/meshview/data:/var/lib/meshview \ + -v $(pwd)/meshview/logs:/var/log/meshview \ + ghcr.io/pablorevilla-meshtastic/meshview:latest +``` + +## Docker Compose + +Create a `docker-compose.yml`: + +```yaml +version: '3.8' + +services: + meshview: + image: ghcr.io/pablorevilla-meshtastic/meshview:latest + container_name: meshview + restart: unless-stopped + ports: + - "8081:8081" + volumes: + - ./config:/etc/meshview + - ./data:/var/lib/meshview + - ./logs:/var/log/meshview + - ./backups:/var/lib/meshview/backups # For database backups + environment: + - TZ=America/Los_Angeles # Set your timezone +``` + +Run with: + +```bash +docker-compose up -d +``` + +## Configuration + +### Minimum Configuration + +Edit your `config.ini` to configure MQTT connection: + +```ini +[mqtt] +server = mqtt.meshtastic.org +topics = ["msh/US/#"] +port = 1883 +username = +password = + +[database] +# SQLAlchemy async connection string. +# Examples: +# sqlite+aiosqlite:///var/lib/meshview/packets.db +# postgresql+asyncpg://user:pass@host:5432/meshview +connection_string = sqlite+aiosqlite:///var/lib/meshview/packets.db +``` + +### Database Backups + +To enable automatic daily backups inside the container: + +```ini +[cleanup] +backup_enabled = True +backup_dir = /var/lib/meshview/backups +backup_hour = 2 +backup_minute = 00 +``` + +Then mount the backups directory: + +```bash +-v $(pwd)/meshview/backups:/var/lib/meshview/backups +``` + +## Available Tags + +| Tag | Description | +|-----|-------------| +| `latest` | Latest build from the main branch | +| `dev-v3` | Development branch | +| `v1.2.3` | Specific version tags | + +## Updating + +Pull the latest image and restart: + +```bash +docker pull ghcr.io/pablorevilla-meshtastic/meshview:latest +docker restart meshview +``` + +Or with docker-compose: + +```bash +docker-compose pull +docker-compose up -d +``` + +## Logs + +View container logs: + +```bash +docker logs meshview + +# Follow logs +docker logs -f meshview + +# Last 100 lines +docker logs --tail 100 meshview +``` + +## Troubleshooting + +### Container won't start + +Check logs: +```bash +docker logs meshview +``` + +### Database permission issues + +Ensure the data directory is writable: +```bash +chmod -R 755 meshview/data +``` + +### Can't connect to MQTT + +1. Check your MQTT configuration in `config.ini` +2. Verify network connectivity from the container: + ```bash + docker exec meshview ping mqtt.meshtastic.org + ``` + +### Port already in use + +Change the host port (left side): +```bash +-p 8082:8081 +``` + +Then access at: http://localhost:8082 + +## Building Your Own Image + +If you want to build from source: + +```bash +git clone https://github.com/pablorevilla-meshtastic/meshview.git +cd meshview +docker build -f Containerfile -t meshview:local . +``` + +## Security Notes + +- The container runs as a non-root user (`app`, UID 10001) +- No privileged access required +- Only port 8081 is exposed +- All data stored in mounted volumes + +## Support + +- GitHub Issues: https://github.com/pablorevilla-meshtastic/meshview/issues +- Documentation: https://github.com/pablorevilla-meshtastic/meshview diff --git a/README.md b/README.md new file mode 100644 index 00000000..e4c85359 --- /dev/null +++ b/README.md @@ -0,0 +1,566 @@ + +# Meshview +![Start Page](screenshots/animated.gif) + +The project serves as a real-time monitoring and diagnostic tool for the Meshtastic mesh network. It provides detailed insights into network activity, including message traffic, node positions, and telemetry data. + +### Version 3.0.1 — December 2025 + +#### 🌐 Multi-Language Support (i18n) +- New `/api/lang` endpoint for serving translations +- Section-based translation loading (e.g., `?section=firehose`) +- Default language controlled via config file language section +- JSON-based translation files for easy expansion +- Core pages updated to support `data-translate-lang` attributes + +### 🛠 Improvements +- Updated UI elements across multiple templates for localization readiness +- General cleanup to support future language additions + +### Version 3.0.0 update - November 2025 + +**Major Infrastructure Improvements:** + +* **Database Migrations**: Alembic integration for safe schema upgrades and database versioning +* **Automated Backups**: Independent database backup system with gzip compression (separate from cleanup) +* **Development Tools**: Quick setup script (`setup-dev.sh`) with pre-commit hooks for code quality +* **Docker Support**: Pre-built containers now available on GitHub Container Registry with automatic builds - ogarcia + +**New Features:** + +* **Traceroute Return Path**: Log and display return path data for traceroute packets - jschrempp +* **Microsecond Timestamps**: Added `import_time_us` columns for higher precision time tracking + +**Technical Improvements:** + +* Migration from manual SQL to Alembic-managed schema +* Container images use `uv` for faster dependency installation +* Python 3.13 support with slim Debian-based images +* Documentation collection in `docs/` directory +* API routes moved to separate modules for better organization +* /version and /health endpoints added for monitoring + +See [README-Docker.md](README-Docker.md) for container deployment and [docs/](docs/) for technical documentation. + +### Version 2.0.7 update - September 2025 +* New database maintenance capability to automatically keep a specific number of days of data. +* Added configuration for update intervals for both the Live Map and the Firehose pages. + +### Version 2.0.6 update - August 2025 +* New Live Map (Shows packet feed live) +* New API /api/config (See API documentation) +* New API /api/edges (See API documentation) +* Adds edges to the map (click to see traceroute and neighbours) + + +### Version 2.0.4 update - August 2025 +* New statistic page with more data. +* New API /api/stats (See API documentation). +* Inprovement on "See Everything" and "Conversation" pages. +* Tracking of replies with links to original message. + +### Version 2.0.3 update - June 2025 +* Moved more graphs to eCharts. +* Addedd smooth updating for "Conversations" and "See everything" sections. +* Now you can turn on and off "Quick Links". +* Network graphs are now dynamically generated depending on your mesh and the presets in use. +* Download node's packet information for the last 3 days to .csv file. +* Display distance traveled by packet. +### Key Features + +* **Live Data Visualization**: Users can view real-time data from the mesh network, including text messages, GPS positions, and node information. + +* **Interactive Map**: The site offers an interactive map displaying the locations of active nodes, helping users identify network coverage areas. + +* **Mesh Graphs**: Visual representations of the network's structure and connectivity are available, illustrating how nodes are interconnected. + +* **Packet Analysis**: Detailed information on individual data packets transmitted within the network can be accessed, including payload content and transmission paths. + +* **Node Statistics**: Users can explore statistics related to network traffic, such as top contributors and message volumes. + +Samples of currently running instances: + +- https://meshview.bayme.sh (SF Bay Area) +- https://www.svme.sh (Sacramento Valley) +- https://meshview.nyme.sh (New York) +- https://meshview.socalmesh.org (LA Area) +- https://map.wpamesh.net (Western Pennsylvania) +- https://meshview.chicagolandmesh.org (Chicago) +- https://meshview.mt.gt (Canadaverse) +- https://canadaverse.org (Canadaverse) +- https://meshview.meshtastic.es (Spain) +- https://view.mtnme.sh (North Georgia / East Tennessee) +- https://meshview.lsinfra.de (Hessen - Germany) +- https://meshview.pvmesh.org (Pioneer Valley, Massachusetts) +- https://meshview.louisianamesh.org (Louisiana) +- https://www.swlamesh.com/map (Southwest Louisiana) +- https://meshview.meshcolombia.co/ (Colombia) +- https://meshview-salzburg.jmt.gr/ (Salzburg / Austria) +--- + + +### Updating from 2.x to 3.x +We are adding the use of Alembic. If using GitHub +Update your codebase by running the pull command +```bash +cd meshview +git pull origin master +``` +Install Alembic in your environment +```bash +./env/bin/pip install alembic +``` +Start your scripts or services. This process will update your database with the latest schema. + +## Installing + +### Using Docker (Recommended) + +The easiest way to run MeshView is using Docker. Pre-built images are available from GitHub Container Registry. + +See **[README-Docker.md](README-Docker.md)** for complete Docker installation and usage instructions. + +### Manual Installation + +Requires **`python3.13`** or above. + +Clone the repo from GitHub: + +```bash +git clone https://github.com/pablorevilla-meshtastic/meshview.git +cd meshview +``` + +#### Quick Setup (Recommended) + +Run the development setup script: + +```bash +./setup-dev.sh +``` + +This will: +- Create Python virtual environment +- Install all requirements +- Install development tools (pre-commit, pytest) +- Set up pre-commit hooks for code formatting +- Create config.ini from sample + +#### Manual Setup + +Create a Python virtual environment: + +```bash +python3 -m venv env +``` + +Install the environment requirements: + +```bash +./env/bin/pip install -r requirements.txt +``` + +Install `graphviz` on MacOS or Debian/Ubuntu Linux: + +```bash +sudo apt-get install graphviz +``` + +Copy `sample.config.ini` to `config.ini`: + +```bash +cp sample.config.ini config.ini +``` + +Edit `config.ini` to match your MQTT and web server settings: + + +```bash +nano config.ini +``` + +> **NOTE** +> On MacOS set the bind configuration line to +> ``` +> bind = 127.0.0.1 +> ``` + +Example: + +```ini +# ------------------------- +# Server Configuration +# ------------------------- +[server] +# The address to bind the server to. Use * to listen on all interfaces. +bind = * + +# Port to run the web server on. +port = 8081 + +# Path to TLS certificate (leave blank to disable HTTPS). +tls_cert = + +# Path for the ACME challenge if using Let's Encrypt. +acme_challenge = + + +# ------------------------- +# Site Appearance & Behavior +# ------------------------- +[site] +# The domain name of your site. +domain = + +# Select language (this represents the name of the json file in the /lang directory) +language = es + +# Site title to show in the browser title bar and headers. +title = Bay Area Mesh + +# A brief message shown on the homepage. +message = Real time data from around the bay area and beyond. + +# Starting URL when loading the index page. +starting = /chat + +# Enable or disable site features (as strings: "True" or "False"). +nodes = True +conversations = True +everything = True +graphs = True +stats = True +net = True +map = True +top = True + +# Map boundaries (used for the map view). +# Defaults will show the San Francisco Bay Area +map_top_left_lat = 39 +map_top_left_lon = -123 +map_bottom_right_lat = 36 +map_bottom_right_lon = -121 + +# Updates intervals in seconds, zero or negative number means no updates +# defaults will be 3 seconds +map_interval=3 +firehose_interval=3 + +# Weekly net details +weekly_net_message = Weekly Mesh check-in. We will keep it open on every Wednesday from 5:00pm for checkins. The message format should be (LONG NAME) - (CITY YOU ARE IN) #BayMeshNet. +net_tag = #BayMeshNet + +# ------------------------- +# MQTT Broker Configuration +# ------------------------- +[mqtt] +# MQTT server hostname or IP. +server = mqtt.bayme.sh + +# Topics to subscribe to (as JSON-like list, but still a string). +topics = ["msh/US/bayarea/#", "msh/US/CA/mrymesh/#", "msh/US/CA/sacvalley"] + +# Port used by MQTT (typically 1883 for unencrypted). +port = 1883 + +# MQTT username and password. +username = meshdev +password = large4cats + + +# ------------------------- +# Database Configuration +# ------------------------- +[database] +# SQLAlchemy async connection string. +# Examples: +# sqlite+aiosqlite:///packets.db +# postgresql+asyncpg://user:pass@host:5432/meshview +connection_string = sqlite+aiosqlite:///packets.db + +> **NOTE (PostgreSQL setup)** +> If you want to use PostgreSQL instead of SQLite: +> +> 1) Install PostgreSQL for your OS. +> 2) Create a user and database: +> - `CREATE USER meshview WITH PASSWORD 'change_me';` +> - `CREATE DATABASE meshview OWNER meshview;` +> 3) Update `config.ini`: +> - `connection_string = postgresql+asyncpg://meshview:change_me@localhost:5432/meshview` +> 4) Initialize the schema: +> - `./env/bin/python startdb.py` + + +# ------------------------- +# Database Cleanup Configuration +# ------------------------- +[cleanup] +# Enable or disable daily cleanup +enabled = False +# Number of days to keep records in the database +days_to_keep = 14 +# Time to run daily cleanup (24-hour format) +hour = 2 +minute = 00 +# Run VACUUM after cleanup +vacuum = False + + +# ------------------------- +# Logging Configuration +# ------------------------- +[logging] +# Enable or disable HTTP access logs from the web server +# When disabled, request logs like "GET /api/chat" will not appear +# Application logs (errors, startup messages, etc.) are unaffected +# Set to True to enable, False to disable (default: False) +access_log = False +# Database cleanup logfile location +db_cleanup_logfile = dbcleanup.log +``` + +--- + +## Running Meshview + +Start the database manager: + +```bash +./env/bin/python startdb.py +``` + +Start the web server: + +```bash +./env/bin/python main.py +``` + +> **NOTE** +> You can specify a custom config file with the `--config` flag: +> +> ```bash +> ./env/bin/python startdb.py --config /path/to/config.ini +> ./env/bin/python main.py --config /path/to/config.ini +> ``` + +Open in your browser: http://localhost:8081/ + +--- + +## Running Meshview with `mvrun.py` + +- `mvrun.py` starts both `startdb.py` and `main.py` in separate threads and merges the output. +- It accepts several command-line arguments for flexible deployment. + +```bash +./env/bin/python mvrun.py +``` + +**Command-line options:** +- `--config CONFIG` - Path to the configuration file (default: `config.ini`) +- `--pid_dir PID_DIR` - Directory for PID files (default: `.`) +- `--py_exec PY_EXEC` - Path to the Python executable (default: `./env/bin/python`) + +**Examples:** +```bash +# Use a specific config file +./env/bin/python mvrun.py --config /etc/meshview/config.ini + +# Store PID files in a specific directory +./env/bin/python mvrun.py --pid_dir /var/run/meshview + +# Use a different Python executable +./env/bin/python mvrun.py --py_exec /usr/bin/python3 +``` + +--- + +## Setting Up Systemd Services (Ubuntu) + +To run Meshview automatically on boot, create systemd services for `startdb.py` and `main.py`. +> **NOTE** +> You need to change the "User" and "/path/to/meshview" for your instance of the code on each service. + +### 1. Service for `startdb.py` + +Create: + +```bash +sudo nano /etc/systemd/system/meshview-db.service +``` + +Paste: + +```ini +[Unit] +Description=Meshview Database Initializer +After=network.target + +[Service] +Type=simple +WorkingDirectory=/path/to/meshview +ExecStart=/path/to/meshview/env/bin/python /path/to/meshview/startdb.py --config /path/to/meshview/config.ini +Restart=always +RestartSec=5 +User=yourusername + +[Install] +WantedBy=multi-user.target +``` + +### 2. Service for `main.py` + +Create: + +```bash +sudo nano /etc/systemd/system/meshview-web.service +``` + +Paste: + +```ini +[Unit] +Description=Meshview Web Server +After=network.target meshview-db.service + +[Service] +Type=simple +WorkingDirectory=/path/to/meshview +ExecStart=/path/to/meshview/env/bin/python /path/to/meshview/main.py --config /path/to/meshview/config.ini +Restart=always +RestartSec=5 +User=yourusername + +[Install] +WantedBy=multi-user.target +``` + +### 3. Enable and start the services + +```bash +sudo systemctl daemon-reexec +sudo systemctl daemon-reload +sudo systemctl enable meshview-db +sudo systemctl enable meshview-web +sudo systemctl start meshview-db +sudo systemctl start meshview-web +``` + +### 4. Check status + +```bash +systemctl status meshview-db +systemctl status meshview-web +``` + +**TIP** +After editing `.service` files, always run: + +```bash +sudo systemctl daemon-reload +``` + +## 5. Database Maintenance +### Database maintnance can now be done via the script itself here is the section from the configuration file. +- Simple to setup +- It will not drop any packets +``` +# ------------------------- +# Database Cleanup Configuration +# ------------------------- +[cleanup] +# Enable or disable daily cleanup +enabled = False +# Number of days to keep records in the database +days_to_keep = 14 +# Time to run daily cleanup (24-hour format) +hour = 2 +minute = 00 +# Run VACUUM after cleanup +vacuum = False + +# ------------------------- +# Logging Configuration +# ------------------------- +[logging] +# Enable or disable HTTP access logs from the web server +access_log = False +# Database cleanup logfile location +db_cleanup_logfile = dbcleanup.log +``` +Once changes are done you need to restart the script for changes to load. + +### Alternatively we can do it via your OS +- Create and save bash script below. (Modify /path/to/file/ to the correct path) +- Name it cleanup.sh +- Make it executable. +```bash + #!/bin/bash + +DB_FILE="/path/to/file/packets.db" + + +# Stop DB service +sudo systemctl stop meshview-db.service +sudo systemctl stop meshview-web.service + +sleep 5 +echo "Run cleanup..." +# Run cleanup queries +sqlite3 "$DB_FILE" <> /path/to/file/cleanup.log 2>&1 +``` + +Check the log file to see it the script run at the specific time. + +--- + +## Testing + +MeshView includes a test suite using pytest. For detailed testing documentation, see [README-testing.md](README-testing.md). + +Quick start: +```bash +./env/bin/pytest tests/test_api_simple.py -v +``` + +--- + +## Technical Documentation + +For more detailed technical documentation including database migrations, architecture details, and advanced topics, see the [docs/](docs/) directory. diff --git a/alembic.ini b/alembic.ini new file mode 100644 index 00000000..e60b7dcb --- /dev/null +++ b/alembic.ini @@ -0,0 +1,120 @@ +# A generic, single database configuration. + +[alembic] +# path to migration scripts +# Use forward slashes (/) also on windows to provide an os agnostic path +script_location = alembic + +# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s +# Uncomment the line below if you want the files to be prepended with date and time +# see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file +# for all available tokens +# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s + +# sys.path path, will be prepended to sys.path if present. +# defaults to the current working directory. +prepend_sys_path = . + +# timezone to use when rendering the date within the migration file +# as well as the filename. +# If specified, requires the python>=3.9 or backports.zoneinfo library and tzdata library. +# Any required deps can installed by adding `alembic[tz]` to the pip requirements +# string value is passed to ZoneInfo() +# leave blank for localtime +# timezone = + +# max length of characters to apply to the "slug" field +# truncate_slug_length = 40 + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + +# set to 'true' to allow .pyc and .pyo files without +# a source .py file to be detected as revisions in the +# versions/ directory +# sourceless = false + +# version location specification; This defaults +# to alembic/versions. When using multiple version +# directories, initial revisions must be specified with --version-path. +# The path separator used here should be the separator specified by "version_path_separator" below. +# version_locations = %(here)s/bar:%(here)s/bat:alembic/versions + +# version path separator; As mentioned above, this is the character used to split +# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep. +# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas. +# Valid values for version_path_separator are: +# +# version_path_separator = : +# version_path_separator = ; +# version_path_separator = space +# version_path_separator = newline +# +# Use os.pathsep. Default configuration used for new projects. +version_path_separator = os + +# set to 'true' to search source files recursively +# in each "version_locations" directory +# new in Alembic version 1.10 +# recursive_version_locations = false + +# the output encoding used when revision files +# are written from script.py.mako +# output_encoding = utf-8 + +# sqlalchemy.url will be set programmatically from meshview config +# sqlalchemy.url = driver://user:pass@localhost/dbname + + +[post_write_hooks] +# post_write_hooks defines scripts or Python functions that are run +# on newly generated revision scripts. See the documentation for further +# detail and examples + +# format using "black" - use the console_scripts runner, against the "black" entrypoint +# hooks = black +# black.type = console_scripts +# black.entrypoint = black +# black.options = -l 79 REVISION_SCRIPT_FILENAME + +# lint with attempts to fix using "ruff" - use the exec runner, execute a binary +# hooks = ruff +# ruff.type = exec +# ruff.executable = %(here)s/.venv/bin/ruff +# ruff.options = --fix REVISION_SCRIPT_FILENAME + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = INFO +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARNING +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(asctime)s %(filename)s:%(lineno)d [pid:%(process)d] %(levelname)s - %(message)s +datefmt = %Y-%m-%d %H:%M:%S \ No newline at end of file diff --git a/alembic/README b/alembic/README new file mode 100644 index 00000000..98e4f9c4 --- /dev/null +++ b/alembic/README @@ -0,0 +1 @@ +Generic single-database configuration. \ No newline at end of file diff --git a/alembic/env.py b/alembic/env.py new file mode 100644 index 00000000..a4d99b7a --- /dev/null +++ b/alembic/env.py @@ -0,0 +1,102 @@ +import asyncio +from logging.config import fileConfig + +from sqlalchemy import pool +from sqlalchemy.engine import Connection +from sqlalchemy.ext.asyncio import async_engine_from_config + +from alembic import context + +# Import models metadata for autogenerate support +from meshview.models import Base + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +# Use disable_existing_loggers=False to preserve app logging configuration +if config.config_file_name is not None: + fileConfig(config.config_file_name, disable_existing_loggers=False) + +# Add your model's MetaData object here for 'autogenerate' support +target_metadata = Base.metadata + + +def run_migrations_offline() -> None: + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + ) + + with context.begin_transaction(): + context.run_migrations() + + +def do_run_migrations(connection: Connection) -> None: + """Run migrations with the given connection.""" + context.configure(connection=connection, target_metadata=target_metadata) + + with context.begin_transaction(): + context.run_migrations() + + +async def run_async_migrations() -> None: + """Run migrations in async mode.""" + # Get configuration section + configuration = config.get_section(config.config_ini_section, {}) + + # If sqlalchemy.url is not set in alembic.ini, try to get it from meshview config + if "sqlalchemy.url" not in configuration: + try: + from meshview.config import CONFIG + + configuration["sqlalchemy.url"] = CONFIG["database"]["connection_string"] + except Exception: + # Fallback to a default for initial migration creation + configuration["sqlalchemy.url"] = "sqlite+aiosqlite:///packets.db" + + connectable = async_engine_from_config( + configuration, + prefix="sqlalchemy.", + poolclass=pool.NullPool, + ) + + async with connectable.connect() as connection: + await connection.run_sync(do_run_migrations) + + await connectable.dispose() + + +def run_migrations_online() -> None: + """Run migrations in 'online' mode with async support.""" + try: + # Event loop is already running, schedule and run the coroutine + import concurrent.futures + + with concurrent.futures.ThreadPoolExecutor() as pool: + pool.submit(lambda: asyncio.run(run_async_migrations())).result() + except RuntimeError: + # No event loop running, create one + asyncio.run(run_async_migrations()) + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/alembic/script.py.mako b/alembic/script.py.mako new file mode 100644 index 00000000..fbc4b07d --- /dev/null +++ b/alembic/script.py.mako @@ -0,0 +1,26 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision: str = ${repr(up_revision)} +down_revision: Union[str, None] = ${repr(down_revision)} +branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)} +depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)} + + +def upgrade() -> None: + ${upgrades if upgrades else "pass"} + + +def downgrade() -> None: + ${downgrades if downgrades else "pass"} diff --git a/alembic/versions/1717fa5c6545_add_example_table.py b/alembic/versions/1717fa5c6545_add_example_table.py new file mode 100644 index 00000000..bc4d59a5 --- /dev/null +++ b/alembic/versions/1717fa5c6545_add_example_table.py @@ -0,0 +1,45 @@ +"""Add example table + +Revision ID: 1717fa5c6545 +Revises: c88468b7ab0b +Create Date: 2025-10-26 20:59:04.347066 + +""" + +from collections.abc import Sequence + +import sqlalchemy as sa + +from alembic import op + +# revision identifiers, used by Alembic. +revision: str = '1717fa5c6545' +down_revision: str | None = 'add_time_us_cols' +branch_labels: str | Sequence[str] | None = None +depends_on: str | Sequence[str] | None = None + + +def upgrade() -> None: + """Create example table with sample columns.""" + op.create_table( + 'example', + sa.Column('id', sa.Integer(), nullable=False, primary_key=True, autoincrement=True), + sa.Column('name', sa.String(length=100), nullable=False), + sa.Column('description', sa.Text(), nullable=True), + sa.Column('value', sa.Float(), nullable=True), + sa.Column('is_active', sa.Boolean(), nullable=False, server_default='1'), + sa.Column( + 'created_at', sa.DateTime(), nullable=False, server_default=sa.text('CURRENT_TIMESTAMP') + ), + sa.Column('updated_at', sa.DateTime(), nullable=True), + sa.PrimaryKeyConstraint('id'), + ) + + # Create an index on the name column for faster lookups + op.create_index('idx_example_name', 'example', ['name']) + + +def downgrade() -> None: + """Remove example table.""" + op.drop_index('idx_example_name', table_name='example') + op.drop_table('example') diff --git a/alembic/versions/2b5a61bb2b75_auto_generated_migration.py b/alembic/versions/2b5a61bb2b75_auto_generated_migration.py new file mode 100644 index 00000000..5b1b5385 --- /dev/null +++ b/alembic/versions/2b5a61bb2b75_auto_generated_migration.py @@ -0,0 +1,35 @@ +"""Add first_seen_us and last_seen_us to node table + +Revision ID: 2b5a61bb2b75 +Revises: ac311b3782a1 +Create Date: 2025-11-05 15:19:13.446724 + +""" + +from collections.abc import Sequence + +import sqlalchemy as sa + +from alembic import op + +# revision identifiers, used by Alembic. +revision: str = '2b5a61bb2b75' +down_revision: str | None = 'ac311b3782a1' +branch_labels: str | Sequence[str] | None = None +depends_on: str | Sequence[str] | None = None + + +def upgrade() -> None: + # Add microsecond epoch timestamp columns for first and last seen times + op.add_column('node', sa.Column('first_seen_us', sa.BigInteger(), nullable=True)) + op.add_column('node', sa.Column('last_seen_us', sa.BigInteger(), nullable=True)) + op.create_index('idx_node_first_seen_us', 'node', ['first_seen_us'], unique=False) + op.create_index('idx_node_last_seen_us', 'node', ['last_seen_us'], unique=False) + + +def downgrade() -> None: + # Remove the microsecond epoch timestamp columns and their indexes + op.drop_index('idx_node_last_seen_us', table_name='node') + op.drop_index('idx_node_first_seen_us', table_name='node') + op.drop_column('node', 'last_seen_us') + op.drop_column('node', 'first_seen_us') diff --git a/alembic/versions/9f3b1a8d2c4f_drop_import_time_columns.py b/alembic/versions/9f3b1a8d2c4f_drop_import_time_columns.py new file mode 100644 index 00000000..24e2cccf --- /dev/null +++ b/alembic/versions/9f3b1a8d2c4f_drop_import_time_columns.py @@ -0,0 +1,65 @@ +"""Drop import_time columns. + +Revision ID: 9f3b1a8d2c4f +Revises: 2b5a61bb2b75 +Create Date: 2026-01-09 09:55:00.000000 +""" + +from collections.abc import Sequence + +import sqlalchemy as sa + +from alembic import op + +# revision identifiers, used by Alembic. +revision: str = "9f3b1a8d2c4f" +down_revision: str | None = "2b5a61bb2b75" +branch_labels: str | Sequence[str] | None = None +depends_on: str | Sequence[str] | None = None + + +def upgrade() -> None: + conn = op.get_bind() + inspector = sa.inspect(conn) + + packet_indexes = {idx["name"] for idx in inspector.get_indexes("packet")} + packet_columns = {col["name"] for col in inspector.get_columns("packet")} + + with op.batch_alter_table("packet", schema=None) as batch_op: + if "idx_packet_import_time" in packet_indexes: + batch_op.drop_index("idx_packet_import_time") + if "idx_packet_from_node_time" in packet_indexes: + batch_op.drop_index("idx_packet_from_node_time") + if "import_time" in packet_columns: + batch_op.drop_column("import_time") + + packet_seen_columns = {col["name"] for col in inspector.get_columns("packet_seen")} + with op.batch_alter_table("packet_seen", schema=None) as batch_op: + if "import_time" in packet_seen_columns: + batch_op.drop_column("import_time") + + traceroute_indexes = {idx["name"] for idx in inspector.get_indexes("traceroute")} + traceroute_columns = {col["name"] for col in inspector.get_columns("traceroute")} + with op.batch_alter_table("traceroute", schema=None) as batch_op: + if "idx_traceroute_import_time" in traceroute_indexes: + batch_op.drop_index("idx_traceroute_import_time") + if "import_time" in traceroute_columns: + batch_op.drop_column("import_time") + + +def downgrade() -> None: + with op.batch_alter_table("traceroute", schema=None) as batch_op: + batch_op.add_column(sa.Column("import_time", sa.DateTime(), nullable=True)) + batch_op.create_index("idx_traceroute_import_time", ["import_time"], unique=False) + + with op.batch_alter_table("packet_seen", schema=None) as batch_op: + batch_op.add_column(sa.Column("import_time", sa.DateTime(), nullable=True)) + + with op.batch_alter_table("packet", schema=None) as batch_op: + batch_op.add_column(sa.Column("import_time", sa.DateTime(), nullable=True)) + batch_op.create_index("idx_packet_import_time", [sa.text("import_time DESC")], unique=False) + batch_op.create_index( + "idx_packet_from_node_time", + ["from_node_id", sa.text("import_time DESC")], + unique=False, + ) diff --git a/alembic/versions/ac311b3782a1_add_route_return_to_traceroute.py b/alembic/versions/ac311b3782a1_add_route_return_to_traceroute.py new file mode 100644 index 00000000..046e8a55 --- /dev/null +++ b/alembic/versions/ac311b3782a1_add_route_return_to_traceroute.py @@ -0,0 +1,31 @@ +"""add route_return to traceroute + +Revision ID: ac311b3782a1 +Revises: 1717fa5c6545 +Create Date: 2025-11-04 20:28:33.174137 + +""" + +from collections.abc import Sequence + +import sqlalchemy as sa + +from alembic import op + +# revision identifiers, used by Alembic. +revision: str = 'ac311b3782a1' +down_revision: str | None = '1717fa5c6545' +branch_labels: str | Sequence[str] | None = None +depends_on: str | Sequence[str] | None = None + + +def upgrade() -> None: + # Add route_return column to traceroute table + with op.batch_alter_table('traceroute', schema=None) as batch_op: + batch_op.add_column(sa.Column('route_return', sa.LargeBinary(), nullable=True)) + + +def downgrade() -> None: + # Remove route_return column from traceroute table + with op.batch_alter_table('traceroute', schema=None) as batch_op: + batch_op.drop_column('route_return') diff --git a/alembic/versions/add_import_time_us_columns.py b/alembic/versions/add_import_time_us_columns.py new file mode 100644 index 00000000..daf588ec --- /dev/null +++ b/alembic/versions/add_import_time_us_columns.py @@ -0,0 +1,74 @@ +"""add import_time_us columns + +Revision ID: add_time_us_cols +Revises: c88468b7ab0b +Create Date: 2025-11-03 14:10:00.000000 + +""" + +from collections.abc import Sequence + +import sqlalchemy as sa + +from alembic import op + +# revision identifiers, used by Alembic. +revision: str = 'add_time_us_cols' +down_revision: str | None = 'c88468b7ab0b' +branch_labels: str | Sequence[str] | None = None +depends_on: str | Sequence[str] | None = None + + +def upgrade() -> None: + # Check if columns already exist, add them if they don't + conn = op.get_bind() + inspector = sa.inspect(conn) + + # Add import_time_us to packet table + packet_columns = [col['name'] for col in inspector.get_columns('packet')] + if 'import_time_us' not in packet_columns: + with op.batch_alter_table('packet', schema=None) as batch_op: + batch_op.add_column(sa.Column('import_time_us', sa.BigInteger(), nullable=True)) + op.create_index( + 'idx_packet_import_time_us', 'packet', [sa.text('import_time_us DESC')], unique=False + ) + op.create_index( + 'idx_packet_from_node_time_us', + 'packet', + ['from_node_id', sa.text('import_time_us DESC')], + unique=False, + ) + + # Add import_time_us to packet_seen table + packet_seen_columns = [col['name'] for col in inspector.get_columns('packet_seen')] + if 'import_time_us' not in packet_seen_columns: + with op.batch_alter_table('packet_seen', schema=None) as batch_op: + batch_op.add_column(sa.Column('import_time_us', sa.BigInteger(), nullable=True)) + op.create_index( + 'idx_packet_seen_import_time_us', 'packet_seen', ['import_time_us'], unique=False + ) + + # Add import_time_us to traceroute table + traceroute_columns = [col['name'] for col in inspector.get_columns('traceroute')] + if 'import_time_us' not in traceroute_columns: + with op.batch_alter_table('traceroute', schema=None) as batch_op: + batch_op.add_column(sa.Column('import_time_us', sa.BigInteger(), nullable=True)) + op.create_index( + 'idx_traceroute_import_time_us', 'traceroute', ['import_time_us'], unique=False + ) + + +def downgrade() -> None: + # Drop indexes and columns + op.drop_index('idx_traceroute_import_time_us', table_name='traceroute') + with op.batch_alter_table('traceroute', schema=None) as batch_op: + batch_op.drop_column('import_time_us') + + op.drop_index('idx_packet_seen_import_time_us', table_name='packet_seen') + with op.batch_alter_table('packet_seen', schema=None) as batch_op: + batch_op.drop_column('import_time_us') + + op.drop_index('idx_packet_from_node_time_us', table_name='packet') + op.drop_index('idx_packet_import_time_us', table_name='packet') + with op.batch_alter_table('packet', schema=None) as batch_op: + batch_op.drop_column('import_time_us') diff --git a/alembic/versions/b7c3c2e3a1f0_add_last_update_us_to_node.py b/alembic/versions/b7c3c2e3a1f0_add_last_update_us_to_node.py new file mode 100644 index 00000000..f2129c10 --- /dev/null +++ b/alembic/versions/b7c3c2e3a1f0_add_last_update_us_to_node.py @@ -0,0 +1,94 @@ +"""Add last_update_us to node and migrate data. + +Revision ID: b7c3c2e3a1f0 +Revises: 9f3b1a8d2c4f +Create Date: 2026-01-12 10:12:00.000000 +""" + +from collections.abc import Sequence +from datetime import UTC, datetime + +import sqlalchemy as sa + +from alembic import op + +# revision identifiers, used by Alembic. +revision: str = "b7c3c2e3a1f0" +down_revision: str | None = "9f3b1a8d2c4f" +branch_labels: str | Sequence[str] | None = None +depends_on: str | Sequence[str] | None = None + + +def _parse_datetime(value): + if value is None: + return None + if isinstance(value, datetime): + dt = value + elif isinstance(value, str): + text = value.replace("Z", "+00:00") + try: + dt = datetime.fromisoformat(text) + except ValueError: + return None + else: + return None + + if dt.tzinfo is None: + return dt.replace(tzinfo=UTC) + return dt.astimezone(UTC) + + +def upgrade() -> None: + conn = op.get_bind() + op.add_column("node", sa.Column("last_update_us", sa.BigInteger(), nullable=True)) + op.create_index("idx_node_last_update_us", "node", ["last_update_us"], unique=False) + + node = sa.table( + "node", + sa.column("id", sa.String()), + sa.column("last_update", sa.DateTime()), + sa.column("last_update_us", sa.BigInteger()), + ) + + rows = conn.execute(sa.select(node.c.id, node.c.last_update)).all() + for node_id, last_update in rows: + dt = _parse_datetime(last_update) + if dt is None: + continue + last_update_us = int(dt.timestamp() * 1_000_000) + conn.execute( + sa.update(node).where(node.c.id == node_id).values(last_update_us=last_update_us) + ) + + if conn.dialect.name == "sqlite": + with op.batch_alter_table("node", schema=None) as batch_op: + batch_op.drop_column("last_update") + else: + op.drop_column("node", "last_update") + + +def downgrade() -> None: + conn = op.get_bind() + op.add_column("node", sa.Column("last_update", sa.DateTime(), nullable=True)) + + node = sa.table( + "node", + sa.column("id", sa.String()), + sa.column("last_update", sa.DateTime()), + sa.column("last_update_us", sa.BigInteger()), + ) + + rows = conn.execute(sa.select(node.c.id, node.c.last_update_us)).all() + for node_id, last_update_us in rows: + if last_update_us is None: + continue + dt = datetime.fromtimestamp(last_update_us / 1_000_000, tz=UTC).replace(tzinfo=None) + conn.execute(sa.update(node).where(node.c.id == node_id).values(last_update=dt)) + + if conn.dialect.name == "sqlite": + with op.batch_alter_table("node", schema=None) as batch_op: + batch_op.drop_index("idx_node_last_update_us") + batch_op.drop_column("last_update_us") + else: + op.drop_index("idx_node_last_update_us", table_name="node") + op.drop_column("node", "last_update_us") diff --git a/alembic/versions/c88468b7ab0b_initial_migration.py b/alembic/versions/c88468b7ab0b_initial_migration.py new file mode 100644 index 00000000..9114a18f --- /dev/null +++ b/alembic/versions/c88468b7ab0b_initial_migration.py @@ -0,0 +1,160 @@ +"""Initial migration + +Revision ID: c88468b7ab0b +Revises: +Create Date: 2025-10-26 20:56:50.285200 + +""" + +from collections.abc import Sequence + +import sqlalchemy as sa + +from alembic import op + +# revision identifiers, used by Alembic. +revision: str = 'c88468b7ab0b' +down_revision: str | None = None +branch_labels: str | Sequence[str] | None = None +depends_on: str | Sequence[str] | None = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + # Get connection and inspector to check what exists + conn = op.get_bind() + inspector = sa.inspect(conn) + existing_tables = inspector.get_table_names() + + # Create node table if it doesn't exist + if 'node' not in existing_tables: + op.create_table( + 'node', + sa.Column('id', sa.String(), nullable=False), + sa.Column('node_id', sa.BigInteger(), nullable=True), + sa.Column('long_name', sa.String(), nullable=True), + sa.Column('short_name', sa.String(), nullable=True), + sa.Column('hw_model', sa.String(), nullable=True), + sa.Column('firmware', sa.String(), nullable=True), + sa.Column('role', sa.String(), nullable=True), + sa.Column('last_lat', sa.BigInteger(), nullable=True), + sa.Column('last_long', sa.BigInteger(), nullable=True), + sa.Column('channel', sa.String(), nullable=True), + sa.Column('last_update', sa.DateTime(), nullable=True), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('node_id'), + ) + op.create_index('idx_node_node_id', 'node', ['node_id'], unique=False) + + # Create packet table if it doesn't exist + if 'packet' not in existing_tables: + op.create_table( + 'packet', + sa.Column('id', sa.BigInteger(), nullable=False), + sa.Column('portnum', sa.Integer(), nullable=True), + sa.Column('from_node_id', sa.BigInteger(), nullable=True), + sa.Column('to_node_id', sa.BigInteger(), nullable=True), + sa.Column('payload', sa.LargeBinary(), nullable=True), + sa.Column('import_time', sa.DateTime(), nullable=True), + sa.Column('import_time_us', sa.BigInteger(), nullable=True), + sa.Column('channel', sa.String(), nullable=True), + sa.PrimaryKeyConstraint('id'), + ) + op.create_index('idx_packet_from_node_id', 'packet', ['from_node_id'], unique=False) + op.create_index('idx_packet_to_node_id', 'packet', ['to_node_id'], unique=False) + op.create_index( + 'idx_packet_import_time', 'packet', [sa.text('import_time DESC')], unique=False + ) + op.create_index( + 'idx_packet_import_time_us', 'packet', [sa.text('import_time_us DESC')], unique=False + ) + op.create_index( + 'idx_packet_from_node_time', + 'packet', + ['from_node_id', sa.text('import_time DESC')], + unique=False, + ) + op.create_index( + 'idx_packet_from_node_time_us', + 'packet', + ['from_node_id', sa.text('import_time_us DESC')], + unique=False, + ) + + # Create packet_seen table if it doesn't exist + if 'packet_seen' not in existing_tables: + op.create_table( + 'packet_seen', + sa.Column('packet_id', sa.BigInteger(), nullable=False), + sa.Column('node_id', sa.BigInteger(), nullable=False), + sa.Column('rx_time', sa.BigInteger(), nullable=False), + sa.Column('hop_limit', sa.Integer(), nullable=True), + sa.Column('hop_start', sa.Integer(), nullable=True), + sa.Column('channel', sa.String(), nullable=True), + sa.Column('rx_snr', sa.Float(), nullable=True), + sa.Column('rx_rssi', sa.Integer(), nullable=True), + sa.Column('topic', sa.String(), nullable=True), + sa.Column('import_time', sa.DateTime(), nullable=True), + sa.Column('import_time_us', sa.BigInteger(), nullable=True), + sa.ForeignKeyConstraint( + ['packet_id'], + ['packet.id'], + ), + sa.PrimaryKeyConstraint('packet_id', 'node_id', 'rx_time'), + ) + op.create_index('idx_packet_seen_node_id', 'packet_seen', ['node_id'], unique=False) + op.create_index('idx_packet_seen_packet_id', 'packet_seen', ['packet_id'], unique=False) + op.create_index( + 'idx_packet_seen_import_time_us', 'packet_seen', ['import_time_us'], unique=False + ) + + # Create traceroute table if it doesn't exist + if 'traceroute' not in existing_tables: + op.create_table( + 'traceroute', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('packet_id', sa.BigInteger(), nullable=True), + sa.Column('gateway_node_id', sa.BigInteger(), nullable=True), + sa.Column('done', sa.Boolean(), nullable=True), + sa.Column('route', sa.LargeBinary(), nullable=True), + sa.Column('import_time', sa.DateTime(), nullable=True), + sa.Column('import_time_us', sa.BigInteger(), nullable=True), + sa.ForeignKeyConstraint( + ['packet_id'], + ['packet.id'], + ), + sa.PrimaryKeyConstraint('id'), + ) + op.create_index('idx_traceroute_import_time', 'traceroute', ['import_time'], unique=False) + op.create_index( + 'idx_traceroute_import_time_us', 'traceroute', ['import_time_us'], unique=False + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + # Drop traceroute table and indexes + op.drop_index('idx_traceroute_import_time_us', table_name='traceroute') + op.drop_index('idx_traceroute_import_time', table_name='traceroute') + op.drop_table('traceroute') + + # Drop packet_seen table and indexes + op.drop_index('idx_packet_seen_import_time_us', table_name='packet_seen') + op.drop_index('idx_packet_seen_packet_id', table_name='packet_seen') + op.drop_index('idx_packet_seen_node_id', table_name='packet_seen') + op.drop_table('packet_seen') + + # Drop packet table and indexes + op.drop_index('idx_packet_from_node_time_us', table_name='packet') + op.drop_index('idx_packet_from_node_time', table_name='packet') + op.drop_index('idx_packet_import_time_us', table_name='packet') + op.drop_index('idx_packet_import_time', table_name='packet') + op.drop_index('idx_packet_to_node_id', table_name='packet') + op.drop_index('idx_packet_from_node_id', table_name='packet') + op.drop_table('packet') + + # Drop node table and indexes + op.drop_index('idx_node_node_id', table_name='node') + op.drop_table('node') + # ### end Alembic commands ### diff --git a/alembic/versions/d4d7b0c2e1a4_drop_last_update_us_from_node.py b/alembic/versions/d4d7b0c2e1a4_drop_last_update_us_from_node.py new file mode 100644 index 00000000..e7bd5a03 --- /dev/null +++ b/alembic/versions/d4d7b0c2e1a4_drop_last_update_us_from_node.py @@ -0,0 +1,34 @@ +"""Drop last_update_us from node. + +Revision ID: d4d7b0c2e1a4 +Revises: b7c3c2e3a1f0 +Create Date: 2026-01-12 10:20:00.000000 +""" + +from collections.abc import Sequence + +import sqlalchemy as sa + +from alembic import op + +# revision identifiers, used by Alembic. +revision: str = "d4d7b0c2e1a4" +down_revision: str | None = "b7c3c2e3a1f0" +branch_labels: str | Sequence[str] | None = None +depends_on: str | Sequence[str] | None = None + + +def upgrade() -> None: + conn = op.get_bind() + if conn.dialect.name == "sqlite": + with op.batch_alter_table("node", schema=None) as batch_op: + batch_op.drop_index("idx_node_last_update_us") + batch_op.drop_column("last_update_us") + else: + op.drop_index("idx_node_last_update_us", table_name="node") + op.drop_column("node", "last_update_us") + + +def downgrade() -> None: + op.add_column("node", sa.Column("last_update_us", sa.BigInteger(), nullable=True)) + op.create_index("idx_node_last_update_us", "node", ["last_update_us"], unique=False) diff --git a/container/build-container.sh b/container/build-container.sh new file mode 100755 index 00000000..120ff5f6 --- /dev/null +++ b/container/build-container.sh @@ -0,0 +1,57 @@ +#!/bin/sh +# +# build-container.sh +# +# Script to build MeshView container images + +set -e + +# Default values +IMAGE_NAME="meshview" +TAG="latest" +CONTAINERFILE="Containerfile" + +# Parse arguments +while [ $# -gt 0 ]; do + case "$1" in + --tag|-t) + TAG="$2" + shift 2 + ;; + --name|-n) + IMAGE_NAME="$2" + shift 2 + ;; + --file|-f) + CONTAINERFILE="$2" + shift 2 + ;; + --help|-h) + echo "Usage: $0 [OPTIONS]" + echo "" + echo "Options:" + echo " -t, --tag TAG Tag for the image (default: latest)" + echo " -n, --name NAME Image name (default: meshview)" + echo " -f, --file FILE Containerfile path (default: Containerfile)" + echo " -h, --help Show this help" + exit 0 + ;; + *) + echo "Unknown option: $1" + echo "Use --help for usage information" + exit 1 + ;; + esac +done + +echo "Building MeshView container image..." +echo " Image: ${IMAGE_NAME}:${TAG}" +echo " Containerfile: ${CONTAINERFILE}" +echo "" + +# Build the container +docker build -f "${CONTAINERFILE}" -t "${IMAGE_NAME}:${TAG}" . + +echo "" +echo "Build complete!" +echo "Run with: docker run --rm -p 8081:8081 ${IMAGE_NAME}:${TAG}" diff --git a/container/config.patch b/container/config.patch new file mode 100644 index 00000000..93753bb8 --- /dev/null +++ b/container/config.patch @@ -0,0 +1,37 @@ +diff --git a/sample.config.ini b/sample.config.ini +index 0e64980..494685c 100644 +--- a/sample.config.ini ++++ b/sample.config.ini +@@ -3,7 +3,7 @@ + # ------------------------- + [server] + # The address to bind the server to. Use * to listen on all interfaces. +-bind = * ++bind = 0.0.0.0 + + # Port to run the web server on. + port = 8081 +@@ -64,7 +64,7 @@ net_tag = #BayMeshNet + # ------------------------- + [mqtt] + # MQTT server hostname or IP. +-server = mqtt.bayme.sh ++server = mqtt.meshtastic.org + + # Topics to subscribe to (as JSON-like list, but still a string). + topics = ["msh/US/bayarea/#", "msh/US/CA/mrymesh/#", "msh/US/CA/sacvalley"] +@@ -82,7 +82,7 @@ password = large4cats + # ------------------------- + [database] + # SQLAlchemy connection string. This one uses SQLite with asyncio support. +-connection_string = sqlite+aiosqlite:///packets.db ++connection_string = sqlite+aiosqlite:////var/lib/meshview/packets.db + + + # ------------------------- +@@ -110,4 +110,4 @@ vacuum = False + # Set to True to enable, False to disable (default: False) + access_log = False + # Database cleanup logfile +-db_cleanup_logfile = dbcleanup.log ++db_cleanup_logfile = /var/log/meshview/dbcleanup.log diff --git a/create_example_migration.py b/create_example_migration.py new file mode 100755 index 00000000..1eb2aa92 --- /dev/null +++ b/create_example_migration.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +""" +Script to create a blank migration for manual editing. + +Usage: + ./env/bin/python create_example_migration.py + +This creates an empty migration file that you can manually edit to add +custom migration logic (data migrations, complex schema changes, etc.) + +Unlike create_migration.py which auto-generates from model changes, +this creates a blank template for you to fill in. +""" + +import os +import sys + +# Add current directory to path +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + +from alembic.config import Config + +from alembic import command + +# Create Alembic config +alembic_cfg = Config("alembic.ini") + +# Set database URL from meshview config +try: + from meshview.config import CONFIG + + database_url = CONFIG["database"]["connection_string"] + alembic_cfg.set_main_option("sqlalchemy.url", database_url) + print(f"Using database URL from config: {database_url}") +except Exception as e: + print(f"Warning: Could not load meshview config: {e}") + print("Using default database URL") + alembic_cfg.set_main_option("sqlalchemy.url", "sqlite+aiosqlite:///packets.db") + +# Generate blank migration +try: + print("Creating blank migration for manual editing...") + command.revision(alembic_cfg, autogenerate=False, message="Manual migration") + print("✓ Successfully created blank migration!") + print("\nNow edit the generated file in alembic/versions/") + print("Add your custom upgrade() and downgrade() logic") +except Exception as e: + print(f"✗ Error creating migration: {e}") + import traceback + + traceback.print_exc() + sys.exit(1) diff --git a/create_migration.py b/create_migration.py new file mode 100755 index 00000000..a242655f --- /dev/null +++ b/create_migration.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 +""" +Helper script to create Alembic migrations from SQLAlchemy model changes. + +Usage: + ./env/bin/python create_migration.py + +This will: +1. Load your current models from meshview/models.py +2. Compare them to the current database schema +3. Auto-generate a migration with the detected changes +4. Save the migration to alembic/versions/ + +After running this, review the generated migration file before committing! +""" + +import os +import sys + +# Add current directory to path +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + +from alembic.config import Config + +from alembic import command + +# Create Alembic config +alembic_cfg = Config("alembic.ini") + +# Set database URL from meshview config +try: + from meshview.config import CONFIG + + database_url = CONFIG["database"]["connection_string"] + alembic_cfg.set_main_option("sqlalchemy.url", database_url) + print(f"Using database URL from config: {database_url}") +except Exception as e: + print(f"Warning: Could not load meshview config: {e}") + print("Using default database URL") + alembic_cfg.set_main_option("sqlalchemy.url", "sqlite+aiosqlite:///packets.db") + +# Generate migration +try: + print("\nComparing models to current database schema...") + print("Generating migration...\n") + command.revision(alembic_cfg, autogenerate=True, message="Auto-generated migration") + print("\n✓ Successfully created migration!") + print("\nNext steps:") + print("1. Review the generated file in alembic/versions/") + print("2. Edit the migration message/logic if needed") + print("3. Test the migration: ./env/bin/alembic upgrade head") + print("4. Commit the migration file to version control") +except Exception as e: + print(f"\n✗ Error creating migration: {e}") + import traceback + + traceback.print_exc() + sys.exit(1) diff --git a/docker/README.md b/docker/README.md new file mode 100644 index 00000000..e2497123 --- /dev/null +++ b/docker/README.md @@ -0,0 +1,36 @@ +# MeshView Docker Container + +> **Note:** This directory contains legacy Docker build files. +> +> **For current Docker usage instructions, please see [README-Docker.md](../README-Docker.md) in the project root.** + +## Current Approach + +Pre-built container images are automatically built and published to GitHub Container Registry: + +```bash +docker pull ghcr.io/pablorevilla-meshtastic/meshview:latest +``` + +See **[README-Docker.md](../README-Docker.md)** for: +- Quick start instructions +- Volume mount configuration +- Docker Compose examples +- Backup configuration +- Troubleshooting + +## Legacy Build (Not Recommended) + +If you need to build your own image for development: + +```bash +# From project root +docker build -f Containerfile -t meshview:local . +``` + +The current Containerfile uses: +- **Base Image**: `python:3.13-slim` (Debian-based) +- **Build tool**: `uv` for fast dependency installation +- **User**: Non-root user `app` (UID 10001) +- **Exposed Port**: `8081` +- **Volumes**: `/etc/meshview`, `/var/lib/meshview`, `/var/log/meshview` diff --git a/docs/ALEMBIC_SETUP.md b/docs/ALEMBIC_SETUP.md new file mode 100644 index 00000000..0905673f --- /dev/null +++ b/docs/ALEMBIC_SETUP.md @@ -0,0 +1,361 @@ +# Alembic Database Migration Setup + +This document describes the automatic database migration system implemented for MeshView using Alembic. + +## Overview + +The system provides automatic database schema migrations with coordination between the writer app (startdb.py) and reader app (web.py): + +- **Writer App**: Automatically runs pending migrations on startup +- **Reader App**: Waits for migrations to complete before starting + +## Architecture + +### Key Components + +1. **`meshview/migrations.py`** - Migration management utilities + - `run_migrations()` - Runs pending migrations (writer app) + - `wait_for_migrations()` - Waits for schema to be current (reader app) + - `is_database_up_to_date()` - Checks schema version + - Migration status tracking table + +2. **`alembic/`** - Alembic migration directory + - `env.py` - Configured for async SQLAlchemy support + - `versions/` - Migration scripts directory + - `alembic.ini` - Alembic configuration + +3. **Modified Apps**: + - `startdb.py` - Writer app that runs migrations before MQTT ingestion + - `meshview/web.py` - Reader app that waits for schema updates + +## How It Works - Automatic In-Place Updates + +### ✨ Fully Automatic Operation + +**No manual migration commands needed!** The database schema updates automatically when you: +1. Deploy new code with migration files +2. Restart the applications + +### Writer App (startdb.py) Startup Sequence + +1. Initialize database connection +2. Create migration status tracking table +3. Set "migration in progress" flag +4. **🔄 Automatically run any pending Alembic migrations** (synchronously) + - Detects current schema version + - Compares to latest available migration + - Runs all pending migrations in sequence + - Updates database schema in place +5. Clear "migration in progress" flag +6. Start MQTT ingestion and other tasks + +### Reader App (web.py) Startup Sequence + +1. Initialize database connection +2. **Check database schema version** +3. If not up to date: + - Wait up to 60 seconds (30 retries × 2 seconds) + - Check every 2 seconds for schema updates + - Automatically proceeds once writer completes migrations +4. Once schema is current, start web server + +### 🎯 Key Point: Zero Manual Steps + +When you deploy new code with migrations: +```bash +# Just start the apps - migrations happen automatically! +./env/bin/python startdb.py # Migrations run here automatically +./env/bin/python main.py # Waits for migrations, then starts +``` + +**The database updates itself!** No need to run `alembic upgrade` manually. + +### Coordination + +The apps coordinate using: +- **Alembic version table** (`alembic_version`) - Tracks current schema version +- **Migration status table** (`migration_status`) - Optional flag for "in progress" state + +## Creating New Migrations + +### Using the helper script: + +```bash +./env/bin/python create_migration.py +``` + +### Manual creation: + +```bash +./env/bin/alembic revision --autogenerate -m "Description of changes" +``` + +This will: +1. Compare current database schema with SQLAlchemy models +2. Generate a migration script in `alembic/versions/` +3. Automatically detect most schema changes + +### Manual migration (advanced): + +```bash +./env/bin/alembic revision -m "Manual migration" +``` + +Then edit the generated file to add custom migration logic. + +## Running Migrations + +### Automatic (Recommended) + +Migrations run automatically when the writer app starts: + +```bash +./env/bin/python startdb.py +``` + +### Manual + +To run migrations manually: + +```bash +./env/bin/alembic upgrade head +``` + +To downgrade: + +```bash +./env/bin/alembic downgrade -1 # Go back one version +./env/bin/alembic downgrade base # Go back to beginning +``` + +## Checking Migration Status + +Check current database version: + +```bash +./env/bin/alembic current +``` + +View migration history: + +```bash +./env/bin/alembic history +``` + +## Benefits + +1. **Zero Manual Intervention**: Migrations run automatically on startup +2. **Safe Coordination**: Reader won't connect to incompatible schema +3. **Version Control**: All schema changes tracked in git +4. **Rollback Capability**: Can downgrade if needed +5. **Auto-generation**: Most migrations created automatically from model changes + +## Migration Workflow + +### Development Process + +1. **Modify SQLAlchemy models** in `meshview/models.py` +2. **Create migration**: + ```bash + ./env/bin/python create_migration.py + ``` +3. **Review generated migration** in `alembic/versions/` +4. **Test migration**: + - Stop all apps + - Start writer app (migrations run automatically) + - Start reader app (waits for schema to be current) +5. **Commit migration** to version control + +### Production Deployment + +1. **Deploy new code** with migration scripts +2. **Start writer app** - Migrations run automatically +3. **Start reader app** - Waits for migrations, then starts +4. **Monitor logs** for migration success + +## Troubleshooting + +### Migration fails + +Check logs in writer app for error details. To manually fix: + +```bash +./env/bin/alembic current # Check current version +./env/bin/alembic history # View available versions +./env/bin/alembic upgrade head # Try manual upgrade +``` + +### Reader app won't start (timeout) + +Check if writer app is running and has completed migrations: + +```bash +./env/bin/alembic current +``` + +### Reset to clean state + +⚠️ **Warning: This will lose all data** + +```bash +rm packets.db # Or your database file +./env/bin/alembic upgrade head # Create fresh schema +``` + +## File Structure + +``` +meshview/ +├── alembic.ini # Alembic configuration +├── alembic/ +│ ├── env.py # Async-enabled migration runner +│ ├── script.py.mako # Migration template +│ └── versions/ # Migration scripts +│ └── c88468b7ab0b_initial_migration.py +├── meshview/ +│ ├── models.py # SQLAlchemy models (source of truth) +│ ├── migrations.py # Migration utilities +│ ├── mqtt_database.py # Writer database connection +│ └── database.py # Reader database connection +├── startdb.py # Writer app (runs migrations) +├── main.py # Entry point for reader app +└── create_migration.py # Helper script for creating migrations +``` + +## Configuration + +Database URL is read from `config.ini`: + +```ini +[database] +connection_string = sqlite+aiosqlite:///packets.db +``` + +Alembic automatically uses this configuration through `meshview/migrations.py`. + +## Important Notes + +1. **Always test migrations** in development before deploying to production +2. **Backup database** before running migrations in production +3. **Check for data loss** - Some migrations may require data migration logic +4. **Coordinate deployments** - Start writer before readers in multi-instance setups +5. **Monitor logs** during first startup after deployment + +## Example Migrations + +### Example 1: Generated Initial Migration + +Here's what an auto-generated migration looks like (from comparing models to database): + +```python +"""Initial migration + +Revision ID: c88468b7ab0b +Revises: +Create Date: 2025-01-26 20:56:50.123456 + +""" +from alembic import op +import sqlalchemy as sa + +# revision identifiers +revision = 'c88468b7ab0b' +down_revision = None +branch_labels = None +depends_on = None + +def upgrade() -> None: + # Upgrade operations + op.create_table('node', + sa.Column('id', sa.String(), nullable=False), + sa.Column('node_id', sa.BigInteger(), nullable=True), + # ... more columns + sa.PrimaryKeyConstraint('id') + ) + +def downgrade() -> None: + # Downgrade operations + op.drop_table('node') +``` + +### Example 2: Manual Migration Adding a New Table + +We've included an example migration (`1717fa5c6545_add_example_table.py`) that demonstrates how to manually create a new table: + +```python +"""Add example table + +Revision ID: 1717fa5c6545 +Revises: c88468b7ab0b +Create Date: 2025-10-26 20:59:04.347066 +""" +from typing import Sequence, Union +from alembic import op +import sqlalchemy as sa + +def upgrade() -> None: + """Create example table with sample columns.""" + op.create_table( + 'example', + sa.Column('id', sa.Integer(), nullable=False, primary_key=True, autoincrement=True), + sa.Column('name', sa.String(length=100), nullable=False), + sa.Column('description', sa.Text(), nullable=True), + sa.Column('value', sa.Float(), nullable=True), + sa.Column('is_active', sa.Boolean(), nullable=False, server_default='1'), + sa.Column('created_at', sa.DateTime(), nullable=False, + server_default=sa.text('CURRENT_TIMESTAMP')), + sa.Column('updated_at', sa.DateTime(), nullable=True), + sa.PrimaryKeyConstraint('id') + ) + + # Create an index on the name column for faster lookups + op.create_index('idx_example_name', 'example', ['name']) + +def downgrade() -> None: + """Remove example table.""" + op.drop_index('idx_example_name', table_name='example') + op.drop_table('example') +``` + +**Key features demonstrated:** +- Various column types (Integer, String, Text, Float, Boolean, DateTime) +- Primary key with autoincrement +- Nullable and non-nullable columns +- Server defaults (for timestamps and booleans) +- Creating indexes +- Proper downgrade that reverses all changes + +**To test this migration:** + +```bash +# Apply the migration +./env/bin/alembic upgrade head + +# Check it was applied +./env/bin/alembic current + +# Verify table was created +sqlite3 packetsPL.db "SELECT sql FROM sqlite_master WHERE type='table' AND name='example';" + +# Roll back the migration +./env/bin/alembic downgrade -1 + +# Verify table was removed +sqlite3 packetsPL.db "SELECT name FROM sqlite_master WHERE type='table' AND name='example';" +``` + +**To remove this example migration** (after testing): + +```bash +# First make sure you're not on this revision +./env/bin/alembic downgrade c88468b7ab0b + +# Then delete the migration file +rm alembic/versions/1717fa5c6545_add_example_table.py +``` + +## References + +- [Alembic Documentation](https://alembic.sqlalchemy.org/) +- [SQLAlchemy Documentation](https://docs.sqlalchemy.org/) +- [Async SQLAlchemy](https://docs.sqlalchemy.org/en/20/orm/extensions/asyncio.html) \ No newline at end of file diff --git a/docs/API_Documentation.md b/docs/API_Documentation.md new file mode 100644 index 00000000..ae16f8fb --- /dev/null +++ b/docs/API_Documentation.md @@ -0,0 +1,391 @@ +# API Documentation + +Base URL: `http(s)://` + +All endpoints return JSON. Timestamps are either ISO 8601 strings or `*_us` values in +microseconds since epoch. + +## 1. Nodes API + +### GET `/api/nodes` +Returns a list of nodes, with optional filtering. + +Query Parameters +- `node_id` (optional, int): Exact node ID. +- `role` (optional, string): Node role. +- `channel` (optional, string): Channel name. +- `hw_model` (optional, string): Hardware model. +- `days_active` (optional, int): Nodes seen within the last N days. + +Response Example +```json +{ + "nodes": [ + { + "id": 42, + "node_id": 1234, + "long_name": "Alice", + "short_name": "A", + "hw_model": "T-Beam", + "firmware": "1.2.3", + "role": "client", + "last_lat": 377749000, + "last_long": -1224194000, + "channel": "main", + "last_seen_us": 1736370123456789 + } + ] +} +``` + +--- + +## 2. Packets API + +### GET `/api/packets` +Returns packets with optional filters. + +Query Parameters +- `packet_id` (optional, int): Return exactly one packet (overrides other filters). +- `limit` (optional, int): Max packets to return, clamped 1-1000. Default: `50`. +- `since` (optional, int): Only packets imported after this microsecond timestamp. +- `portnum` (optional, int): Filter by port number. +- `contains` (optional, string): Payload substring filter. +- `from_node_id` (optional, int): Filter by sender node ID. +- `to_node_id` (optional, int): Filter by recipient node ID. +- `node_id` (optional, int): Legacy filter matching either from or to node ID. + +Response Example +```json +{ + "packets": [ + { + "id": 123, + "import_time_us": 1736370123456789, + "channel": "main", + "from_node_id": 5678, + "to_node_id": 91011, + "portnum": 1, + "long_name": "Alice", + "payload": "Hello, Bob!", + "to_long_name": "Bob", + "reply_id": 122 + } + ], + "latest_import_time": 1736370123456789 +} +``` + +Notes +- For `portnum=1` (text messages), packets are filtered to remove sequence-only payloads. +- `latest_import_time` is returned when available for incremental polling (microseconds). + +--- + +## 3. Channels API + +### GET `/api/channels` +Returns channels seen in a time period. + +Query Parameters +- `period_type` (optional, string): `hour` or `day`. Default: `hour`. +- `length` (optional, int): Number of periods to look back. Default: `24`. + +Response Example +```json +{ + "channels": ["LongFast", "MediumFast", "ShortFast"] +} +``` + +--- + +## 4. Stats API + +### GET `/api/stats` +Returns packet statistics aggregated by time periods, with optional filtering. + +Query Parameters +- `period_type` (optional, string): `hour` or `day`. Default: `hour`. +- `length` (optional, int): Number of periods to include. Default: `24`. +- `channel` (optional, string): Filter by channel (case-insensitive). +- `portnum` (optional, int): Filter by port number. +- `to_node` (optional, int): Filter by destination node ID. +- `from_node` (optional, int): Filter by source node ID. +- `node` (optional, int): If provided, return combined `sent` and `seen` totals for that node. + +Response Example (series) +```json +{ + "period_type": "hour", + "length": 24, + "channel": "LongFast", + "portnum": 1, + "to_node": 12345678, + "from_node": 87654321, + "data": [ + { "period": "2025-08-08 14:00", "count": 10 }, + { "period": "2025-08-08 15:00", "count": 7 } + ] +} +``` + +Response Example (`node` totals) +```json +{ + "node_id": 12345678, + "period_type": "hour", + "length": 24, + "sent": 42, + "seen": 58 +} +``` + +--- + +### GET `/api/stats/count` +Returns total packet counts, optionally filtered. + +Query Parameters +- `packet_id` (optional, int): Filter packet_seen by packet ID. +- `period_type` (optional, string): `hour` or `day`. +- `length` (optional, int): Number of periods to include. +- `channel` (optional, string): Filter by channel. +- `from_node` (optional, int): Filter by source node ID. +- `to_node` (optional, int): Filter by destination node ID. + +Response Example +```json +{ + "total_packets": 12345, + "total_seen": 67890 +} +``` + +--- + +### GET `/api/stats/top` +Returns nodes sorted by packets seen, with pagination. + +Query Parameters +- `period_type` (optional, string): `hour` or `day`. Default: `day`. +- `length` (optional, int): Number of periods to include. Default: `1`. +- `channel` (optional, string): Filter by channel. +- `limit` (optional, int): Max nodes to return. Default: `20`, max `100`. +- `offset` (optional, int): Pagination offset. Default: `0`. + +Response Example +```json +{ + "total": 250, + "limit": 20, + "offset": 0, + "nodes": [ + { + "node_id": 1234, + "long_name": "Alice", + "short_name": "A", + "channel": "main", + "sent": 100, + "seen": 240, + "avg": 2.4 + } + ] +} +``` + +--- + +## 5. Edges API + +### GET `/api/edges` +Returns network edges (connections between nodes) based on traceroutes and neighbor info. +Traceroute edges are collected over the last 48 hours. Neighbor edges are based on +port 71 packets. + +Query Parameters +- `type` (optional, string): `traceroute` or `neighbor`. If omitted, returns both. +- `node_id` (optional, int): Filter edges to only those touching a node. + +Response Example +```json +{ + "edges": [ + { "from": 12345678, "to": 87654321, "type": "traceroute" }, + { "from": 11111111, "to": 22222222, "type": "neighbor" } + ] +} +``` + +--- + +## 6. Config API + +### GET `/api/config` +Returns a safe subset of server configuration. + +Response Example +```json +{ + "site": { + "domain": "example.com", + "language": "en", + "title": "Meshview", + "message": "", + "starting": "/chat", + "nodes": "true", + "chat": "true", + "everything": "true", + "graphs": "true", + "stats": "true", + "net": "true", + "map": "true", + "top": "true", + "map_top_left_lat": 39.0, + "map_top_left_lon": -123.0, + "map_bottom_right_lat": 36.0, + "map_bottom_right_lon": -121.0, + "map_interval": 3, + "firehose_interval": 3, + "weekly_net_message": "Weekly Mesh check-in message.", + "net_tag": "#BayMeshNet", + "version": "3.0.0" + }, + "mqtt": { + "server": "mqtt.example.com", + "topics": ["msh/region/#"] + }, + "cleanup": { + "enabled": "false", + "days_to_keep": "14", + "hour": "2", + "minute": "0", + "vacuum": "false" + } +} +``` + +--- + +## 7. Language API + +### GET `/api/lang` +Returns translation strings. + +Query Parameters +- `lang` (optional, string): Language code (e.g., `en`, `es`). Default from config or `en`. +- `section` (optional, string): Return only one section (e.g., `nodelist`, `firehose`). + +Response Example +```json +{ + "title": "Meshview", + "search_placeholder": "Search..." +} +``` + +--- + +## 8. Packets Seen API + +### GET `/api/packets_seen/{packet_id}` +Returns packet_seen entries for a packet. + +Path Parameters +- `packet_id` (required, int): Packet ID. + +Response Example +```json +{ + "seen": [ + { + "packet_id": 123, + "node_id": 456, + "rx_time": "2025-07-22T12:45:00", + "hop_limit": 7, + "hop_start": 0, + "channel": "main", + "rx_snr": 5.0, + "rx_rssi": -90, + "topic": "msh/region/#", + "import_time_us": 1736370123456789 + } + ] +} +``` + +--- + +## 9. Traceroute API + +### GET `/api/traceroute/{packet_id}` +Returns traceroute details and derived paths for a packet. + +Path Parameters +- `packet_id` (required, int): Packet ID. + +Response Example +```json +{ + "packet": { + "id": 123, + "from": 111, + "to": 222, + "channel": "main" + }, + "traceroute_packets": [ + { + "index": 0, + "gateway_node_id": 333, + "done": true, + "forward_hops": [111, 444, 222], + "reverse_hops": [222, 444, 111] + } + ], + "unique_forward_paths": [ + { "path": [111, 444, 222], "count": 2 } + ], + "unique_reverse_paths": [ + [222, 444, 111] + ], + "winning_paths": [ + [111, 444, 222] + ] +} +``` + +--- + +## 10. Health API + +### GET `/health` +Returns service health and database status. + +Response Example +```json +{ + "status": "healthy", + "timestamp": "2025-07-22T12:45:00+00:00", + "version": "3.0.0", + "git_revision": "abc1234", + "database": "connected", + "database_size": "12.34 MB", + "database_size_bytes": 12939444 +} +``` + +--- + +## 11. Version API + +### GET `/version` +Returns version metadata. + +Response Example +```json +{ + "version": "3.0.0", + "git_revision": "abc1234", + "build_time": "2025-11-01T12:00:00+00:00" +} +``` diff --git a/docs/Database-Changes-With-Alembic.md b/docs/Database-Changes-With-Alembic.md new file mode 100644 index 00000000..453dfa5a --- /dev/null +++ b/docs/Database-Changes-With-Alembic.md @@ -0,0 +1,146 @@ +# Database Changes With Alembic + +This guide explains how to make database schema changes in MeshView using Alembic migrations. + +## Overview + +When you need to add, modify, or remove columns from database tables, you must: +1. Update the SQLAlchemy model +2. Create an Alembic migration +3. Let the system automatically apply the migration + +## Step-by-Step Process + +### 1. Update the Model + +Edit `meshview/models.py` to add/modify the column in the appropriate model class: + +```python +class Traceroute(Base): + __tablename__ = "traceroute" + + id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) + # ... existing columns ... + route_return: Mapped[bytes] = mapped_column(nullable=True) # New column +``` + +### 2. Create an Alembic Migration + +Generate a new migration file with a descriptive message: + +```bash +./env/bin/alembic revision -m "add route_return to traceroute" +``` + +This creates a new file in `alembic/versions/` with a unique revision ID. + +### 3. Fill in the Migration + +Edit the generated migration file to implement the actual database changes: + +```python +def upgrade() -> None: + # Add route_return column to traceroute table + with op.batch_alter_table('traceroute', schema=None) as batch_op: + batch_op.add_column(sa.Column('route_return', sa.LargeBinary(), nullable=True)) + + +def downgrade() -> None: + # Remove route_return column from traceroute table + with op.batch_alter_table('traceroute', schema=None) as batch_op: + batch_op.drop_column('route_return') +``` + +### 4. Migration Runs Automatically + +When you restart the application with `mvrun.py`: + +1. The writer process (`startdb.py`) starts up +2. It checks if the database schema is up to date +3. If new migrations are pending, it runs them automatically +4. The reader process (web server) waits for migrations to complete before starting + +**No manual migration command is needed** - the application handles this automatically on startup. + +### 5. Commit Both Files + +Add both files to git: + +```bash +git add meshview/models.py +git add alembic/versions/ac311b3782a1_add_route_return_to_traceroute.py +git commit -m "Add route_return column to traceroute table" +``` + +## Important Notes + +### SQLite Compatibility + +Always use `batch_alter_table` for SQLite compatibility: + +```python +with op.batch_alter_table('table_name', schema=None) as batch_op: + batch_op.add_column(...) +``` + +SQLite has limited ALTER TABLE support, and `batch_alter_table` works around these limitations. + +### Migration Process + +- **Writer process** (`startdb.py`): Runs migrations on startup +- **Reader process** (web server in `main.py`): Waits for migrations to complete +- Migrations are checked and applied every time the application starts +- The system uses a migration status table to coordinate between processes + +### Common Column Types + +```python +# Integer +column: Mapped[int] = mapped_column(BigInteger, nullable=True) + +# String +column: Mapped[str] = mapped_column(nullable=True) + +# Bytes/Binary +column: Mapped[bytes] = mapped_column(nullable=True) + +# DateTime +column: Mapped[datetime] = mapped_column(nullable=True) + +# Boolean +column: Mapped[bool] = mapped_column(nullable=True) + +# Float +column: Mapped[float] = mapped_column(nullable=True) +``` + +### Migration File Location + +Migrations are stored in: `alembic/versions/` + +Each migration file includes: +- Revision ID (unique identifier) +- Down revision (previous migration in chain) +- Create date +- `upgrade()` function (applies changes) +- `downgrade()` function (reverts changes) + +## Troubleshooting + +### Migration Not Running + +If migrations don't run automatically: + +1. Check that the database is writable +2. Look for errors in the startup logs +3. Verify the migration chain is correct (each migration references the previous one) + +### Manual Migration (Not Recommended) + +If you need to manually run migrations for debugging: + +```bash +./env/bin/alembic upgrade head +``` + +However, the application normally handles this automatically. diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..552da62b --- /dev/null +++ b/docs/README.md @@ -0,0 +1,14 @@ +# Technical Documentation + +This directory contains technical documentation for MeshView that goes beyond initial setup and basic usage. + +These documents are intended for developers, contributors, and advanced users who need deeper insight into the system's architecture, database migrations, API endpoints, and internal workings. + +## Contents + +- [ALEMBIC_SETUP.md](ALEMBIC_SETUP.md) - Database migration setup and management +- [TIMESTAMP_MIGRATION.md](TIMESTAMP_MIGRATION.md) - Details on timestamp schema changes +- [API_Documentation.md](API_Documentation.md) - REST API endpoints and usage +- [CODE_IMPROVEMENTS.md](CODE_IMPROVEMENTS.md) - Suggested code improvements and refactoring ideas + +For initial setup and basic usage instructions, please see the main [README.md](../README.md) in the root directory. diff --git a/docs/TIMESTAMP_MIGRATION.md b/docs/TIMESTAMP_MIGRATION.md new file mode 100644 index 00000000..2197ef43 --- /dev/null +++ b/docs/TIMESTAMP_MIGRATION.md @@ -0,0 +1,193 @@ +# High-Resolution Timestamp Migration + +This document describes the implementation of GitHub issue #55: storing high-resolution timestamps as integers in the database for improved performance and query efficiency. + +## Overview + +The meshview database now stores timestamps in two formats: +1. **TEXT format** (`import_time`): Human-readable ISO8601 format with microseconds (e.g., `2025-03-12 04:15:56.058038`) +2. **INTEGER format** (`import_time_us`): Microseconds since Unix epoch (1970-01-01 00:00:00 UTC) + +The dual format approach provides: +- **Backward compatibility**: Existing `import_time` TEXT columns remain unchanged +- **Performance**: Fast integer comparisons and math operations +- **Precision**: Microsecond resolution for accurate timing +- **Efficiency**: Compact storage and fast indexed lookups + +## Database Changes + +### New Columns Added + +Three tables have new `import_time_us` columns: + +1. **packet.import_time_us** (INTEGER) + - Stores when the packet was imported into the database + - Indexed for fast queries + +2. **packet_seen.import_time_us** (INTEGER) + - Stores when the packet_seen record was imported + - Indexed for performance + +3. **traceroute.import_time_us** (INTEGER) + - Stores when the traceroute was imported + - Indexed for fast lookups + +### New Indexes + +The following indexes were created for optimal query performance: + +```sql +CREATE INDEX idx_packet_import_time_us ON packet(import_time_us DESC); +CREATE INDEX idx_packet_from_node_time_us ON packet(from_node_id, import_time_us DESC); +CREATE INDEX idx_packet_seen_import_time_us ON packet_seen(import_time_us); +CREATE INDEX idx_traceroute_import_time_us ON traceroute(import_time_us); +``` + +## Migration Process + +### For Existing Databases + +Run the migration script to add the new columns and populate them from existing data: + +```bash +python migrate_add_timestamp_us.py [database_path] +``` + +If no path is provided, it defaults to `packets.db` in the current directory. + +The migration script: +1. Checks if migration is needed (idempotent) +2. Adds `import_time_us` columns to the three tables +3. Populates the new columns from existing `import_time` values +4. Creates indexes for optimal performance +5. Verifies the migration completed successfully + +### For New Databases + +New databases created with the updated schema will automatically include the `import_time_us` columns. The MQTT store module populates both columns when inserting new records. + +## Code Changes + +### Models (meshview/models.py) + +The ORM models now include the new `import_time_us` fields: + +```python +class Packet(Base): + import_time: Mapped[datetime] = mapped_column(nullable=True) + import_time_us: Mapped[int] = mapped_column(BigInteger, nullable=True) +``` + +### MQTT Store (meshview/mqtt_store.py) + +The data ingestion logic now populates both timestamp columns using UTC time: + +```python +now = datetime.datetime.now(datetime.timezone.utc) +now_us = int(now.timestamp() * 1_000_000) + +# Both columns are populated +import_time=now, +import_time_us=now_us, +``` + +**Important**: All new timestamps use UTC (Coordinated Universal Time) for consistency across time zones. + +## Using the New Timestamps + +### Example Queries + +**Query packets from the last 7 days:** + +```sql +-- Old way (slower) +SELECT * FROM packet +WHERE import_time >= datetime('now', '-7 days'); + +-- New way (faster) +SELECT * FROM packet +WHERE import_time_us >= (strftime('%s', 'now', '-7 days') * 1000000); +``` + +**Query packets in a specific time range:** + +```sql +SELECT * FROM packet +WHERE import_time_us BETWEEN 1759254380000000 AND 1759254390000000; +``` + +**Calculate time differences (in microseconds):** + +```sql +SELECT + id, + (import_time_us - LAG(import_time_us) OVER (ORDER BY import_time_us)) / 1000000.0 as seconds_since_last +FROM packet +LIMIT 10; +``` + +### Converting Timestamps + +**From datetime to microseconds (UTC):** +```python +import datetime +now = datetime.datetime.now(datetime.timezone.utc) +now_us = int(now.timestamp() * 1_000_000) +``` + +**From microseconds to datetime:** +```python +import datetime +timestamp_us = 1759254380813451 +dt = datetime.datetime.fromtimestamp(timestamp_us / 1_000_000) +``` + +**In SQL queries:** +```sql +-- Datetime to microseconds +SELECT CAST((strftime('%s', import_time) || substr(import_time, 21, 6)) AS INTEGER); + +-- Microseconds to datetime (approximate) +SELECT datetime(import_time_us / 1000000, 'unixepoch'); +``` + +## Performance Benefits + +The integer timestamp columns provide significant performance improvements: + +1. **Faster comparisons**: Integer comparisons are much faster than string/datetime comparisons +2. **Smaller index size**: Integer indexes are more compact than datetime indexes +3. **Range queries**: BETWEEN operations on integers are highly optimized +4. **Math operations**: Easy to calculate time differences, averages, etc. +5. **Sorting**: Integer sorting is faster than datetime sorting + +## Backward Compatibility + +The original `import_time` TEXT columns remain unchanged: +- Existing code continues to work +- Human-readable timestamps still available +- Gradual migration to new columns possible +- No breaking changes for existing queries + +## Future Work + +Future improvements could include: +- Migrating queries to use `import_time_us` columns +- Deprecating the TEXT `import_time` columns (after transition period) +- Adding helper functions for timestamp conversion +- Creating views that expose both formats + +## Testing + +The migration was tested on a production database with: +- 132,466 packet records +- 1,385,659 packet_seen records +- 28,414 traceroute records + +All records were successfully migrated with microsecond precision preserved. + +## References + +- GitHub Issue: #55 - Storing High-Resolution Timestamps in SQLite +- SQLite datetime functions: https://www.sqlite.org/lang_datefunc.html +- Python datetime module: https://docs.python.org/3/library/datetime.html diff --git a/gen_proto.sh b/gen_proto.sh deleted file mode 100755 index fd611b41..00000000 --- a/gen_proto.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/sh -protoc \ - -Iproto_def \ - --python_out=. \ - proto_def/meshtastic/admin.proto \ - proto_def/meshtastic/apponly.proto \ - proto_def/meshtastic/atak.proto \ - proto_def/meshtastic/cannedmessages.proto \ - proto_def/meshtastic/channel.proto \ - proto_def/meshtastic/clientonly.proto \ - proto_def/meshtastic/config.proto \ - proto_def/meshtastic/connection_status.proto \ - proto_def/meshtastic/localonly.proto \ - proto_def/meshtastic/mesh.proto \ - proto_def/meshtastic/module_config.proto \ - proto_def/meshtastic/mqtt.proto \ - proto_def/meshtastic/paxcount.proto \ - proto_def/meshtastic/portnums.proto \ - proto_def/meshtastic/remote_hardware.proto \ - proto_def/meshtastic/rtttl.proto \ - proto_def/meshtastic/storeforward.proto \ - proto_def/meshtastic/telemetry.proto \ - proto_def/meshtastic/xmodem.proto diff --git a/main.py b/main.py index 05fd1b45..43c4a1e7 100644 --- a/main.py +++ b/main.py @@ -1,41 +1,12 @@ import asyncio -import argparse -from meshview import mqtt_reader -from meshview import database -from meshview import store from meshview import web -from meshview import http -async def load_database_from_mqtt(mqtt_server, topic): - async for topic, env in mqtt_reader.get_topic_envelopes(mqtt_server, topic): - await store.process_envelope(topic, env) - - -async def main(args): - database.init_database(args.database) - - await database.create_tables() +async def main(): async with asyncio.TaskGroup() as tg: - tg.create_task(load_database_from_mqtt(args.mqtt_server, args.topic)) - tg.create_task(web.run_server(args.bind, args.port, args.tls_cert)) - if args.acme_challenge: - tg.create_task(http.run_server(args.bind, args.acme_challenge)) + tg.create_task(web.run_server()) if __name__ == '__main__': - parser = argparse.ArgumentParser('meshview') - parser.add_argument('--bind', nargs='*', default=['::1']) - parser.add_argument('--acme-challenge') - parser.add_argument('--port', default=8080, type=int) - parser.add_argument('--tls-cert') - - parser.add_argument('--mqtt-server', default='mqtt.meshtastic.org') - parser.add_argument('--topic', nargs='*', default=['msh/US/#']) - - parser.add_argument('--database', default='sqlite+aiosqlite:///packets.db') - - args = parser.parse_args() - - asyncio.run(main(args)) + asyncio.run(main()) diff --git a/meshtastic/admin_pb2.py b/meshtastic/admin_pb2.py deleted file mode 100644 index 6e5a69b2..00000000 --- a/meshtastic/admin_pb2.py +++ /dev/null @@ -1,39 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: meshtastic/admin.proto -"""Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder -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 -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -from meshtastic import channel_pb2 as meshtastic_dot_channel__pb2 -from meshtastic import config_pb2 as meshtastic_dot_config__pb2 -from meshtastic import connection_status_pb2 as meshtastic_dot_connection__status__pb2 -from meshtastic import mesh_pb2 as meshtastic_dot_mesh__pb2 -from meshtastic import module_config_pb2 as meshtastic_dot_module__config__pb2 - - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16meshtastic/admin.proto\x12\nmeshtastic\x1a\x18meshtastic/channel.proto\x1a\x17meshtastic/config.proto\x1a\"meshtastic/connection_status.proto\x1a\x15meshtastic/mesh.proto\x1a\x1emeshtastic/module_config.proto\"\xe8\x12\n\x0c\x41\x64minMessage\x12\x17\n\x0fsession_passkey\x18\x65 \x01(\x0c\x12\x1d\n\x13get_channel_request\x18\x01 \x01(\rH\x00\x12\x33\n\x14get_channel_response\x18\x02 \x01(\x0b\x32\x13.meshtastic.ChannelH\x00\x12\x1b\n\x11get_owner_request\x18\x03 \x01(\x08H\x00\x12.\n\x12get_owner_response\x18\x04 \x01(\x0b\x32\x10.meshtastic.UserH\x00\x12\x41\n\x12get_config_request\x18\x05 \x01(\x0e\x32#.meshtastic.AdminMessage.ConfigTypeH\x00\x12\x31\n\x13get_config_response\x18\x06 \x01(\x0b\x32\x12.meshtastic.ConfigH\x00\x12N\n\x19get_module_config_request\x18\x07 \x01(\x0e\x32).meshtastic.AdminMessage.ModuleConfigTypeH\x00\x12>\n\x1aget_module_config_response\x18\x08 \x01(\x0b\x32\x18.meshtastic.ModuleConfigH\x00\x12\x34\n*get_canned_message_module_messages_request\x18\n \x01(\x08H\x00\x12\x35\n+get_canned_message_module_messages_response\x18\x0b \x01(\tH\x00\x12%\n\x1bget_device_metadata_request\x18\x0c \x01(\x08H\x00\x12\x42\n\x1cget_device_metadata_response\x18\r \x01(\x0b\x32\x1a.meshtastic.DeviceMetadataH\x00\x12\x1e\n\x14get_ringtone_request\x18\x0e \x01(\x08H\x00\x12\x1f\n\x15get_ringtone_response\x18\x0f \x01(\tH\x00\x12.\n$get_device_connection_status_request\x18\x10 \x01(\x08H\x00\x12S\n%get_device_connection_status_response\x18\x11 \x01(\x0b\x32\".meshtastic.DeviceConnectionStatusH\x00\x12\x31\n\x0cset_ham_mode\x18\x12 \x01(\x0b\x32\x19.meshtastic.HamParametersH\x00\x12/\n%get_node_remote_hardware_pins_request\x18\x13 \x01(\x08H\x00\x12\\\n&get_node_remote_hardware_pins_response\x18\x14 \x01(\x0b\x32*.meshtastic.NodeRemoteHardwarePinsResponseH\x00\x12 \n\x16\x65nter_dfu_mode_request\x18\x15 \x01(\x08H\x00\x12\x1d\n\x13\x64\x65lete_file_request\x18\x16 \x01(\tH\x00\x12\x13\n\tset_scale\x18\x17 \x01(\rH\x00\x12%\n\tset_owner\x18 \x01(\x0b\x32\x10.meshtastic.UserH\x00\x12*\n\x0bset_channel\x18! \x01(\x0b\x32\x13.meshtastic.ChannelH\x00\x12(\n\nset_config\x18\" \x01(\x0b\x32\x12.meshtastic.ConfigH\x00\x12\x35\n\x11set_module_config\x18# \x01(\x0b\x32\x18.meshtastic.ModuleConfigH\x00\x12,\n\"set_canned_message_module_messages\x18$ \x01(\tH\x00\x12\x1e\n\x14set_ringtone_message\x18% \x01(\tH\x00\x12\x1b\n\x11remove_by_nodenum\x18& \x01(\rH\x00\x12\x1b\n\x11set_favorite_node\x18\' \x01(\rH\x00\x12\x1e\n\x14remove_favorite_node\x18( \x01(\rH\x00\x12\x32\n\x12set_fixed_position\x18) \x01(\x0b\x32\x14.meshtastic.PositionH\x00\x12\x1f\n\x15remove_fixed_position\x18* \x01(\x08H\x00\x12\x17\n\rset_time_only\x18+ \x01(\x07H\x00\x12\x1d\n\x13\x62\x65gin_edit_settings\x18@ \x01(\x08H\x00\x12\x1e\n\x14\x63ommit_edit_settings\x18\x41 \x01(\x08H\x00\x12\x1e\n\x14\x66\x61\x63tory_reset_device\x18^ \x01(\x05H\x00\x12\x1c\n\x12reboot_ota_seconds\x18_ \x01(\x05H\x00\x12\x18\n\x0e\x65xit_simulator\x18` \x01(\x08H\x00\x12\x18\n\x0ereboot_seconds\x18\x61 \x01(\x05H\x00\x12\x1a\n\x10shutdown_seconds\x18\x62 \x01(\x05H\x00\x12\x1e\n\x14\x66\x61\x63tory_reset_config\x18\x63 \x01(\x05H\x00\x12\x16\n\x0cnodedb_reset\x18\x64 \x01(\x05H\x00\"\xc1\x01\n\nConfigType\x12\x11\n\rDEVICE_CONFIG\x10\x00\x12\x13\n\x0fPOSITION_CONFIG\x10\x01\x12\x10\n\x0cPOWER_CONFIG\x10\x02\x12\x12\n\x0eNETWORK_CONFIG\x10\x03\x12\x12\n\x0e\x44ISPLAY_CONFIG\x10\x04\x12\x0f\n\x0bLORA_CONFIG\x10\x05\x12\x14\n\x10\x42LUETOOTH_CONFIG\x10\x06\x12\x13\n\x0fSECURITY_CONFIG\x10\x07\x12\x15\n\x11SESSIONKEY_CONFIG\x10\x08\"\xbb\x02\n\x10ModuleConfigType\x12\x0f\n\x0bMQTT_CONFIG\x10\x00\x12\x11\n\rSERIAL_CONFIG\x10\x01\x12\x13\n\x0f\x45XTNOTIF_CONFIG\x10\x02\x12\x17\n\x13STOREFORWARD_CONFIG\x10\x03\x12\x14\n\x10RANGETEST_CONFIG\x10\x04\x12\x14\n\x10TELEMETRY_CONFIG\x10\x05\x12\x14\n\x10\x43\x41NNEDMSG_CONFIG\x10\x06\x12\x10\n\x0c\x41UDIO_CONFIG\x10\x07\x12\x19\n\x15REMOTEHARDWARE_CONFIG\x10\x08\x12\x17\n\x13NEIGHBORINFO_CONFIG\x10\t\x12\x1a\n\x16\x41MBIENTLIGHTING_CONFIG\x10\n\x12\x1a\n\x16\x44\x45TECTIONSENSOR_CONFIG\x10\x0b\x12\x15\n\x11PAXCOUNTER_CONFIG\x10\x0c\x42\x11\n\x0fpayload_variant\"[\n\rHamParameters\x12\x11\n\tcall_sign\x18\x01 \x01(\t\x12\x10\n\x08tx_power\x18\x02 \x01(\x05\x12\x11\n\tfrequency\x18\x03 \x01(\x02\x12\x12\n\nshort_name\x18\x04 \x01(\t\"f\n\x1eNodeRemoteHardwarePinsResponse\x12\x44\n\x19node_remote_hardware_pins\x18\x01 \x03(\x0b\x32!.meshtastic.NodeRemoteHardwarePinB`\n\x13\x63om.geeksville.meshB\x0b\x41\x64minProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') - -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.admin_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\013AdminProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' - _ADMINMESSAGE._serialized_start=181 - _ADMINMESSAGE._serialized_end=2589 - _ADMINMESSAGE_CONFIGTYPE._serialized_start=2059 - _ADMINMESSAGE_CONFIGTYPE._serialized_end=2252 - _ADMINMESSAGE_MODULECONFIGTYPE._serialized_start=2255 - _ADMINMESSAGE_MODULECONFIGTYPE._serialized_end=2570 - _HAMPARAMETERS._serialized_start=2591 - _HAMPARAMETERS._serialized_end=2682 - _NODEREMOTEHARDWAREPINSRESPONSE._serialized_start=2684 - _NODEREMOTEHARDWAREPINSRESPONSE._serialized_end=2786 -# @@protoc_insertion_point(module_scope) diff --git a/meshtastic/apponly_pb2.py b/meshtastic/apponly_pb2.py deleted file mode 100644 index 46d9931c..00000000 --- a/meshtastic/apponly_pb2.py +++ /dev/null @@ -1,28 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: meshtastic/apponly.proto -"""Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder -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 -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -from meshtastic import channel_pb2 as meshtastic_dot_channel__pb2 -from meshtastic import config_pb2 as meshtastic_dot_config__pb2 - - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x18meshtastic/apponly.proto\x12\nmeshtastic\x1a\x18meshtastic/channel.proto\x1a\x17meshtastic/config.proto\"o\n\nChannelSet\x12-\n\x08settings\x18\x01 \x03(\x0b\x32\x1b.meshtastic.ChannelSettings\x12\x32\n\x0blora_config\x18\x02 \x01(\x0b\x32\x1d.meshtastic.Config.LoRaConfigBb\n\x13\x63om.geeksville.meshB\rAppOnlyProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') - -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.apponly_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\rAppOnlyProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' - _CHANNELSET._serialized_start=91 - _CHANNELSET._serialized_end=202 -# @@protoc_insertion_point(module_scope) diff --git a/meshtastic/atak_pb2.py b/meshtastic/atak_pb2.py deleted file mode 100644 index a813729d..00000000 --- a/meshtastic/atak_pb2.py +++ /dev/null @@ -1,40 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: meshtastic/atak.proto -"""Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder -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 -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - - - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15meshtastic/atak.proto\x12\nmeshtastic\"\xe6\x01\n\tTAKPacket\x12\x15\n\ris_compressed\x18\x01 \x01(\x08\x12$\n\x07\x63ontact\x18\x02 \x01(\x0b\x32\x13.meshtastic.Contact\x12 \n\x05group\x18\x03 \x01(\x0b\x32\x11.meshtastic.Group\x12\"\n\x06status\x18\x04 \x01(\x0b\x32\x12.meshtastic.Status\x12\x1e\n\x03pli\x18\x05 \x01(\x0b\x32\x0f.meshtastic.PLIH\x00\x12#\n\x04\x63hat\x18\x06 \x01(\x0b\x32\x13.meshtastic.GeoChatH\x00\x42\x11\n\x0fpayload_variant\"\\\n\x07GeoChat\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\x0f\n\x02to\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0bto_callsign\x18\x03 \x01(\tH\x01\x88\x01\x01\x42\x05\n\x03_toB\x0e\n\x0c_to_callsign\"M\n\x05Group\x12$\n\x04role\x18\x01 \x01(\x0e\x32\x16.meshtastic.MemberRole\x12\x1e\n\x04team\x18\x02 \x01(\x0e\x32\x10.meshtastic.Team\"\x19\n\x06Status\x12\x0f\n\x07\x62\x61ttery\x18\x01 \x01(\r\"4\n\x07\x43ontact\x12\x10\n\x08\x63\x61llsign\x18\x01 \x01(\t\x12\x17\n\x0f\x64\x65vice_callsign\x18\x02 \x01(\t\"_\n\x03PLI\x12\x12\n\nlatitude_i\x18\x01 \x01(\x0f\x12\x13\n\x0blongitude_i\x18\x02 \x01(\x0f\x12\x10\n\x08\x61ltitude\x18\x03 \x01(\x05\x12\r\n\x05speed\x18\x04 \x01(\r\x12\x0e\n\x06\x63ourse\x18\x05 \x01(\r*\xc0\x01\n\x04Team\x12\x14\n\x10Unspecifed_Color\x10\x00\x12\t\n\x05White\x10\x01\x12\n\n\x06Yellow\x10\x02\x12\n\n\x06Orange\x10\x03\x12\x0b\n\x07Magenta\x10\x04\x12\x07\n\x03Red\x10\x05\x12\n\n\x06Maroon\x10\x06\x12\n\n\x06Purple\x10\x07\x12\r\n\tDark_Blue\x10\x08\x12\x08\n\x04\x42lue\x10\t\x12\x08\n\x04\x43yan\x10\n\x12\x08\n\x04Teal\x10\x0b\x12\t\n\x05Green\x10\x0c\x12\x0e\n\nDark_Green\x10\r\x12\t\n\x05\x42rown\x10\x0e*\x7f\n\nMemberRole\x12\x0e\n\nUnspecifed\x10\x00\x12\x0e\n\nTeamMember\x10\x01\x12\x0c\n\x08TeamLead\x10\x02\x12\x06\n\x02HQ\x10\x03\x12\n\n\x06Sniper\x10\x04\x12\t\n\x05Medic\x10\x05\x12\x13\n\x0f\x46orwardObserver\x10\x06\x12\x07\n\x03RTO\x10\x07\x12\x06\n\x02K9\x10\x08\x42_\n\x13\x63om.geeksville.meshB\nATAKProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') - -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.atak_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\nATAKProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' - _TEAM._serialized_start=622 - _TEAM._serialized_end=814 - _MEMBERROLE._serialized_start=816 - _MEMBERROLE._serialized_end=943 - _TAKPACKET._serialized_start=38 - _TAKPACKET._serialized_end=268 - _GEOCHAT._serialized_start=270 - _GEOCHAT._serialized_end=362 - _GROUP._serialized_start=364 - _GROUP._serialized_end=441 - _STATUS._serialized_start=443 - _STATUS._serialized_end=468 - _CONTACT._serialized_start=470 - _CONTACT._serialized_end=522 - _PLI._serialized_start=524 - _PLI._serialized_end=619 -# @@protoc_insertion_point(module_scope) diff --git a/meshtastic/cannedmessages_pb2.py b/meshtastic/cannedmessages_pb2.py deleted file mode 100644 index 058f3cfc..00000000 --- a/meshtastic/cannedmessages_pb2.py +++ /dev/null @@ -1,26 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: meshtastic/cannedmessages.proto -"""Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder -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 -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - - - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1fmeshtastic/cannedmessages.proto\x12\nmeshtastic\"-\n\x19\x43\x61nnedMessageModuleConfig\x12\x10\n\x08messages\x18\x01 \x01(\tBn\n\x13\x63om.geeksville.meshB\x19\x43\x61nnedMessageConfigProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') - -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.cannedmessages_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\031CannedMessageConfigProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' - _CANNEDMESSAGEMODULECONFIG._serialized_start=47 - _CANNEDMESSAGEMODULECONFIG._serialized_end=92 -# @@protoc_insertion_point(module_scope) diff --git a/meshtastic/channel_pb2.py b/meshtastic/channel_pb2.py deleted file mode 100644 index 2e66fe8a..00000000 --- a/meshtastic/channel_pb2.py +++ /dev/null @@ -1,34 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: meshtastic/channel.proto -"""Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder -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 -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - - - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x18meshtastic/channel.proto\x12\nmeshtastic\"\xb8\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\x33\n\x0fmodule_settings\x18\x07 \x01(\x0b\x32\x1a.meshtastic.ModuleSettings\"E\n\x0eModuleSettings\x12\x1a\n\x12position_precision\x18\x01 \x01(\r\x12\x17\n\x0fis_client_muted\x18\x02 \x01(\x08\"\xa1\x01\n\x07\x43hannel\x12\r\n\x05index\x18\x01 \x01(\x05\x12-\n\x08settings\x18\x02 \x01(\x0b\x32\x1b.meshtastic.ChannelSettings\x12&\n\x04role\x18\x03 \x01(\x0e\x32\x18.meshtastic.Channel.Role\"0\n\x04Role\x12\x0c\n\x08\x44ISABLED\x10\x00\x12\x0b\n\x07PRIMARY\x10\x01\x12\r\n\tSECONDARY\x10\x02\x42\x62\n\x13\x63om.geeksville.meshB\rChannelProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') - -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.channel_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\rChannelProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' - _CHANNELSETTINGS.fields_by_name['channel_num']._options = None - _CHANNELSETTINGS.fields_by_name['channel_num']._serialized_options = b'\030\001' - _CHANNELSETTINGS._serialized_start=41 - _CHANNELSETTINGS._serialized_end=225 - _MODULESETTINGS._serialized_start=227 - _MODULESETTINGS._serialized_end=296 - _CHANNEL._serialized_start=299 - _CHANNEL._serialized_end=460 - _CHANNEL_ROLE._serialized_start=412 - _CHANNEL_ROLE._serialized_end=460 -# @@protoc_insertion_point(module_scope) diff --git a/meshtastic/clientonly_pb2.py b/meshtastic/clientonly_pb2.py deleted file mode 100644 index 91e7b0d9..00000000 --- a/meshtastic/clientonly_pb2.py +++ /dev/null @@ -1,27 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: meshtastic/clientonly.proto -"""Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder -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 -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -from meshtastic import localonly_pb2 as meshtastic_dot_localonly__pb2 - - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1bmeshtastic/clientonly.proto\x12\nmeshtastic\x1a\x1ameshtastic/localonly.proto\"\x8d\x02\n\rDeviceProfile\x12\x16\n\tlong_name\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x17\n\nshort_name\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0b\x63hannel_url\x18\x03 \x01(\tH\x02\x88\x01\x01\x12,\n\x06\x63onfig\x18\x04 \x01(\x0b\x32\x17.meshtastic.LocalConfigH\x03\x88\x01\x01\x12\x39\n\rmodule_config\x18\x05 \x01(\x0b\x32\x1d.meshtastic.LocalModuleConfigH\x04\x88\x01\x01\x42\x0c\n\n_long_nameB\r\n\x0b_short_nameB\x0e\n\x0c_channel_urlB\t\n\x07_configB\x10\n\x0e_module_configBe\n\x13\x63om.geeksville.meshB\x10\x43lientOnlyProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') - -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.clientonly_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\020ClientOnlyProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' - _DEVICEPROFILE._serialized_start=72 - _DEVICEPROFILE._serialized_end=341 -# @@protoc_insertion_point(module_scope) diff --git a/meshtastic/config_pb2.py b/meshtastic/config_pb2.py deleted file mode 100644 index 785d13c2..00000000 --- a/meshtastic/config_pb2.py +++ /dev/null @@ -1,88 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: meshtastic/config.proto -"""Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder -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 -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - - - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x17meshtastic/config.proto\x12\nmeshtastic\"\xaf%\n\x06\x43onfig\x12\x31\n\x06\x64\x65vice\x18\x01 \x01(\x0b\x32\x1f.meshtastic.Config.DeviceConfigH\x00\x12\x35\n\x08position\x18\x02 \x01(\x0b\x32!.meshtastic.Config.PositionConfigH\x00\x12/\n\x05power\x18\x03 \x01(\x0b\x32\x1e.meshtastic.Config.PowerConfigH\x00\x12\x33\n\x07network\x18\x04 \x01(\x0b\x32 .meshtastic.Config.NetworkConfigH\x00\x12\x33\n\x07\x64isplay\x18\x05 \x01(\x0b\x32 .meshtastic.Config.DisplayConfigH\x00\x12-\n\x04lora\x18\x06 \x01(\x0b\x32\x1d.meshtastic.Config.LoRaConfigH\x00\x12\x37\n\tbluetooth\x18\x07 \x01(\x0b\x32\".meshtastic.Config.BluetoothConfigH\x00\x12\x35\n\x08security\x18\x08 \x01(\x0b\x32!.meshtastic.Config.SecurityConfigH\x00\x12\x39\n\nsessionkey\x18\t \x01(\x0b\x32#.meshtastic.Config.SessionkeyConfigH\x00\x1a\xa1\x05\n\x0c\x44\x65viceConfig\x12\x32\n\x04role\x18\x01 \x01(\x0e\x32$.meshtastic.Config.DeviceConfig.Role\x12\x1a\n\x0eserial_enabled\x18\x02 \x01(\x08\x42\x02\x18\x01\x12\x1d\n\x11\x64\x65\x62ug_log_enabled\x18\x03 \x01(\x08\x42\x02\x18\x01\x12\x13\n\x0b\x62utton_gpio\x18\x04 \x01(\r\x12\x13\n\x0b\x62uzzer_gpio\x18\x05 \x01(\r\x12I\n\x10rebroadcast_mode\x18\x06 \x01(\x0e\x32/.meshtastic.Config.DeviceConfig.RebroadcastMode\x12 \n\x18node_info_broadcast_secs\x18\x07 \x01(\r\x12\"\n\x1a\x64ouble_tap_as_button_press\x18\x08 \x01(\x08\x12\x16\n\nis_managed\x18\t \x01(\x08\x42\x02\x18\x01\x12\x1c\n\x14\x64isable_triple_click\x18\n \x01(\x08\x12\r\n\x05tzdef\x18\x0b \x01(\t\x12\x1e\n\x16led_heartbeat_disabled\x18\x0c \x01(\x08\"\xae\x01\n\x04Role\x12\n\n\x06\x43LIENT\x10\x00\x12\x0f\n\x0b\x43LIENT_MUTE\x10\x01\x12\n\n\x06ROUTER\x10\x02\x12\x15\n\rROUTER_CLIENT\x10\x03\x1a\x02\x08\x01\x12\x0c\n\x08REPEATER\x10\x04\x12\x0b\n\x07TRACKER\x10\x05\x12\n\n\x06SENSOR\x10\x06\x12\x07\n\x03TAK\x10\x07\x12\x11\n\rCLIENT_HIDDEN\x10\x08\x12\x12\n\x0eLOST_AND_FOUND\x10\t\x12\x0f\n\x0bTAK_TRACKER\x10\n\"Q\n\x0fRebroadcastMode\x12\x07\n\x03\x41LL\x10\x00\x12\x15\n\x11\x41LL_SKIP_DECODING\x10\x01\x12\x0e\n\nLOCAL_ONLY\x10\x02\x12\x0e\n\nKNOWN_ONLY\x10\x03\x1a\x91\x05\n\x0ePositionConfig\x12\x1f\n\x17position_broadcast_secs\x18\x01 \x01(\r\x12(\n position_broadcast_smart_enabled\x18\x02 \x01(\x08\x12\x16\n\x0e\x66ixed_position\x18\x03 \x01(\x08\x12\x17\n\x0bgps_enabled\x18\x04 \x01(\x08\x42\x02\x18\x01\x12\x1b\n\x13gps_update_interval\x18\x05 \x01(\r\x12\x1c\n\x10gps_attempt_time\x18\x06 \x01(\rB\x02\x18\x01\x12\x16\n\x0eposition_flags\x18\x07 \x01(\r\x12\x0f\n\x07rx_gpio\x18\x08 \x01(\r\x12\x0f\n\x07tx_gpio\x18\t \x01(\r\x12(\n broadcast_smart_minimum_distance\x18\n \x01(\r\x12-\n%broadcast_smart_minimum_interval_secs\x18\x0b \x01(\r\x12\x13\n\x0bgps_en_gpio\x18\x0c \x01(\r\x12;\n\x08gps_mode\x18\r \x01(\x0e\x32).meshtastic.Config.PositionConfig.GpsMode\"\xab\x01\n\rPositionFlags\x12\t\n\x05UNSET\x10\x00\x12\x0c\n\x08\x41LTITUDE\x10\x01\x12\x10\n\x0c\x41LTITUDE_MSL\x10\x02\x12\x16\n\x12GEOIDAL_SEPARATION\x10\x04\x12\x07\n\x03\x44OP\x10\x08\x12\t\n\x05HVDOP\x10\x10\x12\r\n\tSATINVIEW\x10 \x12\n\n\x06SEQ_NO\x10@\x12\x0e\n\tTIMESTAMP\x10\x80\x01\x12\x0c\n\x07HEADING\x10\x80\x02\x12\n\n\x05SPEED\x10\x80\x04\"5\n\x07GpsMode\x12\x0c\n\x08\x44ISABLED\x10\x00\x12\x0b\n\x07\x45NABLED\x10\x01\x12\x0f\n\x0bNOT_PRESENT\x10\x02\x1a\x84\x02\n\x0bPowerConfig\x12\x17\n\x0fis_power_saving\x18\x01 \x01(\x08\x12&\n\x1eon_battery_shutdown_after_secs\x18\x02 \x01(\r\x12\x1f\n\x17\x61\x64\x63_multiplier_override\x18\x03 \x01(\x02\x12\x1b\n\x13wait_bluetooth_secs\x18\x04 \x01(\r\x12\x10\n\x08sds_secs\x18\x06 \x01(\r\x12\x0f\n\x07ls_secs\x18\x07 \x01(\r\x12\x15\n\rmin_wake_secs\x18\x08 \x01(\r\x12\"\n\x1a\x64\x65vice_battery_ina_address\x18\t \x01(\r\x12\x18\n\x10powermon_enables\x18 \x01(\x04\x1a\xfe\x02\n\rNetworkConfig\x12\x14\n\x0cwifi_enabled\x18\x01 \x01(\x08\x12\x11\n\twifi_ssid\x18\x03 \x01(\t\x12\x10\n\x08wifi_psk\x18\x04 \x01(\t\x12\x12\n\nntp_server\x18\x05 \x01(\t\x12\x13\n\x0b\x65th_enabled\x18\x06 \x01(\x08\x12\x42\n\x0c\x61\x64\x64ress_mode\x18\x07 \x01(\x0e\x32,.meshtastic.Config.NetworkConfig.AddressMode\x12@\n\x0bipv4_config\x18\x08 \x01(\x0b\x32+.meshtastic.Config.NetworkConfig.IpV4Config\x12\x16\n\x0ersyslog_server\x18\t \x01(\t\x1a\x46\n\nIpV4Config\x12\n\n\x02ip\x18\x01 \x01(\x07\x12\x0f\n\x07gateway\x18\x02 \x01(\x07\x12\x0e\n\x06subnet\x18\x03 \x01(\x07\x12\x0b\n\x03\x64ns\x18\x04 \x01(\x07\"#\n\x0b\x41\x64\x64ressMode\x12\x08\n\x04\x44HCP\x10\x00\x12\n\n\x06STATIC\x10\x01\x1a\xcd\x07\n\rDisplayConfig\x12\x16\n\x0escreen_on_secs\x18\x01 \x01(\r\x12H\n\ngps_format\x18\x02 \x01(\x0e\x32\x34.meshtastic.Config.DisplayConfig.GpsCoordinateFormat\x12!\n\x19\x61uto_screen_carousel_secs\x18\x03 \x01(\r\x12\x19\n\x11\x63ompass_north_top\x18\x04 \x01(\x08\x12\x13\n\x0b\x66lip_screen\x18\x05 \x01(\x08\x12<\n\x05units\x18\x06 \x01(\x0e\x32-.meshtastic.Config.DisplayConfig.DisplayUnits\x12\x37\n\x04oled\x18\x07 \x01(\x0e\x32).meshtastic.Config.DisplayConfig.OledType\x12\x41\n\x0b\x64isplaymode\x18\x08 \x01(\x0e\x32,.meshtastic.Config.DisplayConfig.DisplayMode\x12\x14\n\x0cheading_bold\x18\t \x01(\x08\x12\x1d\n\x15wake_on_tap_or_motion\x18\n \x01(\x08\x12P\n\x13\x63ompass_orientation\x18\x0b \x01(\x0e\x32\x33.meshtastic.Config.DisplayConfig.CompassOrientation\"M\n\x13GpsCoordinateFormat\x12\x07\n\x03\x44\x45\x43\x10\x00\x12\x07\n\x03\x44MS\x10\x01\x12\x07\n\x03UTM\x10\x02\x12\x08\n\x04MGRS\x10\x03\x12\x07\n\x03OLC\x10\x04\x12\x08\n\x04OSGR\x10\x05\"(\n\x0c\x44isplayUnits\x12\n\n\x06METRIC\x10\x00\x12\x0c\n\x08IMPERIAL\x10\x01\"M\n\x08OledType\x12\r\n\tOLED_AUTO\x10\x00\x12\x10\n\x0cOLED_SSD1306\x10\x01\x12\x0f\n\x0bOLED_SH1106\x10\x02\x12\x0f\n\x0bOLED_SH1107\x10\x03\"A\n\x0b\x44isplayMode\x12\x0b\n\x07\x44\x45\x46\x41ULT\x10\x00\x12\x0c\n\x08TWOCOLOR\x10\x01\x12\x0c\n\x08INVERTED\x10\x02\x12\t\n\x05\x43OLOR\x10\x03\"\xba\x01\n\x12\x43ompassOrientation\x12\r\n\tDEGREES_0\x10\x00\x12\x0e\n\nDEGREES_90\x10\x01\x12\x0f\n\x0b\x44\x45GREES_180\x10\x02\x12\x0f\n\x0b\x44\x45GREES_270\x10\x03\x12\x16\n\x12\x44\x45GREES_0_INVERTED\x10\x04\x12\x17\n\x13\x44\x45GREES_90_INVERTED\x10\x05\x12\x18\n\x14\x44\x45GREES_180_INVERTED\x10\x06\x12\x18\n\x14\x44\x45GREES_270_INVERTED\x10\x07\x1a\xde\x06\n\nLoRaConfig\x12\x12\n\nuse_preset\x18\x01 \x01(\x08\x12?\n\x0cmodem_preset\x18\x02 \x01(\x0e\x32).meshtastic.Config.LoRaConfig.ModemPreset\x12\x11\n\tbandwidth\x18\x03 \x01(\r\x12\x15\n\rspread_factor\x18\x04 \x01(\r\x12\x13\n\x0b\x63oding_rate\x18\x05 \x01(\r\x12\x18\n\x10\x66requency_offset\x18\x06 \x01(\x02\x12\x38\n\x06region\x18\x07 \x01(\x0e\x32(.meshtastic.Config.LoRaConfig.RegionCode\x12\x11\n\thop_limit\x18\x08 \x01(\r\x12\x12\n\ntx_enabled\x18\t \x01(\x08\x12\x10\n\x08tx_power\x18\n \x01(\x05\x12\x13\n\x0b\x63hannel_num\x18\x0b \x01(\r\x12\x1b\n\x13override_duty_cycle\x18\x0c \x01(\x08\x12\x1e\n\x16sx126x_rx_boosted_gain\x18\r \x01(\x08\x12\x1a\n\x12override_frequency\x18\x0e \x01(\x02\x12\x17\n\x0fpa_fan_disabled\x18\x0f \x01(\x08\x12\x17\n\x0fignore_incoming\x18g \x03(\r\x12\x13\n\x0bignore_mqtt\x18h \x01(\x08\"\xcd\x01\n\nRegionCode\x12\t\n\x05UNSET\x10\x00\x12\x06\n\x02US\x10\x01\x12\n\n\x06\x45U_433\x10\x02\x12\n\n\x06\x45U_868\x10\x03\x12\x06\n\x02\x43N\x10\x04\x12\x06\n\x02JP\x10\x05\x12\x07\n\x03\x41NZ\x10\x06\x12\x06\n\x02KR\x10\x07\x12\x06\n\x02TW\x10\x08\x12\x06\n\x02RU\x10\t\x12\x06\n\x02IN\x10\n\x12\n\n\x06NZ_865\x10\x0b\x12\x06\n\x02TH\x10\x0c\x12\x0b\n\x07LORA_24\x10\r\x12\n\n\x06UA_433\x10\x0e\x12\n\n\x06UA_868\x10\x0f\x12\n\n\x06MY_433\x10\x10\x12\n\n\x06MY_919\x10\x11\x12\n\n\x06SG_923\x10\x12\"\xa9\x01\n\x0bModemPreset\x12\r\n\tLONG_FAST\x10\x00\x12\r\n\tLONG_SLOW\x10\x01\x12\x16\n\x0eVERY_LONG_SLOW\x10\x02\x1a\x02\x08\x01\x12\x0f\n\x0bMEDIUM_SLOW\x10\x03\x12\x0f\n\x0bMEDIUM_FAST\x10\x04\x12\x0e\n\nSHORT_SLOW\x10\x05\x12\x0e\n\nSHORT_FAST\x10\x06\x12\x11\n\rLONG_MODERATE\x10\x07\x12\x0f\n\x0bSHORT_TURBO\x10\x08\x1a\xd1\x01\n\x0f\x42luetoothConfig\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12<\n\x04mode\x18\x02 \x01(\x0e\x32..meshtastic.Config.BluetoothConfig.PairingMode\x12\x11\n\tfixed_pin\x18\x03 \x01(\r\x12\"\n\x16\x64\x65vice_logging_enabled\x18\x04 \x01(\x08\x42\x02\x18\x01\"8\n\x0bPairingMode\x12\x0e\n\nRANDOM_PIN\x10\x00\x12\r\n\tFIXED_PIN\x10\x01\x12\n\n\x06NO_PIN\x10\x02\x1a\xd9\x01\n\x0eSecurityConfig\x12\x12\n\npublic_key\x18\x01 \x01(\x0c\x12\x13\n\x0bprivate_key\x18\x02 \x01(\x0c\x12\x11\n\tadmin_key\x18\x03 \x01(\x0c\x12\x12\n\nis_managed\x18\x04 \x01(\x08\x12\x16\n\x0eserial_enabled\x18\x05 \x01(\x08\x12\x1d\n\x15\x64\x65\x62ug_log_api_enabled\x18\x06 \x01(\x08\x12!\n\x19\x62luetooth_logging_enabled\x18\x07 \x01(\x08\x12\x1d\n\x15\x61\x64min_channel_enabled\x18\x08 \x01(\x08\x1a\x12\n\x10SessionkeyConfigB\x11\n\x0fpayload_variantBa\n\x13\x63om.geeksville.meshB\x0c\x43onfigProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') - -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.config_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\014ConfigProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' - _CONFIG_DEVICECONFIG_ROLE.values_by_name["ROUTER_CLIENT"]._options = None - _CONFIG_DEVICECONFIG_ROLE.values_by_name["ROUTER_CLIENT"]._serialized_options = b'\010\001' - _CONFIG_DEVICECONFIG.fields_by_name['serial_enabled']._options = None - _CONFIG_DEVICECONFIG.fields_by_name['serial_enabled']._serialized_options = b'\030\001' - _CONFIG_DEVICECONFIG.fields_by_name['debug_log_enabled']._options = None - _CONFIG_DEVICECONFIG.fields_by_name['debug_log_enabled']._serialized_options = b'\030\001' - _CONFIG_DEVICECONFIG.fields_by_name['is_managed']._options = None - _CONFIG_DEVICECONFIG.fields_by_name['is_managed']._serialized_options = b'\030\001' - _CONFIG_POSITIONCONFIG.fields_by_name['gps_enabled']._options = None - _CONFIG_POSITIONCONFIG.fields_by_name['gps_enabled']._serialized_options = b'\030\001' - _CONFIG_POSITIONCONFIG.fields_by_name['gps_attempt_time']._options = None - _CONFIG_POSITIONCONFIG.fields_by_name['gps_attempt_time']._serialized_options = b'\030\001' - _CONFIG_LORACONFIG_MODEMPRESET.values_by_name["VERY_LONG_SLOW"]._options = None - _CONFIG_LORACONFIG_MODEMPRESET.values_by_name["VERY_LONG_SLOW"]._serialized_options = b'\010\001' - _CONFIG_BLUETOOTHCONFIG.fields_by_name['device_logging_enabled']._options = None - _CONFIG_BLUETOOTHCONFIG.fields_by_name['device_logging_enabled']._serialized_options = b'\030\001' - _CONFIG._serialized_start=40 - _CONFIG._serialized_end=4823 - _CONFIG_DEVICECONFIG._serialized_start=530 - _CONFIG_DEVICECONFIG._serialized_end=1203 - _CONFIG_DEVICECONFIG_ROLE._serialized_start=946 - _CONFIG_DEVICECONFIG_ROLE._serialized_end=1120 - _CONFIG_DEVICECONFIG_REBROADCASTMODE._serialized_start=1122 - _CONFIG_DEVICECONFIG_REBROADCASTMODE._serialized_end=1203 - _CONFIG_POSITIONCONFIG._serialized_start=1206 - _CONFIG_POSITIONCONFIG._serialized_end=1863 - _CONFIG_POSITIONCONFIG_POSITIONFLAGS._serialized_start=1637 - _CONFIG_POSITIONCONFIG_POSITIONFLAGS._serialized_end=1808 - _CONFIG_POSITIONCONFIG_GPSMODE._serialized_start=1810 - _CONFIG_POSITIONCONFIG_GPSMODE._serialized_end=1863 - _CONFIG_POWERCONFIG._serialized_start=1866 - _CONFIG_POWERCONFIG._serialized_end=2126 - _CONFIG_NETWORKCONFIG._serialized_start=2129 - _CONFIG_NETWORKCONFIG._serialized_end=2511 - _CONFIG_NETWORKCONFIG_IPV4CONFIG._serialized_start=2404 - _CONFIG_NETWORKCONFIG_IPV4CONFIG._serialized_end=2474 - _CONFIG_NETWORKCONFIG_ADDRESSMODE._serialized_start=2476 - _CONFIG_NETWORKCONFIG_ADDRESSMODE._serialized_end=2511 - _CONFIG_DISPLAYCONFIG._serialized_start=2514 - _CONFIG_DISPLAYCONFIG._serialized_end=3487 - _CONFIG_DISPLAYCONFIG_GPSCOORDINATEFORMAT._serialized_start=3033 - _CONFIG_DISPLAYCONFIG_GPSCOORDINATEFORMAT._serialized_end=3110 - _CONFIG_DISPLAYCONFIG_DISPLAYUNITS._serialized_start=3112 - _CONFIG_DISPLAYCONFIG_DISPLAYUNITS._serialized_end=3152 - _CONFIG_DISPLAYCONFIG_OLEDTYPE._serialized_start=3154 - _CONFIG_DISPLAYCONFIG_OLEDTYPE._serialized_end=3231 - _CONFIG_DISPLAYCONFIG_DISPLAYMODE._serialized_start=3233 - _CONFIG_DISPLAYCONFIG_DISPLAYMODE._serialized_end=3298 - _CONFIG_DISPLAYCONFIG_COMPASSORIENTATION._serialized_start=3301 - _CONFIG_DISPLAYCONFIG_COMPASSORIENTATION._serialized_end=3487 - _CONFIG_LORACONFIG._serialized_start=3490 - _CONFIG_LORACONFIG._serialized_end=4352 - _CONFIG_LORACONFIG_REGIONCODE._serialized_start=3975 - _CONFIG_LORACONFIG_REGIONCODE._serialized_end=4180 - _CONFIG_LORACONFIG_MODEMPRESET._serialized_start=4183 - _CONFIG_LORACONFIG_MODEMPRESET._serialized_end=4352 - _CONFIG_BLUETOOTHCONFIG._serialized_start=4355 - _CONFIG_BLUETOOTHCONFIG._serialized_end=4564 - _CONFIG_BLUETOOTHCONFIG_PAIRINGMODE._serialized_start=4508 - _CONFIG_BLUETOOTHCONFIG_PAIRINGMODE._serialized_end=4564 - _CONFIG_SECURITYCONFIG._serialized_start=4567 - _CONFIG_SECURITYCONFIG._serialized_end=4784 - _CONFIG_SESSIONKEYCONFIG._serialized_start=4786 - _CONFIG_SESSIONKEYCONFIG._serialized_end=4804 -# @@protoc_insertion_point(module_scope) diff --git a/meshtastic/connection_status_pb2.py b/meshtastic/connection_status_pb2.py deleted file mode 100644 index 1d371892..00000000 --- a/meshtastic/connection_status_pb2.py +++ /dev/null @@ -1,36 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: meshtastic/connection_status.proto -"""Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder -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 -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - - - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\"meshtastic/connection_status.proto\x12\nmeshtastic\"\xb1\x02\n\x16\x44\x65viceConnectionStatus\x12\x33\n\x04wifi\x18\x01 \x01(\x0b\x32 .meshtastic.WifiConnectionStatusH\x00\x88\x01\x01\x12;\n\x08\x65thernet\x18\x02 \x01(\x0b\x32$.meshtastic.EthernetConnectionStatusH\x01\x88\x01\x01\x12=\n\tbluetooth\x18\x03 \x01(\x0b\x32%.meshtastic.BluetoothConnectionStatusH\x02\x88\x01\x01\x12\x37\n\x06serial\x18\x04 \x01(\x0b\x32\".meshtastic.SerialConnectionStatusH\x03\x88\x01\x01\x42\x07\n\x05_wifiB\x0b\n\t_ethernetB\x0c\n\n_bluetoothB\t\n\x07_serial\"g\n\x14WifiConnectionStatus\x12\x33\n\x06status\x18\x01 \x01(\x0b\x32#.meshtastic.NetworkConnectionStatus\x12\x0c\n\x04ssid\x18\x02 \x01(\t\x12\x0c\n\x04rssi\x18\x03 \x01(\x05\"O\n\x18\x45thernetConnectionStatus\x12\x33\n\x06status\x18\x01 \x01(\x0b\x32#.meshtastic.NetworkConnectionStatus\"{\n\x17NetworkConnectionStatus\x12\x12\n\nip_address\x18\x01 \x01(\x07\x12\x14\n\x0cis_connected\x18\x02 \x01(\x08\x12\x19\n\x11is_mqtt_connected\x18\x03 \x01(\x08\x12\x1b\n\x13is_syslog_connected\x18\x04 \x01(\x08\"L\n\x19\x42luetoothConnectionStatus\x12\x0b\n\x03pin\x18\x01 \x01(\r\x12\x0c\n\x04rssi\x18\x02 \x01(\x05\x12\x14\n\x0cis_connected\x18\x03 \x01(\x08\"<\n\x16SerialConnectionStatus\x12\x0c\n\x04\x62\x61ud\x18\x01 \x01(\r\x12\x14\n\x0cis_connected\x18\x02 \x01(\x08\x42\x65\n\x13\x63om.geeksville.meshB\x10\x43onnStatusProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') - -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.connection_status_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\020ConnStatusProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' - _DEVICECONNECTIONSTATUS._serialized_start=51 - _DEVICECONNECTIONSTATUS._serialized_end=356 - _WIFICONNECTIONSTATUS._serialized_start=358 - _WIFICONNECTIONSTATUS._serialized_end=461 - _ETHERNETCONNECTIONSTATUS._serialized_start=463 - _ETHERNETCONNECTIONSTATUS._serialized_end=542 - _NETWORKCONNECTIONSTATUS._serialized_start=544 - _NETWORKCONNECTIONSTATUS._serialized_end=667 - _BLUETOOTHCONNECTIONSTATUS._serialized_start=669 - _BLUETOOTHCONNECTIONSTATUS._serialized_end=745 - _SERIALCONNECTIONSTATUS._serialized_start=747 - _SERIALCONNECTIONSTATUS._serialized_end=807 -# @@protoc_insertion_point(module_scope) diff --git a/meshtastic/localonly_pb2.py b/meshtastic/localonly_pb2.py deleted file mode 100644 index 1f786a61..00000000 --- a/meshtastic/localonly_pb2.py +++ /dev/null @@ -1,30 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: meshtastic/localonly.proto -"""Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder -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 -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -from meshtastic import config_pb2 as meshtastic_dot_config__pb2 -from meshtastic import module_config_pb2 as meshtastic_dot_module__config__pb2 - - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1ameshtastic/localonly.proto\x12\nmeshtastic\x1a\x17meshtastic/config.proto\x1a\x1emeshtastic/module_config.proto\"\xb2\x03\n\x0bLocalConfig\x12/\n\x06\x64\x65vice\x18\x01 \x01(\x0b\x32\x1f.meshtastic.Config.DeviceConfig\x12\x33\n\x08position\x18\x02 \x01(\x0b\x32!.meshtastic.Config.PositionConfig\x12-\n\x05power\x18\x03 \x01(\x0b\x32\x1e.meshtastic.Config.PowerConfig\x12\x31\n\x07network\x18\x04 \x01(\x0b\x32 .meshtastic.Config.NetworkConfig\x12\x31\n\x07\x64isplay\x18\x05 \x01(\x0b\x32 .meshtastic.Config.DisplayConfig\x12+\n\x04lora\x18\x06 \x01(\x0b\x32\x1d.meshtastic.Config.LoRaConfig\x12\x35\n\tbluetooth\x18\x07 \x01(\x0b\x32\".meshtastic.Config.BluetoothConfig\x12\x0f\n\x07version\x18\x08 \x01(\r\x12\x33\n\x08security\x18\t \x01(\x0b\x32!.meshtastic.Config.SecurityConfig\"\xfb\x06\n\x11LocalModuleConfig\x12\x31\n\x04mqtt\x18\x01 \x01(\x0b\x32#.meshtastic.ModuleConfig.MQTTConfig\x12\x35\n\x06serial\x18\x02 \x01(\x0b\x32%.meshtastic.ModuleConfig.SerialConfig\x12R\n\x15\x65xternal_notification\x18\x03 \x01(\x0b\x32\x33.meshtastic.ModuleConfig.ExternalNotificationConfig\x12\x42\n\rstore_forward\x18\x04 \x01(\x0b\x32+.meshtastic.ModuleConfig.StoreForwardConfig\x12<\n\nrange_test\x18\x05 \x01(\x0b\x32(.meshtastic.ModuleConfig.RangeTestConfig\x12;\n\ttelemetry\x18\x06 \x01(\x0b\x32(.meshtastic.ModuleConfig.TelemetryConfig\x12\x44\n\x0e\x63\x61nned_message\x18\x07 \x01(\x0b\x32,.meshtastic.ModuleConfig.CannedMessageConfig\x12\x33\n\x05\x61udio\x18\t \x01(\x0b\x32$.meshtastic.ModuleConfig.AudioConfig\x12\x46\n\x0fremote_hardware\x18\n \x01(\x0b\x32-.meshtastic.ModuleConfig.RemoteHardwareConfig\x12\x42\n\rneighbor_info\x18\x0b \x01(\x0b\x32+.meshtastic.ModuleConfig.NeighborInfoConfig\x12H\n\x10\x61mbient_lighting\x18\x0c \x01(\x0b\x32..meshtastic.ModuleConfig.AmbientLightingConfig\x12H\n\x10\x64\x65tection_sensor\x18\r \x01(\x0b\x32..meshtastic.ModuleConfig.DetectionSensorConfig\x12=\n\npaxcounter\x18\x0e \x01(\x0b\x32).meshtastic.ModuleConfig.PaxcounterConfig\x12\x0f\n\x07version\x18\x08 \x01(\rBd\n\x13\x63om.geeksville.meshB\x0fLocalOnlyProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') - -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.localonly_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\017LocalOnlyProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' - _LOCALCONFIG._serialized_start=100 - _LOCALCONFIG._serialized_end=534 - _LOCALMODULECONFIG._serialized_start=537 - _LOCALMODULECONFIG._serialized_end=1428 -# @@protoc_insertion_point(module_scope) diff --git a/meshtastic/mesh_pb2.py b/meshtastic/mesh_pb2.py deleted file mode 100644 index f820c872..00000000 --- a/meshtastic/mesh_pb2.py +++ /dev/null @@ -1,102 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: meshtastic/mesh.proto -"""Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder -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 -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -from meshtastic import channel_pb2 as meshtastic_dot_channel__pb2 -from meshtastic import config_pb2 as meshtastic_dot_config__pb2 -from meshtastic import module_config_pb2 as meshtastic_dot_module__config__pb2 -from meshtastic import portnums_pb2 as meshtastic_dot_portnums__pb2 -from meshtastic import telemetry_pb2 as meshtastic_dot_telemetry__pb2 -from meshtastic import xmodem_pb2 as meshtastic_dot_xmodem__pb2 - - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15meshtastic/mesh.proto\x12\nmeshtastic\x1a\x18meshtastic/channel.proto\x1a\x17meshtastic/config.proto\x1a\x1emeshtastic/module_config.proto\x1a\x19meshtastic/portnums.proto\x1a\x1ameshtastic/telemetry.proto\x1a\x17meshtastic/xmodem.proto\"\x87\x07\n\x08Position\x12\x17\n\nlatitude_i\x18\x01 \x01(\x0fH\x00\x88\x01\x01\x12\x18\n\x0blongitude_i\x18\x02 \x01(\x0fH\x01\x88\x01\x01\x12\x15\n\x08\x61ltitude\x18\x03 \x01(\x05H\x02\x88\x01\x01\x12\x0c\n\x04time\x18\x04 \x01(\x07\x12\x37\n\x0flocation_source\x18\x05 \x01(\x0e\x32\x1e.meshtastic.Position.LocSource\x12\x37\n\x0f\x61ltitude_source\x18\x06 \x01(\x0e\x32\x1e.meshtastic.Position.AltSource\x12\x11\n\ttimestamp\x18\x07 \x01(\x07\x12\x1f\n\x17timestamp_millis_adjust\x18\x08 \x01(\x05\x12\x19\n\x0c\x61ltitude_hae\x18\t \x01(\x11H\x03\x88\x01\x01\x12(\n\x1b\x61ltitude_geoidal_separation\x18\n \x01(\x11H\x04\x88\x01\x01\x12\x0c\n\x04PDOP\x18\x0b \x01(\r\x12\x0c\n\x04HDOP\x18\x0c \x01(\r\x12\x0c\n\x04VDOP\x18\r \x01(\r\x12\x14\n\x0cgps_accuracy\x18\x0e \x01(\r\x12\x19\n\x0cground_speed\x18\x0f \x01(\rH\x05\x88\x01\x01\x12\x19\n\x0cground_track\x18\x10 \x01(\rH\x06\x88\x01\x01\x12\x13\n\x0b\x66ix_quality\x18\x11 \x01(\r\x12\x10\n\x08\x66ix_type\x18\x12 \x01(\r\x12\x14\n\x0csats_in_view\x18\x13 \x01(\r\x12\x11\n\tsensor_id\x18\x14 \x01(\r\x12\x13\n\x0bnext_update\x18\x15 \x01(\r\x12\x12\n\nseq_number\x18\x16 \x01(\r\x12\x16\n\x0eprecision_bits\x18\x17 \x01(\r\"N\n\tLocSource\x12\r\n\tLOC_UNSET\x10\x00\x12\x0e\n\nLOC_MANUAL\x10\x01\x12\x10\n\x0cLOC_INTERNAL\x10\x02\x12\x10\n\x0cLOC_EXTERNAL\x10\x03\"b\n\tAltSource\x12\r\n\tALT_UNSET\x10\x00\x12\x0e\n\nALT_MANUAL\x10\x01\x12\x10\n\x0c\x41LT_INTERNAL\x10\x02\x12\x10\n\x0c\x41LT_EXTERNAL\x10\x03\x12\x12\n\x0e\x41LT_BAROMETRIC\x10\x04\x42\r\n\x0b_latitude_iB\x0e\n\x0c_longitude_iB\x0b\n\t_altitudeB\x0f\n\r_altitude_haeB\x1e\n\x1c_altitude_geoidal_separationB\x0f\n\r_ground_speedB\x0f\n\r_ground_track\"\xd8\x01\n\x04User\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\tlong_name\x18\x02 \x01(\t\x12\x12\n\nshort_name\x18\x03 \x01(\t\x12\x13\n\x07macaddr\x18\x04 \x01(\x0c\x42\x02\x18\x01\x12+\n\x08hw_model\x18\x05 \x01(\x0e\x32\x19.meshtastic.HardwareModel\x12\x13\n\x0bis_licensed\x18\x06 \x01(\x08\x12\x32\n\x04role\x18\x07 \x01(\x0e\x32$.meshtastic.Config.DeviceConfig.Role\x12\x12\n\npublic_key\x18\x08 \x01(\x0c\"Z\n\x0eRouteDiscovery\x12\r\n\x05route\x18\x01 \x03(\x07\x12\x13\n\x0bsnr_towards\x18\x02 \x03(\x05\x12\x12\n\nroute_back\x18\x03 \x03(\x07\x12\x10\n\x08snr_back\x18\x04 \x03(\x05\"\xa4\x03\n\x07Routing\x12\x33\n\rroute_request\x18\x01 \x01(\x0b\x32\x1a.meshtastic.RouteDiscoveryH\x00\x12\x31\n\x0broute_reply\x18\x02 \x01(\x0b\x32\x1a.meshtastic.RouteDiscoveryH\x00\x12\x31\n\x0c\x65rror_reason\x18\x03 \x01(\x0e\x32\x19.meshtastic.Routing.ErrorH\x00\"\xf2\x01\n\x05\x45rror\x12\x08\n\x04NONE\x10\x00\x12\x0c\n\x08NO_ROUTE\x10\x01\x12\x0b\n\x07GOT_NAK\x10\x02\x12\x0b\n\x07TIMEOUT\x10\x03\x12\x10\n\x0cNO_INTERFACE\x10\x04\x12\x12\n\x0eMAX_RETRANSMIT\x10\x05\x12\x0e\n\nNO_CHANNEL\x10\x06\x12\r\n\tTOO_LARGE\x10\x07\x12\x0f\n\x0bNO_RESPONSE\x10\x08\x12\x14\n\x10\x44UTY_CYCLE_LIMIT\x10\t\x12\x0f\n\x0b\x42\x41\x44_REQUEST\x10 \x12\x12\n\x0eNOT_AUTHORIZED\x10!\x12\x0e\n\nPKI_FAILED\x10\"\x12\x16\n\x12PKI_UNKNOWN_PUBKEY\x10#B\t\n\x07variant\"\xa7\x01\n\x04\x44\x61ta\x12$\n\x07portnum\x18\x01 \x01(\x0e\x32\x13.meshtastic.PortNum\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\x12\x15\n\rwant_response\x18\x03 \x01(\x08\x12\x0c\n\x04\x64\x65st\x18\x04 \x01(\x07\x12\x0e\n\x06source\x18\x05 \x01(\x07\x12\x12\n\nrequest_id\x18\x06 \x01(\x07\x12\x10\n\x08reply_id\x18\x07 \x01(\x07\x12\r\n\x05\x65moji\x18\x08 \x01(\x07\"\xbc\x01\n\x08Waypoint\x12\n\n\x02id\x18\x01 \x01(\r\x12\x17\n\nlatitude_i\x18\x02 \x01(\x0fH\x00\x88\x01\x01\x12\x18\n\x0blongitude_i\x18\x03 \x01(\x0fH\x01\x88\x01\x01\x12\x0e\n\x06\x65xpire\x18\x04 \x01(\r\x12\x11\n\tlocked_to\x18\x05 \x01(\r\x12\x0c\n\x04name\x18\x06 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x07 \x01(\t\x12\x0c\n\x04icon\x18\x08 \x01(\x07\x42\r\n\x0b_latitude_iB\x0e\n\x0c_longitude_i\"l\n\x16MqttClientProxyMessage\x12\r\n\x05topic\x18\x01 \x01(\t\x12\x0e\n\x04\x64\x61ta\x18\x02 \x01(\x0cH\x00\x12\x0e\n\x04text\x18\x03 \x01(\tH\x00\x12\x10\n\x08retained\x18\x04 \x01(\x08\x42\x11\n\x0fpayload_variant\"\xc0\x04\n\nMeshPacket\x12\x0c\n\x04\x66rom\x18\x01 \x01(\x07\x12\n\n\x02to\x18\x02 \x01(\x07\x12\x0f\n\x07\x63hannel\x18\x03 \x01(\r\x12#\n\x07\x64\x65\x63oded\x18\x04 \x01(\x0b\x32\x10.meshtastic.DataH\x00\x12\x13\n\tencrypted\x18\x05 \x01(\x0cH\x00\x12\n\n\x02id\x18\x06 \x01(\x07\x12\x0f\n\x07rx_time\x18\x07 \x01(\x07\x12\x0e\n\x06rx_snr\x18\x08 \x01(\x02\x12\x11\n\thop_limit\x18\t \x01(\r\x12\x10\n\x08want_ack\x18\n \x01(\x08\x12\x31\n\x08priority\x18\x0b \x01(\x0e\x32\x1f.meshtastic.MeshPacket.Priority\x12\x0f\n\x07rx_rssi\x18\x0c \x01(\x05\x12\x33\n\x07\x64\x65layed\x18\r \x01(\x0e\x32\x1e.meshtastic.MeshPacket.DelayedB\x02\x18\x01\x12\x10\n\x08via_mqtt\x18\x0e \x01(\x08\x12\x11\n\thop_start\x18\x0f \x01(\r\x12\x12\n\npublic_key\x18\x10 \x01(\x0c\x12\x15\n\rpki_encrypted\x18\x11 \x01(\x08\"[\n\x08Priority\x12\t\n\x05UNSET\x10\x00\x12\x07\n\x03MIN\x10\x01\x12\x0e\n\nBACKGROUND\x10\n\x12\x0b\n\x07\x44\x45\x46\x41ULT\x10@\x12\x0c\n\x08RELIABLE\x10\x46\x12\x07\n\x03\x41\x43K\x10x\x12\x07\n\x03MAX\x10\x7f\"B\n\x07\x44\x65layed\x12\x0c\n\x08NO_DELAY\x10\x00\x12\x15\n\x11\x44\x45LAYED_BROADCAST\x10\x01\x12\x12\n\x0e\x44\x45LAYED_DIRECT\x10\x02\x42\x11\n\x0fpayload_variant\"\xfe\x01\n\x08NodeInfo\x12\x0b\n\x03num\x18\x01 \x01(\r\x12\x1e\n\x04user\x18\x02 \x01(\x0b\x32\x10.meshtastic.User\x12&\n\x08position\x18\x03 \x01(\x0b\x32\x14.meshtastic.Position\x12\x0b\n\x03snr\x18\x04 \x01(\x02\x12\x12\n\nlast_heard\x18\x05 \x01(\x07\x12\x31\n\x0e\x64\x65vice_metrics\x18\x06 \x01(\x0b\x32\x19.meshtastic.DeviceMetrics\x12\x0f\n\x07\x63hannel\x18\x07 \x01(\r\x12\x10\n\x08via_mqtt\x18\x08 \x01(\x08\x12\x11\n\thops_away\x18\t \x01(\r\x12\x13\n\x0bis_favorite\x18\n \x01(\x08\"P\n\nMyNodeInfo\x12\x13\n\x0bmy_node_num\x18\x01 \x01(\r\x12\x14\n\x0creboot_count\x18\x08 \x01(\r\x12\x17\n\x0fmin_app_version\x18\x0b \x01(\r\"\xc0\x01\n\tLogRecord\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\x0c\n\x04time\x18\x02 \x01(\x07\x12\x0e\n\x06source\x18\x03 \x01(\t\x12*\n\x05level\x18\x04 \x01(\x0e\x32\x1b.meshtastic.LogRecord.Level\"X\n\x05Level\x12\t\n\x05UNSET\x10\x00\x12\x0c\n\x08\x43RITICAL\x10\x32\x12\t\n\x05\x45RROR\x10(\x12\x0b\n\x07WARNING\x10\x1e\x12\x08\n\x04INFO\x10\x14\x12\t\n\x05\x44\x45\x42UG\x10\n\x12\t\n\x05TRACE\x10\x05\"P\n\x0bQueueStatus\x12\x0b\n\x03res\x18\x01 \x01(\x05\x12\x0c\n\x04\x66ree\x18\x02 \x01(\r\x12\x0e\n\x06maxlen\x18\x03 \x01(\r\x12\x16\n\x0emesh_packet_id\x18\x04 \x01(\r\"\xc3\x05\n\tFromRadio\x12\n\n\x02id\x18\x01 \x01(\r\x12(\n\x06packet\x18\x02 \x01(\x0b\x32\x16.meshtastic.MeshPacketH\x00\x12)\n\x07my_info\x18\x03 \x01(\x0b\x32\x16.meshtastic.MyNodeInfoH\x00\x12)\n\tnode_info\x18\x04 \x01(\x0b\x32\x14.meshtastic.NodeInfoH\x00\x12$\n\x06\x63onfig\x18\x05 \x01(\x0b\x32\x12.meshtastic.ConfigH\x00\x12+\n\nlog_record\x18\x06 \x01(\x0b\x32\x15.meshtastic.LogRecordH\x00\x12\x1c\n\x12\x63onfig_complete_id\x18\x07 \x01(\rH\x00\x12\x12\n\x08rebooted\x18\x08 \x01(\x08H\x00\x12\x30\n\x0cmoduleConfig\x18\t \x01(\x0b\x32\x18.meshtastic.ModuleConfigH\x00\x12&\n\x07\x63hannel\x18\n \x01(\x0b\x32\x13.meshtastic.ChannelH\x00\x12.\n\x0bqueueStatus\x18\x0b \x01(\x0b\x32\x17.meshtastic.QueueStatusH\x00\x12*\n\x0cxmodemPacket\x18\x0c \x01(\x0b\x32\x12.meshtastic.XModemH\x00\x12.\n\x08metadata\x18\r \x01(\x0b\x32\x1a.meshtastic.DeviceMetadataH\x00\x12\x44\n\x16mqttClientProxyMessage\x18\x0e \x01(\x0b\x32\".meshtastic.MqttClientProxyMessageH\x00\x12(\n\x08\x66ileInfo\x18\x0f \x01(\x0b\x32\x14.meshtastic.FileInfoH\x00\x12<\n\x12\x63lientNotification\x18\x10 \x01(\x0b\x32\x1e.meshtastic.ClientNotificationH\x00\x42\x11\n\x0fpayload_variant\"\x83\x01\n\x12\x43lientNotification\x12\x15\n\x08reply_id\x18\x01 \x01(\rH\x00\x88\x01\x01\x12\x0c\n\x04time\x18\x02 \x01(\x07\x12*\n\x05level\x18\x03 \x01(\x0e\x32\x1b.meshtastic.LogRecord.Level\x12\x0f\n\x07message\x18\x04 \x01(\tB\x0b\n\t_reply_id\"1\n\x08\x46ileInfo\x12\x11\n\tfile_name\x18\x01 \x01(\t\x12\x12\n\nsize_bytes\x18\x02 \x01(\r\"\x94\x02\n\x07ToRadio\x12(\n\x06packet\x18\x01 \x01(\x0b\x32\x16.meshtastic.MeshPacketH\x00\x12\x18\n\x0ewant_config_id\x18\x03 \x01(\rH\x00\x12\x14\n\ndisconnect\x18\x04 \x01(\x08H\x00\x12*\n\x0cxmodemPacket\x18\x05 \x01(\x0b\x32\x12.meshtastic.XModemH\x00\x12\x44\n\x16mqttClientProxyMessage\x18\x06 \x01(\x0b\x32\".meshtastic.MqttClientProxyMessageH\x00\x12*\n\theartbeat\x18\x07 \x01(\x0b\x32\x15.meshtastic.HeartbeatH\x00\x42\x11\n\x0fpayload_variant\"@\n\nCompressed\x12$\n\x07portnum\x18\x01 \x01(\x0e\x32\x13.meshtastic.PortNum\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\"\x87\x01\n\x0cNeighborInfo\x12\x0f\n\x07node_id\x18\x01 \x01(\r\x12\x17\n\x0flast_sent_by_id\x18\x02 \x01(\r\x12$\n\x1cnode_broadcast_interval_secs\x18\x03 \x01(\r\x12\'\n\tneighbors\x18\x04 \x03(\x0b\x32\x14.meshtastic.Neighbor\"d\n\x08Neighbor\x12\x0f\n\x07node_id\x18\x01 \x01(\r\x12\x0b\n\x03snr\x18\x02 \x01(\x02\x12\x14\n\x0clast_rx_time\x18\x03 \x01(\x07\x12$\n\x1cnode_broadcast_interval_secs\x18\x04 \x01(\r\"\xad\x02\n\x0e\x44\x65viceMetadata\x12\x18\n\x10\x66irmware_version\x18\x01 \x01(\t\x12\x1c\n\x14\x64\x65vice_state_version\x18\x02 \x01(\r\x12\x13\n\x0b\x63\x61nShutdown\x18\x03 \x01(\x08\x12\x0f\n\x07hasWifi\x18\x04 \x01(\x08\x12\x14\n\x0chasBluetooth\x18\x05 \x01(\x08\x12\x13\n\x0bhasEthernet\x18\x06 \x01(\x08\x12\x32\n\x04role\x18\x07 \x01(\x0e\x32$.meshtastic.Config.DeviceConfig.Role\x12\x16\n\x0eposition_flags\x18\x08 \x01(\r\x12+\n\x08hw_model\x18\t \x01(\x0e\x32\x19.meshtastic.HardwareModel\x12\x19\n\x11hasRemoteHardware\x18\n \x01(\x08\"\x0b\n\tHeartbeat\"U\n\x15NodeRemoteHardwarePin\x12\x10\n\x08node_num\x18\x01 \x01(\r\x12*\n\x03pin\x18\x02 \x01(\x0b\x32\x1d.meshtastic.RemoteHardwarePin\"e\n\x0e\x43hunkedPayload\x12\x12\n\npayload_id\x18\x01 \x01(\r\x12\x13\n\x0b\x63hunk_count\x18\x02 \x01(\r\x12\x13\n\x0b\x63hunk_index\x18\x03 \x01(\r\x12\x15\n\rpayload_chunk\x18\x04 \x01(\x0c\"\x1f\n\rresend_chunks\x12\x0e\n\x06\x63hunks\x18\x01 \x03(\r\"\xaa\x01\n\x16\x43hunkedPayloadResponse\x12\x12\n\npayload_id\x18\x01 \x01(\r\x12\x1a\n\x10request_transfer\x18\x02 \x01(\x08H\x00\x12\x19\n\x0f\x61\x63\x63\x65pt_transfer\x18\x03 \x01(\x08H\x00\x12\x32\n\rresend_chunks\x18\x04 \x01(\x0b\x32\x19.meshtastic.resend_chunksH\x00\x42\x11\n\x0fpayload_variant*\x9a\x0b\n\rHardwareModel\x12\t\n\x05UNSET\x10\x00\x12\x0c\n\x08TLORA_V2\x10\x01\x12\x0c\n\x08TLORA_V1\x10\x02\x12\x12\n\x0eTLORA_V2_1_1P6\x10\x03\x12\t\n\x05TBEAM\x10\x04\x12\x0f\n\x0bHELTEC_V2_0\x10\x05\x12\x0e\n\nTBEAM_V0P7\x10\x06\x12\n\n\x06T_ECHO\x10\x07\x12\x10\n\x0cTLORA_V1_1P3\x10\x08\x12\x0b\n\x07RAK4631\x10\t\x12\x0f\n\x0bHELTEC_V2_1\x10\n\x12\r\n\tHELTEC_V1\x10\x0b\x12\x18\n\x14LILYGO_TBEAM_S3_CORE\x10\x0c\x12\x0c\n\x08RAK11200\x10\r\x12\x0b\n\x07NANO_G1\x10\x0e\x12\x12\n\x0eTLORA_V2_1_1P8\x10\x0f\x12\x0f\n\x0bTLORA_T3_S3\x10\x10\x12\x14\n\x10NANO_G1_EXPLORER\x10\x11\x12\x11\n\rNANO_G2_ULTRA\x10\x12\x12\r\n\tLORA_TYPE\x10\x13\x12\x0b\n\x07WIPHONE\x10\x14\x12\x0e\n\nWIO_WM1110\x10\x15\x12\x0b\n\x07RAK2560\x10\x16\x12\x13\n\x0fHELTEC_HRU_3601\x10\x17\x12\x0e\n\nSTATION_G1\x10\x19\x12\x0c\n\x08RAK11310\x10\x1a\x12\x14\n\x10SENSELORA_RP2040\x10\x1b\x12\x10\n\x0cSENSELORA_S3\x10\x1c\x12\r\n\tCANARYONE\x10\x1d\x12\x0f\n\x0bRP2040_LORA\x10\x1e\x12\x0e\n\nSTATION_G2\x10\x1f\x12\x11\n\rLORA_RELAY_V1\x10 \x12\x0e\n\nNRF52840DK\x10!\x12\x07\n\x03PPR\x10\"\x12\x0f\n\x0bGENIEBLOCKS\x10#\x12\x11\n\rNRF52_UNKNOWN\x10$\x12\r\n\tPORTDUINO\x10%\x12\x0f\n\x0b\x41NDROID_SIM\x10&\x12\n\n\x06\x44IY_V1\x10\'\x12\x15\n\x11NRF52840_PCA10059\x10(\x12\n\n\x06\x44R_DEV\x10)\x12\x0b\n\x07M5STACK\x10*\x12\r\n\tHELTEC_V3\x10+\x12\x11\n\rHELTEC_WSL_V3\x10,\x12\x13\n\x0f\x42\x45TAFPV_2400_TX\x10-\x12\x17\n\x13\x42\x45TAFPV_900_NANO_TX\x10.\x12\x0c\n\x08RPI_PICO\x10/\x12\x1b\n\x17HELTEC_WIRELESS_TRACKER\x10\x30\x12\x19\n\x15HELTEC_WIRELESS_PAPER\x10\x31\x12\n\n\x06T_DECK\x10\x32\x12\x0e\n\nT_WATCH_S3\x10\x33\x12\x11\n\rPICOMPUTER_S3\x10\x34\x12\x0f\n\x0bHELTEC_HT62\x10\x35\x12\x12\n\x0e\x45\x42YTE_ESP32_S3\x10\x36\x12\x11\n\rESP32_S3_PICO\x10\x37\x12\r\n\tCHATTER_2\x10\x38\x12\x1e\n\x1aHELTEC_WIRELESS_PAPER_V1_0\x10\x39\x12 \n\x1cHELTEC_WIRELESS_TRACKER_V1_0\x10:\x12\x0b\n\x07UNPHONE\x10;\x12\x0c\n\x08TD_LORAC\x10<\x12\x13\n\x0f\x43\x44\x45\x42YTE_EORA_S3\x10=\x12\x0f\n\x0bTWC_MESH_V4\x10>\x12\x16\n\x12NRF52_PROMICRO_DIY\x10?\x12\x1f\n\x1bRADIOMASTER_900_BANDIT_NANO\x10@\x12\x1c\n\x18HELTEC_CAPSULE_SENSOR_V3\x10\x41\x12\x1d\n\x19HELTEC_VISION_MASTER_T190\x10\x42\x12\x1d\n\x19HELTEC_VISION_MASTER_E213\x10\x43\x12\x1d\n\x19HELTEC_VISION_MASTER_E290\x10\x44\x12\x19\n\x15HELTEC_MESH_NODE_T114\x10\x45\x12\x16\n\x12SENSECAP_INDICATOR\x10\x46\x12\x13\n\x0fTRACKER_T1000_E\x10G\x12\x0b\n\x07RAK3172\x10H\x12\n\n\x06WIO_E5\x10I\x12\x1a\n\x16RADIOMASTER_900_BANDIT\x10J\x12\x13\n\x0fME25LS01_4Y10TD\x10K\x12\x0f\n\nPRIVATE_HW\x10\xff\x01*,\n\tConstants\x12\x08\n\x04ZERO\x10\x00\x12\x15\n\x10\x44\x41TA_PAYLOAD_LEN\x10\xed\x01*\xb4\x02\n\x11\x43riticalErrorCode\x12\x08\n\x04NONE\x10\x00\x12\x0f\n\x0bTX_WATCHDOG\x10\x01\x12\x14\n\x10SLEEP_ENTER_WAIT\x10\x02\x12\x0c\n\x08NO_RADIO\x10\x03\x12\x0f\n\x0bUNSPECIFIED\x10\x04\x12\x15\n\x11UBLOX_UNIT_FAILED\x10\x05\x12\r\n\tNO_AXP192\x10\x06\x12\x19\n\x15INVALID_RADIO_SETTING\x10\x07\x12\x13\n\x0fTRANSMIT_FAILED\x10\x08\x12\x0c\n\x08\x42ROWNOUT\x10\t\x12\x12\n\x0eSX1262_FAILURE\x10\n\x12\x11\n\rRADIO_SPI_BUG\x10\x0b\x12 \n\x1c\x46LASH_CORRUPTION_RECOVERABLE\x10\x0c\x12\"\n\x1e\x46LASH_CORRUPTION_UNRECOVERABLE\x10\rB_\n\x13\x63om.geeksville.meshB\nMeshProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') - -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.mesh_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\nMeshProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' - _USER.fields_by_name['macaddr']._options = None - _USER.fields_by_name['macaddr']._serialized_options = b'\030\001' - _MESHPACKET.fields_by_name['delayed']._options = None - _MESHPACKET.fields_by_name['delayed']._serialized_options = b'\030\001' - _HARDWAREMODEL._serialized_start=5700 - _HARDWAREMODEL._serialized_end=7134 - _CONSTANTS._serialized_start=7136 - _CONSTANTS._serialized_end=7180 - _CRITICALERRORCODE._serialized_start=7183 - _CRITICALERRORCODE._serialized_end=7491 - _POSITION._serialized_start=201 - _POSITION._serialized_end=1104 - _POSITION_LOCSOURCE._serialized_start=799 - _POSITION_LOCSOURCE._serialized_end=877 - _POSITION_ALTSOURCE._serialized_start=879 - _POSITION_ALTSOURCE._serialized_end=977 - _USER._serialized_start=1107 - _USER._serialized_end=1323 - _ROUTEDISCOVERY._serialized_start=1325 - _ROUTEDISCOVERY._serialized_end=1415 - _ROUTING._serialized_start=1418 - _ROUTING._serialized_end=1838 - _ROUTING_ERROR._serialized_start=1585 - _ROUTING_ERROR._serialized_end=1827 - _DATA._serialized_start=1841 - _DATA._serialized_end=2008 - _WAYPOINT._serialized_start=2011 - _WAYPOINT._serialized_end=2199 - _MQTTCLIENTPROXYMESSAGE._serialized_start=2201 - _MQTTCLIENTPROXYMESSAGE._serialized_end=2309 - _MESHPACKET._serialized_start=2312 - _MESHPACKET._serialized_end=2888 - _MESHPACKET_PRIORITY._serialized_start=2710 - _MESHPACKET_PRIORITY._serialized_end=2801 - _MESHPACKET_DELAYED._serialized_start=2803 - _MESHPACKET_DELAYED._serialized_end=2869 - _NODEINFO._serialized_start=2891 - _NODEINFO._serialized_end=3145 - _MYNODEINFO._serialized_start=3147 - _MYNODEINFO._serialized_end=3227 - _LOGRECORD._serialized_start=3230 - _LOGRECORD._serialized_end=3422 - _LOGRECORD_LEVEL._serialized_start=3334 - _LOGRECORD_LEVEL._serialized_end=3422 - _QUEUESTATUS._serialized_start=3424 - _QUEUESTATUS._serialized_end=3504 - _FROMRADIO._serialized_start=3507 - _FROMRADIO._serialized_end=4214 - _CLIENTNOTIFICATION._serialized_start=4217 - _CLIENTNOTIFICATION._serialized_end=4348 - _FILEINFO._serialized_start=4350 - _FILEINFO._serialized_end=4399 - _TORADIO._serialized_start=4402 - _TORADIO._serialized_end=4678 - _COMPRESSED._serialized_start=4680 - _COMPRESSED._serialized_end=4744 - _NEIGHBORINFO._serialized_start=4747 - _NEIGHBORINFO._serialized_end=4882 - _NEIGHBOR._serialized_start=4884 - _NEIGHBOR._serialized_end=4984 - _DEVICEMETADATA._serialized_start=4987 - _DEVICEMETADATA._serialized_end=5288 - _HEARTBEAT._serialized_start=5290 - _HEARTBEAT._serialized_end=5301 - _NODEREMOTEHARDWAREPIN._serialized_start=5303 - _NODEREMOTEHARDWAREPIN._serialized_end=5388 - _CHUNKEDPAYLOAD._serialized_start=5390 - _CHUNKEDPAYLOAD._serialized_end=5491 - _RESEND_CHUNKS._serialized_start=5493 - _RESEND_CHUNKS._serialized_end=5524 - _CHUNKEDPAYLOADRESPONSE._serialized_start=5527 - _CHUNKEDPAYLOADRESPONSE._serialized_end=5697 -# @@protoc_insertion_point(module_scope) diff --git a/meshtastic/module_config_pb2.py b/meshtastic/module_config_pb2.py deleted file mode 100644 index a99c0106..00000000 --- a/meshtastic/module_config_pb2.py +++ /dev/null @@ -1,66 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: meshtastic/module_config.proto -"""Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder -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 -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - - - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1emeshtastic/module_config.proto\x12\nmeshtastic\"\xf0\"\n\x0cModuleConfig\x12\x33\n\x04mqtt\x18\x01 \x01(\x0b\x32#.meshtastic.ModuleConfig.MQTTConfigH\x00\x12\x37\n\x06serial\x18\x02 \x01(\x0b\x32%.meshtastic.ModuleConfig.SerialConfigH\x00\x12T\n\x15\x65xternal_notification\x18\x03 \x01(\x0b\x32\x33.meshtastic.ModuleConfig.ExternalNotificationConfigH\x00\x12\x44\n\rstore_forward\x18\x04 \x01(\x0b\x32+.meshtastic.ModuleConfig.StoreForwardConfigH\x00\x12>\n\nrange_test\x18\x05 \x01(\x0b\x32(.meshtastic.ModuleConfig.RangeTestConfigH\x00\x12=\n\ttelemetry\x18\x06 \x01(\x0b\x32(.meshtastic.ModuleConfig.TelemetryConfigH\x00\x12\x46\n\x0e\x63\x61nned_message\x18\x07 \x01(\x0b\x32,.meshtastic.ModuleConfig.CannedMessageConfigH\x00\x12\x35\n\x05\x61udio\x18\x08 \x01(\x0b\x32$.meshtastic.ModuleConfig.AudioConfigH\x00\x12H\n\x0fremote_hardware\x18\t \x01(\x0b\x32-.meshtastic.ModuleConfig.RemoteHardwareConfigH\x00\x12\x44\n\rneighbor_info\x18\n \x01(\x0b\x32+.meshtastic.ModuleConfig.NeighborInfoConfigH\x00\x12J\n\x10\x61mbient_lighting\x18\x0b \x01(\x0b\x32..meshtastic.ModuleConfig.AmbientLightingConfigH\x00\x12J\n\x10\x64\x65tection_sensor\x18\x0c \x01(\x0b\x32..meshtastic.ModuleConfig.DetectionSensorConfigH\x00\x12?\n\npaxcounter\x18\r \x01(\x0b\x32).meshtastic.ModuleConfig.PaxcounterConfigH\x00\x1a\xb0\x02\n\nMQTTConfig\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x0f\n\x07\x61\x64\x64ress\x18\x02 \x01(\t\x12\x10\n\x08username\x18\x03 \x01(\t\x12\x10\n\x08password\x18\x04 \x01(\t\x12\x1a\n\x12\x65ncryption_enabled\x18\x05 \x01(\x08\x12\x14\n\x0cjson_enabled\x18\x06 \x01(\x08\x12\x13\n\x0btls_enabled\x18\x07 \x01(\x08\x12\x0c\n\x04root\x18\x08 \x01(\t\x12\x1f\n\x17proxy_to_client_enabled\x18\t \x01(\x08\x12\x1d\n\x15map_reporting_enabled\x18\n \x01(\x08\x12G\n\x13map_report_settings\x18\x0b \x01(\x0b\x32*.meshtastic.ModuleConfig.MapReportSettings\x1aN\n\x11MapReportSettings\x12\x1d\n\x15publish_interval_secs\x18\x01 \x01(\r\x12\x1a\n\x12position_precision\x18\x02 \x01(\r\x1a\x82\x01\n\x14RemoteHardwareConfig\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\"\n\x1a\x61llow_undefined_pin_access\x18\x02 \x01(\x08\x12\x35\n\x0e\x61vailable_pins\x18\x03 \x03(\x0b\x32\x1d.meshtastic.RemoteHardwarePin\x1a>\n\x12NeighborInfoConfig\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x17\n\x0fupdate_interval\x18\x02 \x01(\r\x1a\xd2\x01\n\x15\x44\x65tectionSensorConfig\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x1e\n\x16minimum_broadcast_secs\x18\x02 \x01(\r\x12\x1c\n\x14state_broadcast_secs\x18\x03 \x01(\r\x12\x11\n\tsend_bell\x18\x04 \x01(\x08\x12\x0c\n\x04name\x18\x05 \x01(\t\x12\x13\n\x0bmonitor_pin\x18\x06 \x01(\r\x12 \n\x18\x64\x65tection_triggered_high\x18\x07 \x01(\x08\x12\x12\n\nuse_pullup\x18\x08 \x01(\x08\x1a\xe4\x02\n\x0b\x41udioConfig\x12\x16\n\x0e\x63odec2_enabled\x18\x01 \x01(\x08\x12\x0f\n\x07ptt_pin\x18\x02 \x01(\r\x12@\n\x07\x62itrate\x18\x03 \x01(\x0e\x32/.meshtastic.ModuleConfig.AudioConfig.Audio_Baud\x12\x0e\n\x06i2s_ws\x18\x04 \x01(\r\x12\x0e\n\x06i2s_sd\x18\x05 \x01(\r\x12\x0f\n\x07i2s_din\x18\x06 \x01(\r\x12\x0f\n\x07i2s_sck\x18\x07 \x01(\r\"\xa7\x01\n\nAudio_Baud\x12\x12\n\x0e\x43ODEC2_DEFAULT\x10\x00\x12\x0f\n\x0b\x43ODEC2_3200\x10\x01\x12\x0f\n\x0b\x43ODEC2_2400\x10\x02\x12\x0f\n\x0b\x43ODEC2_1600\x10\x03\x12\x0f\n\x0b\x43ODEC2_1400\x10\x04\x12\x0f\n\x0b\x43ODEC2_1300\x10\x05\x12\x0f\n\x0b\x43ODEC2_1200\x10\x06\x12\x0e\n\nCODEC2_700\x10\x07\x12\x0f\n\x0b\x43ODEC2_700B\x10\x08\x1av\n\x10PaxcounterConfig\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\"\n\x1apaxcounter_update_interval\x18\x02 \x01(\r\x12\x16\n\x0ewifi_threshold\x18\x03 \x01(\x05\x12\x15\n\rble_threshold\x18\x04 \x01(\x05\x1a\xee\x04\n\x0cSerialConfig\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x0c\n\x04\x65\x63ho\x18\x02 \x01(\x08\x12\x0b\n\x03rxd\x18\x03 \x01(\r\x12\x0b\n\x03txd\x18\x04 \x01(\r\x12?\n\x04\x62\x61ud\x18\x05 \x01(\x0e\x32\x31.meshtastic.ModuleConfig.SerialConfig.Serial_Baud\x12\x0f\n\x07timeout\x18\x06 \x01(\r\x12?\n\x04mode\x18\x07 \x01(\x0e\x32\x31.meshtastic.ModuleConfig.SerialConfig.Serial_Mode\x12$\n\x1coverride_console_serial_port\x18\x08 \x01(\x08\"\x8a\x02\n\x0bSerial_Baud\x12\x10\n\x0c\x42\x41UD_DEFAULT\x10\x00\x12\x0c\n\x08\x42\x41UD_110\x10\x01\x12\x0c\n\x08\x42\x41UD_300\x10\x02\x12\x0c\n\x08\x42\x41UD_600\x10\x03\x12\r\n\tBAUD_1200\x10\x04\x12\r\n\tBAUD_2400\x10\x05\x12\r\n\tBAUD_4800\x10\x06\x12\r\n\tBAUD_9600\x10\x07\x12\x0e\n\nBAUD_19200\x10\x08\x12\x0e\n\nBAUD_38400\x10\t\x12\x0e\n\nBAUD_57600\x10\n\x12\x0f\n\x0b\x42\x41UD_115200\x10\x0b\x12\x0f\n\x0b\x42\x41UD_230400\x10\x0c\x12\x0f\n\x0b\x42\x41UD_460800\x10\r\x12\x0f\n\x0b\x42\x41UD_576000\x10\x0e\x12\x0f\n\x0b\x42\x41UD_921600\x10\x0f\"_\n\x0bSerial_Mode\x12\x0b\n\x07\x44\x45\x46\x41ULT\x10\x00\x12\n\n\x06SIMPLE\x10\x01\x12\t\n\x05PROTO\x10\x02\x12\x0b\n\x07TEXTMSG\x10\x03\x12\x08\n\x04NMEA\x10\x04\x12\x0b\n\x07\x43\x41LTOPO\x10\x05\x12\x08\n\x04WS85\x10\x06\x1a\xe9\x02\n\x1a\x45xternalNotificationConfig\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x11\n\toutput_ms\x18\x02 \x01(\r\x12\x0e\n\x06output\x18\x03 \x01(\r\x12\x14\n\x0coutput_vibra\x18\x08 \x01(\r\x12\x15\n\routput_buzzer\x18\t \x01(\r\x12\x0e\n\x06\x61\x63tive\x18\x04 \x01(\x08\x12\x15\n\ralert_message\x18\x05 \x01(\x08\x12\x1b\n\x13\x61lert_message_vibra\x18\n \x01(\x08\x12\x1c\n\x14\x61lert_message_buzzer\x18\x0b \x01(\x08\x12\x12\n\nalert_bell\x18\x06 \x01(\x08\x12\x18\n\x10\x61lert_bell_vibra\x18\x0c \x01(\x08\x12\x19\n\x11\x61lert_bell_buzzer\x18\r \x01(\x08\x12\x0f\n\x07use_pwm\x18\x07 \x01(\x08\x12\x13\n\x0bnag_timeout\x18\x0e \x01(\r\x12\x19\n\x11use_i2s_as_buzzer\x18\x0f \x01(\x08\x1a\x97\x01\n\x12StoreForwardConfig\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x11\n\theartbeat\x18\x02 \x01(\x08\x12\x0f\n\x07records\x18\x03 \x01(\r\x12\x1a\n\x12history_return_max\x18\x04 \x01(\r\x12\x1d\n\x15history_return_window\x18\x05 \x01(\r\x12\x11\n\tis_server\x18\x06 \x01(\x08\x1a@\n\x0fRangeTestConfig\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x0e\n\x06sender\x18\x02 \x01(\r\x12\x0c\n\x04save\x18\x03 \x01(\x08\x1a\xe6\x02\n\x0fTelemetryConfig\x12\x1e\n\x16\x64\x65vice_update_interval\x18\x01 \x01(\r\x12#\n\x1b\x65nvironment_update_interval\x18\x02 \x01(\r\x12\'\n\x1f\x65nvironment_measurement_enabled\x18\x03 \x01(\x08\x12\"\n\x1a\x65nvironment_screen_enabled\x18\x04 \x01(\x08\x12&\n\x1e\x65nvironment_display_fahrenheit\x18\x05 \x01(\x08\x12\x1b\n\x13\x61ir_quality_enabled\x18\x06 \x01(\x08\x12\x1c\n\x14\x61ir_quality_interval\x18\x07 \x01(\r\x12!\n\x19power_measurement_enabled\x18\x08 \x01(\x08\x12\x1d\n\x15power_update_interval\x18\t \x01(\r\x12\x1c\n\x14power_screen_enabled\x18\n \x01(\x08\x1a\xd6\x04\n\x13\x43\x61nnedMessageConfig\x12\x17\n\x0frotary1_enabled\x18\x01 \x01(\x08\x12\x19\n\x11inputbroker_pin_a\x18\x02 \x01(\r\x12\x19\n\x11inputbroker_pin_b\x18\x03 \x01(\r\x12\x1d\n\x15inputbroker_pin_press\x18\x04 \x01(\r\x12Y\n\x14inputbroker_event_cw\x18\x05 \x01(\x0e\x32;.meshtastic.ModuleConfig.CannedMessageConfig.InputEventChar\x12Z\n\x15inputbroker_event_ccw\x18\x06 \x01(\x0e\x32;.meshtastic.ModuleConfig.CannedMessageConfig.InputEventChar\x12\\\n\x17inputbroker_event_press\x18\x07 \x01(\x0e\x32;.meshtastic.ModuleConfig.CannedMessageConfig.InputEventChar\x12\x17\n\x0fupdown1_enabled\x18\x08 \x01(\x08\x12\x0f\n\x07\x65nabled\x18\t \x01(\x08\x12\x1a\n\x12\x61llow_input_source\x18\n \x01(\t\x12\x11\n\tsend_bell\x18\x0b \x01(\x08\"c\n\x0eInputEventChar\x12\x08\n\x04NONE\x10\x00\x12\x06\n\x02UP\x10\x11\x12\x08\n\x04\x44OWN\x10\x12\x12\x08\n\x04LEFT\x10\x13\x12\t\n\x05RIGHT\x10\x14\x12\n\n\x06SELECT\x10\n\x12\x08\n\x04\x42\x41\x43K\x10\x1b\x12\n\n\x06\x43\x41NCEL\x10\x18\x1a\x65\n\x15\x41mbientLightingConfig\x12\x11\n\tled_state\x18\x01 \x01(\x08\x12\x0f\n\x07\x63urrent\x18\x02 \x01(\r\x12\x0b\n\x03red\x18\x03 \x01(\r\x12\r\n\x05green\x18\x04 \x01(\r\x12\x0c\n\x04\x62lue\x18\x05 \x01(\rB\x11\n\x0fpayload_variant\"d\n\x11RemoteHardwarePin\x12\x10\n\x08gpio_pin\x18\x01 \x01(\r\x12\x0c\n\x04name\x18\x02 \x01(\t\x12/\n\x04type\x18\x03 \x01(\x0e\x32!.meshtastic.RemoteHardwarePinType*I\n\x15RemoteHardwarePinType\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x10\n\x0c\x44IGITAL_READ\x10\x01\x12\x11\n\rDIGITAL_WRITE\x10\x02\x42g\n\x13\x63om.geeksville.meshB\x12ModuleConfigProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') - -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.module_config_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\022ModuleConfigProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' - _REMOTEHARDWAREPINTYPE._serialized_start=4615 - _REMOTEHARDWAREPINTYPE._serialized_end=4688 - _MODULECONFIG._serialized_start=47 - _MODULECONFIG._serialized_end=4511 - _MODULECONFIG_MQTTCONFIG._serialized_start=945 - _MODULECONFIG_MQTTCONFIG._serialized_end=1249 - _MODULECONFIG_MAPREPORTSETTINGS._serialized_start=1251 - _MODULECONFIG_MAPREPORTSETTINGS._serialized_end=1329 - _MODULECONFIG_REMOTEHARDWARECONFIG._serialized_start=1332 - _MODULECONFIG_REMOTEHARDWARECONFIG._serialized_end=1462 - _MODULECONFIG_NEIGHBORINFOCONFIG._serialized_start=1464 - _MODULECONFIG_NEIGHBORINFOCONFIG._serialized_end=1526 - _MODULECONFIG_DETECTIONSENSORCONFIG._serialized_start=1529 - _MODULECONFIG_DETECTIONSENSORCONFIG._serialized_end=1739 - _MODULECONFIG_AUDIOCONFIG._serialized_start=1742 - _MODULECONFIG_AUDIOCONFIG._serialized_end=2098 - _MODULECONFIG_AUDIOCONFIG_AUDIO_BAUD._serialized_start=1931 - _MODULECONFIG_AUDIOCONFIG_AUDIO_BAUD._serialized_end=2098 - _MODULECONFIG_PAXCOUNTERCONFIG._serialized_start=2100 - _MODULECONFIG_PAXCOUNTERCONFIG._serialized_end=2218 - _MODULECONFIG_SERIALCONFIG._serialized_start=2221 - _MODULECONFIG_SERIALCONFIG._serialized_end=2843 - _MODULECONFIG_SERIALCONFIG_SERIAL_BAUD._serialized_start=2480 - _MODULECONFIG_SERIALCONFIG_SERIAL_BAUD._serialized_end=2746 - _MODULECONFIG_SERIALCONFIG_SERIAL_MODE._serialized_start=2748 - _MODULECONFIG_SERIALCONFIG_SERIAL_MODE._serialized_end=2843 - _MODULECONFIG_EXTERNALNOTIFICATIONCONFIG._serialized_start=2846 - _MODULECONFIG_EXTERNALNOTIFICATIONCONFIG._serialized_end=3207 - _MODULECONFIG_STOREFORWARDCONFIG._serialized_start=3210 - _MODULECONFIG_STOREFORWARDCONFIG._serialized_end=3361 - _MODULECONFIG_RANGETESTCONFIG._serialized_start=3363 - _MODULECONFIG_RANGETESTCONFIG._serialized_end=3427 - _MODULECONFIG_TELEMETRYCONFIG._serialized_start=3430 - _MODULECONFIG_TELEMETRYCONFIG._serialized_end=3788 - _MODULECONFIG_CANNEDMESSAGECONFIG._serialized_start=3791 - _MODULECONFIG_CANNEDMESSAGECONFIG._serialized_end=4389 - _MODULECONFIG_CANNEDMESSAGECONFIG_INPUTEVENTCHAR._serialized_start=4290 - _MODULECONFIG_CANNEDMESSAGECONFIG_INPUTEVENTCHAR._serialized_end=4389 - _MODULECONFIG_AMBIENTLIGHTINGCONFIG._serialized_start=4391 - _MODULECONFIG_AMBIENTLIGHTINGCONFIG._serialized_end=4492 - _REMOTEHARDWAREPIN._serialized_start=4513 - _REMOTEHARDWAREPIN._serialized_end=4613 -# @@protoc_insertion_point(module_scope) diff --git a/meshtastic/mqtt_pb2.py b/meshtastic/mqtt_pb2.py deleted file mode 100644 index e4ba9e71..00000000 --- a/meshtastic/mqtt_pb2.py +++ /dev/null @@ -1,30 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: meshtastic/mqtt.proto -"""Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder -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 -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -from meshtastic import config_pb2 as meshtastic_dot_config__pb2 -from meshtastic import mesh_pb2 as meshtastic_dot_mesh__pb2 - - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15meshtastic/mqtt.proto\x12\nmeshtastic\x1a\x17meshtastic/config.proto\x1a\x15meshtastic/mesh.proto\"a\n\x0fServiceEnvelope\x12&\n\x06packet\x18\x01 \x01(\x0b\x32\x16.meshtastic.MeshPacket\x12\x12\n\nchannel_id\x18\x02 \x01(\t\x12\x12\n\ngateway_id\x18\x03 \x01(\t\"\xbc\x03\n\tMapReport\x12\x11\n\tlong_name\x18\x01 \x01(\t\x12\x12\n\nshort_name\x18\x02 \x01(\t\x12\x32\n\x04role\x18\x03 \x01(\x0e\x32$.meshtastic.Config.DeviceConfig.Role\x12+\n\x08hw_model\x18\x04 \x01(\x0e\x32\x19.meshtastic.HardwareModel\x12\x18\n\x10\x66irmware_version\x18\x05 \x01(\t\x12\x38\n\x06region\x18\x06 \x01(\x0e\x32(.meshtastic.Config.LoRaConfig.RegionCode\x12?\n\x0cmodem_preset\x18\x07 \x01(\x0e\x32).meshtastic.Config.LoRaConfig.ModemPreset\x12\x1b\n\x13has_default_channel\x18\x08 \x01(\x08\x12\x12\n\nlatitude_i\x18\t \x01(\x0f\x12\x13\n\x0blongitude_i\x18\n \x01(\x0f\x12\x10\n\x08\x61ltitude\x18\x0b \x01(\x05\x12\x1a\n\x12position_precision\x18\x0c \x01(\r\x12\x1e\n\x16num_online_local_nodes\x18\r \x01(\rB_\n\x13\x63om.geeksville.meshB\nMQTTProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') - -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.mqtt_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\nMQTTProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' - _SERVICEENVELOPE._serialized_start=85 - _SERVICEENVELOPE._serialized_end=182 - _MAPREPORT._serialized_start=185 - _MAPREPORT._serialized_end=629 -# @@protoc_insertion_point(module_scope) diff --git a/meshtastic/paxcount_pb2.py b/meshtastic/paxcount_pb2.py deleted file mode 100644 index 890258fb..00000000 --- a/meshtastic/paxcount_pb2.py +++ /dev/null @@ -1,26 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: meshtastic/paxcount.proto -"""Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder -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 -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - - - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x19meshtastic/paxcount.proto\x12\nmeshtastic\"5\n\x08Paxcount\x12\x0c\n\x04wifi\x18\x01 \x01(\r\x12\x0b\n\x03\x62le\x18\x02 \x01(\r\x12\x0e\n\x06uptime\x18\x03 \x01(\rBc\n\x13\x63om.geeksville.meshB\x0ePaxcountProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') - -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.paxcount_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\016PaxcountProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' - _PAXCOUNT._serialized_start=41 - _PAXCOUNT._serialized_end=94 -# @@protoc_insertion_point(module_scope) diff --git a/meshtastic/portnums_pb2.py b/meshtastic/portnums_pb2.py deleted file mode 100644 index 2c4a5901..00000000 --- a/meshtastic/portnums_pb2.py +++ /dev/null @@ -1,26 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: meshtastic/portnums.proto -"""Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder -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 -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - - - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x19meshtastic/portnums.proto\x12\nmeshtastic*\xa2\x04\n\x07PortNum\x12\x0f\n\x0bUNKNOWN_APP\x10\x00\x12\x14\n\x10TEXT_MESSAGE_APP\x10\x01\x12\x17\n\x13REMOTE_HARDWARE_APP\x10\x02\x12\x10\n\x0cPOSITION_APP\x10\x03\x12\x10\n\x0cNODEINFO_APP\x10\x04\x12\x0f\n\x0bROUTING_APP\x10\x05\x12\r\n\tADMIN_APP\x10\x06\x12\x1f\n\x1bTEXT_MESSAGE_COMPRESSED_APP\x10\x07\x12\x10\n\x0cWAYPOINT_APP\x10\x08\x12\r\n\tAUDIO_APP\x10\t\x12\x18\n\x14\x44\x45TECTION_SENSOR_APP\x10\n\x12\r\n\tREPLY_APP\x10 \x12\x11\n\rIP_TUNNEL_APP\x10!\x12\x12\n\x0ePAXCOUNTER_APP\x10\"\x12\x0e\n\nSERIAL_APP\x10@\x12\x15\n\x11STORE_FORWARD_APP\x10\x41\x12\x12\n\x0eRANGE_TEST_APP\x10\x42\x12\x11\n\rTELEMETRY_APP\x10\x43\x12\x0b\n\x07ZPS_APP\x10\x44\x12\x11\n\rSIMULATOR_APP\x10\x45\x12\x12\n\x0eTRACEROUTE_APP\x10\x46\x12\x14\n\x10NEIGHBORINFO_APP\x10G\x12\x0f\n\x0b\x41TAK_PLUGIN\x10H\x12\x12\n\x0eMAP_REPORT_APP\x10I\x12\x13\n\x0fPOWERSTRESS_APP\x10J\x12\x10\n\x0bPRIVATE_APP\x10\x80\x02\x12\x13\n\x0e\x41TAK_FORWARDER\x10\x81\x02\x12\x08\n\x03MAX\x10\xff\x03\x42]\n\x13\x63om.geeksville.meshB\x08PortnumsZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') - -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.portnums_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\010PortnumsZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' - _PORTNUM._serialized_start=42 - _PORTNUM._serialized_end=588 -# @@protoc_insertion_point(module_scope) diff --git a/meshtastic/protobuf/__init__.py b/meshtastic/protobuf/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/meshtastic/protobuf/admin_pb2.py b/meshtastic/protobuf/admin_pb2.py new file mode 100644 index 00000000..f5709da4 --- /dev/null +++ b/meshtastic/protobuf/admin_pb2.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: meshtastic/protobuf/admin.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from meshtastic.protobuf import channel_pb2 as meshtastic_dot_protobuf_dot_channel__pb2 +from meshtastic.protobuf import config_pb2 as meshtastic_dot_protobuf_dot_config__pb2 +from meshtastic.protobuf import connection_status_pb2 as meshtastic_dot_protobuf_dot_connection__status__pb2 +from meshtastic.protobuf import device_ui_pb2 as meshtastic_dot_protobuf_dot_device__ui__pb2 +from meshtastic.protobuf import mesh_pb2 as meshtastic_dot_protobuf_dot_mesh__pb2 +from meshtastic.protobuf import module_config_pb2 as meshtastic_dot_protobuf_dot_module__config__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1fmeshtastic/protobuf/admin.proto\x12\x13meshtastic.protobuf\x1a!meshtastic/protobuf/channel.proto\x1a meshtastic/protobuf/config.proto\x1a+meshtastic/protobuf/connection_status.proto\x1a#meshtastic/protobuf/device_ui.proto\x1a\x1emeshtastic/protobuf/mesh.proto\x1a\'meshtastic/protobuf/module_config.proto\"\xa5\x1a\n\x0c\x41\x64minMessage\x12\x17\n\x0fsession_passkey\x18\x65 \x01(\x0c\x12\x1d\n\x13get_channel_request\x18\x01 \x01(\rH\x00\x12<\n\x14get_channel_response\x18\x02 \x01(\x0b\x32\x1c.meshtastic.protobuf.ChannelH\x00\x12\x1b\n\x11get_owner_request\x18\x03 \x01(\x08H\x00\x12\x37\n\x12get_owner_response\x18\x04 \x01(\x0b\x32\x19.meshtastic.protobuf.UserH\x00\x12J\n\x12get_config_request\x18\x05 \x01(\x0e\x32,.meshtastic.protobuf.AdminMessage.ConfigTypeH\x00\x12:\n\x13get_config_response\x18\x06 \x01(\x0b\x32\x1b.meshtastic.protobuf.ConfigH\x00\x12W\n\x19get_module_config_request\x18\x07 \x01(\x0e\x32\x32.meshtastic.protobuf.AdminMessage.ModuleConfigTypeH\x00\x12G\n\x1aget_module_config_response\x18\x08 \x01(\x0b\x32!.meshtastic.protobuf.ModuleConfigH\x00\x12\x34\n*get_canned_message_module_messages_request\x18\n \x01(\x08H\x00\x12\x35\n+get_canned_message_module_messages_response\x18\x0b \x01(\tH\x00\x12%\n\x1bget_device_metadata_request\x18\x0c \x01(\x08H\x00\x12K\n\x1cget_device_metadata_response\x18\r \x01(\x0b\x32#.meshtastic.protobuf.DeviceMetadataH\x00\x12\x1e\n\x14get_ringtone_request\x18\x0e \x01(\x08H\x00\x12\x1f\n\x15get_ringtone_response\x18\x0f \x01(\tH\x00\x12.\n$get_device_connection_status_request\x18\x10 \x01(\x08H\x00\x12\\\n%get_device_connection_status_response\x18\x11 \x01(\x0b\x32+.meshtastic.protobuf.DeviceConnectionStatusH\x00\x12:\n\x0cset_ham_mode\x18\x12 \x01(\x0b\x32\".meshtastic.protobuf.HamParametersH\x00\x12/\n%get_node_remote_hardware_pins_request\x18\x13 \x01(\x08H\x00\x12\x65\n&get_node_remote_hardware_pins_response\x18\x14 \x01(\x0b\x32\x33.meshtastic.protobuf.NodeRemoteHardwarePinsResponseH\x00\x12 \n\x16\x65nter_dfu_mode_request\x18\x15 \x01(\x08H\x00\x12\x1d\n\x13\x64\x65lete_file_request\x18\x16 \x01(\tH\x00\x12\x13\n\tset_scale\x18\x17 \x01(\rH\x00\x12N\n\x12\x62\x61\x63kup_preferences\x18\x18 \x01(\x0e\x32\x30.meshtastic.protobuf.AdminMessage.BackupLocationH\x00\x12O\n\x13restore_preferences\x18\x19 \x01(\x0e\x32\x30.meshtastic.protobuf.AdminMessage.BackupLocationH\x00\x12U\n\x19remove_backup_preferences\x18\x1a \x01(\x0e\x32\x30.meshtastic.protobuf.AdminMessage.BackupLocationH\x00\x12H\n\x10send_input_event\x18\x1b \x01(\x0b\x32,.meshtastic.protobuf.AdminMessage.InputEventH\x00\x12.\n\tset_owner\x18 \x01(\x0b\x32\x19.meshtastic.protobuf.UserH\x00\x12\x33\n\x0bset_channel\x18! \x01(\x0b\x32\x1c.meshtastic.protobuf.ChannelH\x00\x12\x31\n\nset_config\x18\" \x01(\x0b\x32\x1b.meshtastic.protobuf.ConfigH\x00\x12>\n\x11set_module_config\x18# \x01(\x0b\x32!.meshtastic.protobuf.ModuleConfigH\x00\x12,\n\"set_canned_message_module_messages\x18$ \x01(\tH\x00\x12\x1e\n\x14set_ringtone_message\x18% \x01(\tH\x00\x12\x1b\n\x11remove_by_nodenum\x18& \x01(\rH\x00\x12\x1b\n\x11set_favorite_node\x18\' \x01(\rH\x00\x12\x1e\n\x14remove_favorite_node\x18( \x01(\rH\x00\x12;\n\x12set_fixed_position\x18) \x01(\x0b\x32\x1d.meshtastic.protobuf.PositionH\x00\x12\x1f\n\x15remove_fixed_position\x18* \x01(\x08H\x00\x12\x17\n\rset_time_only\x18+ \x01(\x07H\x00\x12\x1f\n\x15get_ui_config_request\x18, \x01(\x08H\x00\x12\x45\n\x16get_ui_config_response\x18- \x01(\x0b\x32#.meshtastic.protobuf.DeviceUIConfigH\x00\x12>\n\x0fstore_ui_config\x18. \x01(\x0b\x32#.meshtastic.protobuf.DeviceUIConfigH\x00\x12\x1a\n\x10set_ignored_node\x18/ \x01(\rH\x00\x12\x1d\n\x13remove_ignored_node\x18\x30 \x01(\rH\x00\x12\x1d\n\x13\x62\x65gin_edit_settings\x18@ \x01(\x08H\x00\x12\x1e\n\x14\x63ommit_edit_settings\x18\x41 \x01(\x08H\x00\x12\x39\n\x0b\x61\x64\x64_contact\x18\x42 \x01(\x0b\x32\".meshtastic.protobuf.SharedContactH\x00\x12\x45\n\x10key_verification\x18\x43 \x01(\x0b\x32).meshtastic.protobuf.KeyVerificationAdminH\x00\x12\x1e\n\x14\x66\x61\x63tory_reset_device\x18^ \x01(\x05H\x00\x12\x1c\n\x12reboot_ota_seconds\x18_ \x01(\x05H\x00\x12\x18\n\x0e\x65xit_simulator\x18` \x01(\x08H\x00\x12\x18\n\x0ereboot_seconds\x18\x61 \x01(\x05H\x00\x12\x1a\n\x10shutdown_seconds\x18\x62 \x01(\x05H\x00\x12\x1e\n\x14\x66\x61\x63tory_reset_config\x18\x63 \x01(\x05H\x00\x12\x16\n\x0cnodedb_reset\x18\x64 \x01(\x05H\x00\x1aS\n\nInputEvent\x12\x12\n\nevent_code\x18\x01 \x01(\r\x12\x0f\n\x07kb_char\x18\x02 \x01(\r\x12\x0f\n\x07touch_x\x18\x03 \x01(\r\x12\x0f\n\x07touch_y\x18\x04 \x01(\r\"\xd6\x01\n\nConfigType\x12\x11\n\rDEVICE_CONFIG\x10\x00\x12\x13\n\x0fPOSITION_CONFIG\x10\x01\x12\x10\n\x0cPOWER_CONFIG\x10\x02\x12\x12\n\x0eNETWORK_CONFIG\x10\x03\x12\x12\n\x0e\x44ISPLAY_CONFIG\x10\x04\x12\x0f\n\x0bLORA_CONFIG\x10\x05\x12\x14\n\x10\x42LUETOOTH_CONFIG\x10\x06\x12\x13\n\x0fSECURITY_CONFIG\x10\x07\x12\x15\n\x11SESSIONKEY_CONFIG\x10\x08\x12\x13\n\x0f\x44\x45VICEUI_CONFIG\x10\t\"\xbb\x02\n\x10ModuleConfigType\x12\x0f\n\x0bMQTT_CONFIG\x10\x00\x12\x11\n\rSERIAL_CONFIG\x10\x01\x12\x13\n\x0f\x45XTNOTIF_CONFIG\x10\x02\x12\x17\n\x13STOREFORWARD_CONFIG\x10\x03\x12\x14\n\x10RANGETEST_CONFIG\x10\x04\x12\x14\n\x10TELEMETRY_CONFIG\x10\x05\x12\x14\n\x10\x43\x41NNEDMSG_CONFIG\x10\x06\x12\x10\n\x0c\x41UDIO_CONFIG\x10\x07\x12\x19\n\x15REMOTEHARDWARE_CONFIG\x10\x08\x12\x17\n\x13NEIGHBORINFO_CONFIG\x10\t\x12\x1a\n\x16\x41MBIENTLIGHTING_CONFIG\x10\n\x12\x1a\n\x16\x44\x45TECTIONSENSOR_CONFIG\x10\x0b\x12\x15\n\x11PAXCOUNTER_CONFIG\x10\x0c\"#\n\x0e\x42\x61\x63kupLocation\x12\t\n\x05\x46LASH\x10\x00\x12\x06\n\x02SD\x10\x01\x42\x11\n\x0fpayload_variant\"[\n\rHamParameters\x12\x11\n\tcall_sign\x18\x01 \x01(\t\x12\x10\n\x08tx_power\x18\x02 \x01(\x05\x12\x11\n\tfrequency\x18\x03 \x01(\x02\x12\x12\n\nshort_name\x18\x04 \x01(\t\"o\n\x1eNodeRemoteHardwarePinsResponse\x12M\n\x19node_remote_hardware_pins\x18\x01 \x03(\x0b\x32*.meshtastic.protobuf.NodeRemoteHardwarePin\"|\n\rSharedContact\x12\x10\n\x08node_num\x18\x01 \x01(\r\x12\'\n\x04user\x18\x02 \x01(\x0b\x32\x19.meshtastic.protobuf.User\x12\x15\n\rshould_ignore\x18\x03 \x01(\x08\x12\x19\n\x11manually_verified\x18\x04 \x01(\x08\"\xa5\x02\n\x14KeyVerificationAdmin\x12K\n\x0cmessage_type\x18\x01 \x01(\x0e\x32\x35.meshtastic.protobuf.KeyVerificationAdmin.MessageType\x12\x16\n\x0eremote_nodenum\x18\x02 \x01(\r\x12\r\n\x05nonce\x18\x03 \x01(\x04\x12\x1c\n\x0fsecurity_number\x18\x04 \x01(\rH\x00\x88\x01\x01\"g\n\x0bMessageType\x12\x19\n\x15INITIATE_VERIFICATION\x10\x00\x12\x1b\n\x17PROVIDE_SECURITY_NUMBER\x10\x01\x12\r\n\tDO_VERIFY\x10\x02\x12\x11\n\rDO_NOT_VERIFY\x10\x03\x42\x12\n\x10_security_numberBa\n\x14org.meshtastic.protoB\x0b\x41\x64minProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.admin_pb2', _globals) +if _descriptor._USE_C_DESCRIPTORS == False: + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\013AdminProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' + _globals['_ADMINMESSAGE']._serialized_start=281 + _globals['_ADMINMESSAGE']._serialized_end=3646 + _globals['_ADMINMESSAGE_INPUTEVENT']._serialized_start=2972 + _globals['_ADMINMESSAGE_INPUTEVENT']._serialized_end=3055 + _globals['_ADMINMESSAGE_CONFIGTYPE']._serialized_start=3058 + _globals['_ADMINMESSAGE_CONFIGTYPE']._serialized_end=3272 + _globals['_ADMINMESSAGE_MODULECONFIGTYPE']._serialized_start=3275 + _globals['_ADMINMESSAGE_MODULECONFIGTYPE']._serialized_end=3590 + _globals['_ADMINMESSAGE_BACKUPLOCATION']._serialized_start=3592 + _globals['_ADMINMESSAGE_BACKUPLOCATION']._serialized_end=3627 + _globals['_HAMPARAMETERS']._serialized_start=3648 + _globals['_HAMPARAMETERS']._serialized_end=3739 + _globals['_NODEREMOTEHARDWAREPINSRESPONSE']._serialized_start=3741 + _globals['_NODEREMOTEHARDWAREPINSRESPONSE']._serialized_end=3852 + _globals['_SHAREDCONTACT']._serialized_start=3854 + _globals['_SHAREDCONTACT']._serialized_end=3978 + _globals['_KEYVERIFICATIONADMIN']._serialized_start=3981 + _globals['_KEYVERIFICATIONADMIN']._serialized_end=4274 + _globals['_KEYVERIFICATIONADMIN_MESSAGETYPE']._serialized_start=4151 + _globals['_KEYVERIFICATIONADMIN_MESSAGETYPE']._serialized_end=4254 +# @@protoc_insertion_point(module_scope) diff --git a/meshtastic/protobuf/admin_pb2.pyi b/meshtastic/protobuf/admin_pb2.pyi new file mode 100644 index 00000000..9d189d14 --- /dev/null +++ b/meshtastic/protobuf/admin_pb2.pyi @@ -0,0 +1,889 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" + +import builtins +import collections.abc +import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.internal.enum_type_wrapper +import google.protobuf.message +import meshtastic.protobuf.channel_pb2 +import meshtastic.protobuf.config_pb2 +import meshtastic.protobuf.connection_status_pb2 +import meshtastic.protobuf.device_ui_pb2 +import meshtastic.protobuf.mesh_pb2 +import meshtastic.protobuf.module_config_pb2 +import sys +import typing + +if sys.version_info >= (3, 10): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing.final +class AdminMessage(google.protobuf.message.Message): + """ + This message is handled by the Admin module and is responsible for all settings/channel read/write operations. + This message is used to do settings operations to both remote AND local nodes. + (Prior to 1.2 these operations were done via special ToRadio operations) + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + class _ConfigType: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + + class _ConfigTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[AdminMessage._ConfigType.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + DEVICE_CONFIG: AdminMessage._ConfigType.ValueType # 0 + """ + TODO: REPLACE + """ + POSITION_CONFIG: AdminMessage._ConfigType.ValueType # 1 + """ + TODO: REPLACE + """ + POWER_CONFIG: AdminMessage._ConfigType.ValueType # 2 + """ + TODO: REPLACE + """ + NETWORK_CONFIG: AdminMessage._ConfigType.ValueType # 3 + """ + TODO: REPLACE + """ + DISPLAY_CONFIG: AdminMessage._ConfigType.ValueType # 4 + """ + TODO: REPLACE + """ + LORA_CONFIG: AdminMessage._ConfigType.ValueType # 5 + """ + TODO: REPLACE + """ + BLUETOOTH_CONFIG: AdminMessage._ConfigType.ValueType # 6 + """ + TODO: REPLACE + """ + SECURITY_CONFIG: AdminMessage._ConfigType.ValueType # 7 + """ + TODO: REPLACE + """ + SESSIONKEY_CONFIG: AdminMessage._ConfigType.ValueType # 8 + """ + Session key config + """ + DEVICEUI_CONFIG: AdminMessage._ConfigType.ValueType # 9 + """ + device-ui config + """ + + class ConfigType(_ConfigType, metaclass=_ConfigTypeEnumTypeWrapper): + """ + TODO: REPLACE + """ + + DEVICE_CONFIG: AdminMessage.ConfigType.ValueType # 0 + """ + TODO: REPLACE + """ + POSITION_CONFIG: AdminMessage.ConfigType.ValueType # 1 + """ + TODO: REPLACE + """ + POWER_CONFIG: AdminMessage.ConfigType.ValueType # 2 + """ + TODO: REPLACE + """ + NETWORK_CONFIG: AdminMessage.ConfigType.ValueType # 3 + """ + TODO: REPLACE + """ + DISPLAY_CONFIG: AdminMessage.ConfigType.ValueType # 4 + """ + TODO: REPLACE + """ + LORA_CONFIG: AdminMessage.ConfigType.ValueType # 5 + """ + TODO: REPLACE + """ + BLUETOOTH_CONFIG: AdminMessage.ConfigType.ValueType # 6 + """ + TODO: REPLACE + """ + SECURITY_CONFIG: AdminMessage.ConfigType.ValueType # 7 + """ + TODO: REPLACE + """ + SESSIONKEY_CONFIG: AdminMessage.ConfigType.ValueType # 8 + """ + Session key config + """ + DEVICEUI_CONFIG: AdminMessage.ConfigType.ValueType # 9 + """ + device-ui config + """ + + class _ModuleConfigType: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + + class _ModuleConfigTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[AdminMessage._ModuleConfigType.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + MQTT_CONFIG: AdminMessage._ModuleConfigType.ValueType # 0 + """ + TODO: REPLACE + """ + SERIAL_CONFIG: AdminMessage._ModuleConfigType.ValueType # 1 + """ + TODO: REPLACE + """ + EXTNOTIF_CONFIG: AdminMessage._ModuleConfigType.ValueType # 2 + """ + TODO: REPLACE + """ + STOREFORWARD_CONFIG: AdminMessage._ModuleConfigType.ValueType # 3 + """ + TODO: REPLACE + """ + RANGETEST_CONFIG: AdminMessage._ModuleConfigType.ValueType # 4 + """ + TODO: REPLACE + """ + TELEMETRY_CONFIG: AdminMessage._ModuleConfigType.ValueType # 5 + """ + TODO: REPLACE + """ + CANNEDMSG_CONFIG: AdminMessage._ModuleConfigType.ValueType # 6 + """ + TODO: REPLACE + """ + AUDIO_CONFIG: AdminMessage._ModuleConfigType.ValueType # 7 + """ + TODO: REPLACE + """ + REMOTEHARDWARE_CONFIG: AdminMessage._ModuleConfigType.ValueType # 8 + """ + TODO: REPLACE + """ + NEIGHBORINFO_CONFIG: AdminMessage._ModuleConfigType.ValueType # 9 + """ + TODO: REPLACE + """ + AMBIENTLIGHTING_CONFIG: AdminMessage._ModuleConfigType.ValueType # 10 + """ + TODO: REPLACE + """ + DETECTIONSENSOR_CONFIG: AdminMessage._ModuleConfigType.ValueType # 11 + """ + TODO: REPLACE + """ + PAXCOUNTER_CONFIG: AdminMessage._ModuleConfigType.ValueType # 12 + """ + TODO: REPLACE + """ + + class ModuleConfigType(_ModuleConfigType, metaclass=_ModuleConfigTypeEnumTypeWrapper): + """ + TODO: REPLACE + """ + + MQTT_CONFIG: AdminMessage.ModuleConfigType.ValueType # 0 + """ + TODO: REPLACE + """ + SERIAL_CONFIG: AdminMessage.ModuleConfigType.ValueType # 1 + """ + TODO: REPLACE + """ + EXTNOTIF_CONFIG: AdminMessage.ModuleConfigType.ValueType # 2 + """ + TODO: REPLACE + """ + STOREFORWARD_CONFIG: AdminMessage.ModuleConfigType.ValueType # 3 + """ + TODO: REPLACE + """ + RANGETEST_CONFIG: AdminMessage.ModuleConfigType.ValueType # 4 + """ + TODO: REPLACE + """ + TELEMETRY_CONFIG: AdminMessage.ModuleConfigType.ValueType # 5 + """ + TODO: REPLACE + """ + CANNEDMSG_CONFIG: AdminMessage.ModuleConfigType.ValueType # 6 + """ + TODO: REPLACE + """ + AUDIO_CONFIG: AdminMessage.ModuleConfigType.ValueType # 7 + """ + TODO: REPLACE + """ + REMOTEHARDWARE_CONFIG: AdminMessage.ModuleConfigType.ValueType # 8 + """ + TODO: REPLACE + """ + NEIGHBORINFO_CONFIG: AdminMessage.ModuleConfigType.ValueType # 9 + """ + TODO: REPLACE + """ + AMBIENTLIGHTING_CONFIG: AdminMessage.ModuleConfigType.ValueType # 10 + """ + TODO: REPLACE + """ + DETECTIONSENSOR_CONFIG: AdminMessage.ModuleConfigType.ValueType # 11 + """ + TODO: REPLACE + """ + PAXCOUNTER_CONFIG: AdminMessage.ModuleConfigType.ValueType # 12 + """ + TODO: REPLACE + """ + + class _BackupLocation: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + + class _BackupLocationEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[AdminMessage._BackupLocation.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + FLASH: AdminMessage._BackupLocation.ValueType # 0 + """ + Backup to the internal flash + """ + SD: AdminMessage._BackupLocation.ValueType # 1 + """ + Backup to the SD card + """ + + class BackupLocation(_BackupLocation, metaclass=_BackupLocationEnumTypeWrapper): ... + FLASH: AdminMessage.BackupLocation.ValueType # 0 + """ + Backup to the internal flash + """ + SD: AdminMessage.BackupLocation.ValueType # 1 + """ + Backup to the SD card + """ + + @typing.final + class InputEvent(google.protobuf.message.Message): + """ + Input event message to be sent to the node. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + EVENT_CODE_FIELD_NUMBER: builtins.int + KB_CHAR_FIELD_NUMBER: builtins.int + TOUCH_X_FIELD_NUMBER: builtins.int + TOUCH_Y_FIELD_NUMBER: builtins.int + event_code: builtins.int + """ + The input event code + """ + kb_char: builtins.int + """ + Keyboard character code + """ + touch_x: builtins.int + """ + The touch X coordinate + """ + touch_y: builtins.int + """ + The touch Y coordinate + """ + def __init__( + self, + *, + event_code: builtins.int = ..., + kb_char: builtins.int = ..., + touch_x: builtins.int = ..., + touch_y: builtins.int = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["event_code", b"event_code", "kb_char", b"kb_char", "touch_x", b"touch_x", "touch_y", b"touch_y"]) -> None: ... + + SESSION_PASSKEY_FIELD_NUMBER: builtins.int + GET_CHANNEL_REQUEST_FIELD_NUMBER: builtins.int + GET_CHANNEL_RESPONSE_FIELD_NUMBER: builtins.int + GET_OWNER_REQUEST_FIELD_NUMBER: builtins.int + GET_OWNER_RESPONSE_FIELD_NUMBER: builtins.int + GET_CONFIG_REQUEST_FIELD_NUMBER: builtins.int + GET_CONFIG_RESPONSE_FIELD_NUMBER: builtins.int + GET_MODULE_CONFIG_REQUEST_FIELD_NUMBER: builtins.int + GET_MODULE_CONFIG_RESPONSE_FIELD_NUMBER: builtins.int + GET_CANNED_MESSAGE_MODULE_MESSAGES_REQUEST_FIELD_NUMBER: builtins.int + GET_CANNED_MESSAGE_MODULE_MESSAGES_RESPONSE_FIELD_NUMBER: builtins.int + GET_DEVICE_METADATA_REQUEST_FIELD_NUMBER: builtins.int + GET_DEVICE_METADATA_RESPONSE_FIELD_NUMBER: builtins.int + GET_RINGTONE_REQUEST_FIELD_NUMBER: builtins.int + GET_RINGTONE_RESPONSE_FIELD_NUMBER: builtins.int + GET_DEVICE_CONNECTION_STATUS_REQUEST_FIELD_NUMBER: builtins.int + GET_DEVICE_CONNECTION_STATUS_RESPONSE_FIELD_NUMBER: builtins.int + SET_HAM_MODE_FIELD_NUMBER: builtins.int + GET_NODE_REMOTE_HARDWARE_PINS_REQUEST_FIELD_NUMBER: builtins.int + GET_NODE_REMOTE_HARDWARE_PINS_RESPONSE_FIELD_NUMBER: builtins.int + ENTER_DFU_MODE_REQUEST_FIELD_NUMBER: builtins.int + DELETE_FILE_REQUEST_FIELD_NUMBER: builtins.int + SET_SCALE_FIELD_NUMBER: builtins.int + BACKUP_PREFERENCES_FIELD_NUMBER: builtins.int + RESTORE_PREFERENCES_FIELD_NUMBER: builtins.int + REMOVE_BACKUP_PREFERENCES_FIELD_NUMBER: builtins.int + SEND_INPUT_EVENT_FIELD_NUMBER: builtins.int + SET_OWNER_FIELD_NUMBER: builtins.int + SET_CHANNEL_FIELD_NUMBER: builtins.int + SET_CONFIG_FIELD_NUMBER: builtins.int + SET_MODULE_CONFIG_FIELD_NUMBER: builtins.int + SET_CANNED_MESSAGE_MODULE_MESSAGES_FIELD_NUMBER: builtins.int + SET_RINGTONE_MESSAGE_FIELD_NUMBER: builtins.int + REMOVE_BY_NODENUM_FIELD_NUMBER: builtins.int + SET_FAVORITE_NODE_FIELD_NUMBER: builtins.int + REMOVE_FAVORITE_NODE_FIELD_NUMBER: builtins.int + SET_FIXED_POSITION_FIELD_NUMBER: builtins.int + REMOVE_FIXED_POSITION_FIELD_NUMBER: builtins.int + SET_TIME_ONLY_FIELD_NUMBER: builtins.int + GET_UI_CONFIG_REQUEST_FIELD_NUMBER: builtins.int + GET_UI_CONFIG_RESPONSE_FIELD_NUMBER: builtins.int + STORE_UI_CONFIG_FIELD_NUMBER: builtins.int + SET_IGNORED_NODE_FIELD_NUMBER: builtins.int + REMOVE_IGNORED_NODE_FIELD_NUMBER: builtins.int + BEGIN_EDIT_SETTINGS_FIELD_NUMBER: builtins.int + COMMIT_EDIT_SETTINGS_FIELD_NUMBER: builtins.int + ADD_CONTACT_FIELD_NUMBER: builtins.int + KEY_VERIFICATION_FIELD_NUMBER: builtins.int + FACTORY_RESET_DEVICE_FIELD_NUMBER: builtins.int + REBOOT_OTA_SECONDS_FIELD_NUMBER: builtins.int + EXIT_SIMULATOR_FIELD_NUMBER: builtins.int + REBOOT_SECONDS_FIELD_NUMBER: builtins.int + SHUTDOWN_SECONDS_FIELD_NUMBER: builtins.int + FACTORY_RESET_CONFIG_FIELD_NUMBER: builtins.int + NODEDB_RESET_FIELD_NUMBER: builtins.int + session_passkey: builtins.bytes + """ + The node generates this key and sends it with any get_x_response packets. + The client MUST include the same key with any set_x commands. Key expires after 300 seconds. + Prevents replay attacks for admin messages. + """ + get_channel_request: builtins.int + """ + Send the specified channel in the response to this message + NOTE: This field is sent with the channel index + 1 (to ensure we never try to send 'zero' - which protobufs treats as not present) + """ + get_owner_request: builtins.bool + """ + Send the current owner data in the response to this message. + """ + get_config_request: global___AdminMessage.ConfigType.ValueType + """ + Ask for the following config data to be sent + """ + get_module_config_request: global___AdminMessage.ModuleConfigType.ValueType + """ + Ask for the following config data to be sent + """ + get_canned_message_module_messages_request: builtins.bool + """ + Get the Canned Message Module messages in the response to this message. + """ + get_canned_message_module_messages_response: builtins.str + """ + Get the Canned Message Module messages in the response to this message. + """ + get_device_metadata_request: builtins.bool + """ + Request the node to send device metadata (firmware, protobuf version, etc) + """ + get_ringtone_request: builtins.bool + """ + Get the Ringtone in the response to this message. + """ + get_ringtone_response: builtins.str + """ + Get the Ringtone in the response to this message. + """ + get_device_connection_status_request: builtins.bool + """ + Request the node to send it's connection status + """ + get_node_remote_hardware_pins_request: builtins.bool + """ + Get the mesh's nodes with their available gpio pins for RemoteHardware module use + """ + enter_dfu_mode_request: builtins.bool + """ + Enter (UF2) DFU mode + Only implemented on NRF52 currently + """ + delete_file_request: builtins.str + """ + Delete the file by the specified path from the device + """ + set_scale: builtins.int + """ + Set zero and offset for scale chips + """ + backup_preferences: global___AdminMessage.BackupLocation.ValueType + """ + Backup the node's preferences + """ + restore_preferences: global___AdminMessage.BackupLocation.ValueType + """ + Restore the node's preferences + """ + remove_backup_preferences: global___AdminMessage.BackupLocation.ValueType + """ + Remove backups of the node's preferences + """ + set_canned_message_module_messages: builtins.str + """ + Set the Canned Message Module messages text. + """ + set_ringtone_message: builtins.str + """ + Set the ringtone for ExternalNotification. + """ + remove_by_nodenum: builtins.int + """ + Remove the node by the specified node-num from the NodeDB on the device + """ + set_favorite_node: builtins.int + """ + Set specified node-num to be favorited on the NodeDB on the device + """ + remove_favorite_node: builtins.int + """ + Set specified node-num to be un-favorited on the NodeDB on the device + """ + remove_fixed_position: builtins.bool + """ + Clear fixed position coordinates and then set position.fixed_position = false + """ + set_time_only: builtins.int + """ + Set time only on the node + Convenience method to set the time on the node (as Net quality) without any other position data + """ + get_ui_config_request: builtins.bool + """ + Tell the node to send the stored ui data. + """ + set_ignored_node: builtins.int + """ + Set specified node-num to be ignored on the NodeDB on the device + """ + remove_ignored_node: builtins.int + """ + Set specified node-num to be un-ignored on the NodeDB on the device + """ + begin_edit_settings: builtins.bool + """ + Begins an edit transaction for config, module config, owner, and channel settings changes + This will delay the standard *implicit* save to the file system and subsequent reboot behavior until committed (commit_edit_settings) + """ + commit_edit_settings: builtins.bool + """ + Commits an open transaction for any edits made to config, module config, owner, and channel settings + """ + factory_reset_device: builtins.int + """ + Tell the node to factory reset config everything; all device state and configuration will be returned to factory defaults and BLE bonds will be cleared. + """ + reboot_ota_seconds: builtins.int + """ + Tell the node to reboot into the OTA Firmware in this many seconds (or <0 to cancel reboot) + Only Implemented for ESP32 Devices. This needs to be issued to send a new main firmware via bluetooth. + """ + exit_simulator: builtins.bool + """ + This message is only supported for the simulator Portduino build. + If received the simulator will exit successfully. + """ + reboot_seconds: builtins.int + """ + Tell the node to reboot in this many seconds (or <0 to cancel reboot) + """ + shutdown_seconds: builtins.int + """ + Tell the node to shutdown in this many seconds (or <0 to cancel shutdown) + """ + factory_reset_config: builtins.int + """ + Tell the node to factory reset config; all device state and configuration will be returned to factory defaults; BLE bonds will be preserved. + """ + nodedb_reset: builtins.int + """ + Tell the node to reset the nodedb. + """ + @property + def get_channel_response(self) -> meshtastic.protobuf.channel_pb2.Channel: + """ + TODO: REPLACE + """ + + @property + def get_owner_response(self) -> meshtastic.protobuf.mesh_pb2.User: + """ + TODO: REPLACE + """ + + @property + def get_config_response(self) -> meshtastic.protobuf.config_pb2.Config: + """ + Send the current Config in the response to this message. + """ + + @property + def get_module_config_response(self) -> meshtastic.protobuf.module_config_pb2.ModuleConfig: + """ + Send the current Config in the response to this message. + """ + + @property + def get_device_metadata_response(self) -> meshtastic.protobuf.mesh_pb2.DeviceMetadata: + """ + Device metadata response + """ + + @property + def get_device_connection_status_response(self) -> meshtastic.protobuf.connection_status_pb2.DeviceConnectionStatus: + """ + Device connection status response + """ + + @property + def set_ham_mode(self) -> global___HamParameters: + """ + Setup a node for licensed amateur (ham) radio operation + """ + + @property + def get_node_remote_hardware_pins_response(self) -> global___NodeRemoteHardwarePinsResponse: + """ + Respond with the mesh's nodes with their available gpio pins for RemoteHardware module use + """ + + @property + def send_input_event(self) -> global___AdminMessage.InputEvent: + """ + Send an input event to the node. + This is used to trigger physical input events like button presses, touch events, etc. + """ + + @property + def set_owner(self) -> meshtastic.protobuf.mesh_pb2.User: + """ + Set the owner for this node + """ + + @property + def set_channel(self) -> meshtastic.protobuf.channel_pb2.Channel: + """ + Set channels (using the new API). + A special channel is the "primary channel". + The other records are secondary channels. + Note: only one channel can be marked as primary. + If the client sets a particular channel to be primary, the previous channel will be set to SECONDARY automatically. + """ + + @property + def set_config(self) -> meshtastic.protobuf.config_pb2.Config: + """ + Set the current Config + """ + + @property + def set_module_config(self) -> meshtastic.protobuf.module_config_pb2.ModuleConfig: + """ + Set the current Config + """ + + @property + def set_fixed_position(self) -> meshtastic.protobuf.mesh_pb2.Position: + """ + Set fixed position data on the node and then set the position.fixed_position = true + """ + + @property + def get_ui_config_response(self) -> meshtastic.protobuf.device_ui_pb2.DeviceUIConfig: + """ + Reply stored device ui data. + """ + + @property + def store_ui_config(self) -> meshtastic.protobuf.device_ui_pb2.DeviceUIConfig: + """ + Tell the node to store UI data persistently. + """ + + @property + def add_contact(self) -> global___SharedContact: + """ + Add a contact (User) to the nodedb + """ + + @property + def key_verification(self) -> global___KeyVerificationAdmin: + """ + Initiate or respond to a key verification request + """ + + def __init__( + self, + *, + session_passkey: builtins.bytes = ..., + get_channel_request: builtins.int = ..., + get_channel_response: meshtastic.protobuf.channel_pb2.Channel | None = ..., + get_owner_request: builtins.bool = ..., + get_owner_response: meshtastic.protobuf.mesh_pb2.User | None = ..., + get_config_request: global___AdminMessage.ConfigType.ValueType = ..., + get_config_response: meshtastic.protobuf.config_pb2.Config | None = ..., + get_module_config_request: global___AdminMessage.ModuleConfigType.ValueType = ..., + get_module_config_response: meshtastic.protobuf.module_config_pb2.ModuleConfig | None = ..., + get_canned_message_module_messages_request: builtins.bool = ..., + get_canned_message_module_messages_response: builtins.str = ..., + get_device_metadata_request: builtins.bool = ..., + get_device_metadata_response: meshtastic.protobuf.mesh_pb2.DeviceMetadata | None = ..., + get_ringtone_request: builtins.bool = ..., + get_ringtone_response: builtins.str = ..., + get_device_connection_status_request: builtins.bool = ..., + get_device_connection_status_response: meshtastic.protobuf.connection_status_pb2.DeviceConnectionStatus | None = ..., + set_ham_mode: global___HamParameters | None = ..., + get_node_remote_hardware_pins_request: builtins.bool = ..., + get_node_remote_hardware_pins_response: global___NodeRemoteHardwarePinsResponse | None = ..., + enter_dfu_mode_request: builtins.bool = ..., + delete_file_request: builtins.str = ..., + set_scale: builtins.int = ..., + backup_preferences: global___AdminMessage.BackupLocation.ValueType = ..., + restore_preferences: global___AdminMessage.BackupLocation.ValueType = ..., + remove_backup_preferences: global___AdminMessage.BackupLocation.ValueType = ..., + send_input_event: global___AdminMessage.InputEvent | None = ..., + set_owner: meshtastic.protobuf.mesh_pb2.User | None = ..., + set_channel: meshtastic.protobuf.channel_pb2.Channel | None = ..., + set_config: meshtastic.protobuf.config_pb2.Config | None = ..., + set_module_config: meshtastic.protobuf.module_config_pb2.ModuleConfig | None = ..., + set_canned_message_module_messages: builtins.str = ..., + set_ringtone_message: builtins.str = ..., + remove_by_nodenum: builtins.int = ..., + set_favorite_node: builtins.int = ..., + remove_favorite_node: builtins.int = ..., + set_fixed_position: meshtastic.protobuf.mesh_pb2.Position | None = ..., + remove_fixed_position: builtins.bool = ..., + set_time_only: builtins.int = ..., + get_ui_config_request: builtins.bool = ..., + get_ui_config_response: meshtastic.protobuf.device_ui_pb2.DeviceUIConfig | None = ..., + store_ui_config: meshtastic.protobuf.device_ui_pb2.DeviceUIConfig | None = ..., + set_ignored_node: builtins.int = ..., + remove_ignored_node: builtins.int = ..., + begin_edit_settings: builtins.bool = ..., + commit_edit_settings: builtins.bool = ..., + add_contact: global___SharedContact | None = ..., + key_verification: global___KeyVerificationAdmin | None = ..., + factory_reset_device: builtins.int = ..., + reboot_ota_seconds: builtins.int = ..., + exit_simulator: builtins.bool = ..., + reboot_seconds: builtins.int = ..., + shutdown_seconds: builtins.int = ..., + factory_reset_config: builtins.int = ..., + nodedb_reset: builtins.int = ..., + ) -> 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", "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"]) -> 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", "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"]) -> 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", "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"] | None: ... + +global___AdminMessage = AdminMessage + +@typing.final +class HamParameters(google.protobuf.message.Message): + """ + Parameters for setting up Meshtastic for ameteur radio usage + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + CALL_SIGN_FIELD_NUMBER: builtins.int + TX_POWER_FIELD_NUMBER: builtins.int + FREQUENCY_FIELD_NUMBER: builtins.int + SHORT_NAME_FIELD_NUMBER: builtins.int + call_sign: builtins.str + """ + Amateur radio call sign, eg. KD2ABC + """ + tx_power: builtins.int + """ + Transmit power in dBm at the LoRA transceiver, not including any amplification + """ + frequency: builtins.float + """ + The selected frequency of LoRA operation + Please respect your local laws, regulations, and band plans. + Ensure your radio is capable of operating of the selected frequency before setting this. + """ + short_name: builtins.str + """ + Optional short name of user + """ + def __init__( + self, + *, + call_sign: builtins.str = ..., + tx_power: builtins.int = ..., + frequency: builtins.float = ..., + short_name: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["call_sign", b"call_sign", "frequency", b"frequency", "short_name", b"short_name", "tx_power", b"tx_power"]) -> None: ... + +global___HamParameters = HamParameters + +@typing.final +class NodeRemoteHardwarePinsResponse(google.protobuf.message.Message): + """ + Response envelope for node_remote_hardware_pins + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + NODE_REMOTE_HARDWARE_PINS_FIELD_NUMBER: builtins.int + @property + def node_remote_hardware_pins(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[meshtastic.protobuf.mesh_pb2.NodeRemoteHardwarePin]: + """ + Nodes and their respective remote hardware GPIO pins + """ + + def __init__( + self, + *, + node_remote_hardware_pins: collections.abc.Iterable[meshtastic.protobuf.mesh_pb2.NodeRemoteHardwarePin] | None = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["node_remote_hardware_pins", b"node_remote_hardware_pins"]) -> None: ... + +global___NodeRemoteHardwarePinsResponse = NodeRemoteHardwarePinsResponse + +@typing.final +class SharedContact(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + NODE_NUM_FIELD_NUMBER: builtins.int + USER_FIELD_NUMBER: builtins.int + SHOULD_IGNORE_FIELD_NUMBER: builtins.int + MANUALLY_VERIFIED_FIELD_NUMBER: builtins.int + node_num: builtins.int + """ + The node number of the contact + """ + should_ignore: builtins.bool + """ + Add this contact to the blocked / ignored list + """ + manually_verified: builtins.bool + """ + Set the IS_KEY_MANUALLY_VERIFIED bit + """ + @property + def user(self) -> meshtastic.protobuf.mesh_pb2.User: + """ + The User of the contact + """ + + def __init__( + self, + *, + node_num: builtins.int = ..., + user: meshtastic.protobuf.mesh_pb2.User | None = ..., + should_ignore: builtins.bool = ..., + manually_verified: builtins.bool = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["user", b"user"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["manually_verified", b"manually_verified", "node_num", b"node_num", "should_ignore", b"should_ignore", "user", b"user"]) -> None: ... + +global___SharedContact = SharedContact + +@typing.final +class KeyVerificationAdmin(google.protobuf.message.Message): + """ + This message is used by a client to initiate or complete a key verification + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + class _MessageType: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + + class _MessageTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[KeyVerificationAdmin._MessageType.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + INITIATE_VERIFICATION: KeyVerificationAdmin._MessageType.ValueType # 0 + """ + This is the first stage, where a client initiates + """ + PROVIDE_SECURITY_NUMBER: KeyVerificationAdmin._MessageType.ValueType # 1 + """ + After the nonce has been returned over the mesh, the client prompts for the security number + And uses this message to provide it to the node. + """ + DO_VERIFY: KeyVerificationAdmin._MessageType.ValueType # 2 + """ + Once the user has compared the verification message, this message notifies the node. + """ + DO_NOT_VERIFY: KeyVerificationAdmin._MessageType.ValueType # 3 + """ + This is the cancel path, can be taken at any point + """ + + class MessageType(_MessageType, metaclass=_MessageTypeEnumTypeWrapper): + """ + Three stages of this request. + """ + + INITIATE_VERIFICATION: KeyVerificationAdmin.MessageType.ValueType # 0 + """ + This is the first stage, where a client initiates + """ + PROVIDE_SECURITY_NUMBER: KeyVerificationAdmin.MessageType.ValueType # 1 + """ + After the nonce has been returned over the mesh, the client prompts for the security number + And uses this message to provide it to the node. + """ + DO_VERIFY: KeyVerificationAdmin.MessageType.ValueType # 2 + """ + Once the user has compared the verification message, this message notifies the node. + """ + DO_NOT_VERIFY: KeyVerificationAdmin.MessageType.ValueType # 3 + """ + This is the cancel path, can be taken at any point + """ + + MESSAGE_TYPE_FIELD_NUMBER: builtins.int + REMOTE_NODENUM_FIELD_NUMBER: builtins.int + NONCE_FIELD_NUMBER: builtins.int + SECURITY_NUMBER_FIELD_NUMBER: builtins.int + message_type: global___KeyVerificationAdmin.MessageType.ValueType + remote_nodenum: builtins.int + """ + The nodenum we're requesting + """ + nonce: builtins.int + """ + The nonce is used to track the connection + """ + security_number: builtins.int + """ + The 4 digit code generated by the remote node, and communicated outside the mesh + """ + def __init__( + self, + *, + message_type: global___KeyVerificationAdmin.MessageType.ValueType = ..., + remote_nodenum: builtins.int = ..., + nonce: builtins.int = ..., + security_number: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["_security_number", b"_security_number", "security_number", b"security_number"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["_security_number", b"_security_number", "message_type", b"message_type", "nonce", b"nonce", "remote_nodenum", b"remote_nodenum", "security_number", b"security_number"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["_security_number", b"_security_number"]) -> typing.Literal["security_number"] | None: ... + +global___KeyVerificationAdmin = KeyVerificationAdmin diff --git a/meshtastic/protobuf/apponly_pb2.py b/meshtastic/protobuf/apponly_pb2.py new file mode 100644 index 00000000..70e04e81 --- /dev/null +++ b/meshtastic/protobuf/apponly_pb2.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: meshtastic/protobuf/apponly.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from meshtastic.protobuf import channel_pb2 as meshtastic_dot_protobuf_dot_channel__pb2 +from meshtastic.protobuf import config_pb2 as meshtastic_dot_protobuf_dot_config__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n!meshtastic/protobuf/apponly.proto\x12\x13meshtastic.protobuf\x1a!meshtastic/protobuf/channel.proto\x1a meshtastic/protobuf/config.proto\"\x81\x01\n\nChannelSet\x12\x36\n\x08settings\x18\x01 \x03(\x0b\x32$.meshtastic.protobuf.ChannelSettings\x12;\n\x0blora_config\x18\x02 \x01(\x0b\x32&.meshtastic.protobuf.Config.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) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.apponly_pb2', _globals) +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 +# @@protoc_insertion_point(module_scope) diff --git a/meshtastic/protobuf/apponly_pb2.pyi b/meshtastic/protobuf/apponly_pb2.pyi new file mode 100644 index 00000000..c8a34bc6 --- /dev/null +++ b/meshtastic/protobuf/apponly_pb2.pyi @@ -0,0 +1,52 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" + +import builtins +import collections.abc +import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.message +import meshtastic.protobuf.channel_pb2 +import meshtastic.protobuf.config_pb2 +import typing + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing.final +class ChannelSet(google.protobuf.message.Message): + """ + This is the most compact possible representation for a set of channels. + It includes only one PRIMARY channel (which must be first) and + any SECONDARY channels. + No DISABLED channels are included. + This abstraction is used only on the the 'app side' of the world (ie python, javascript and android etc) to show a group of Channels as a (long) URL + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + SETTINGS_FIELD_NUMBER: builtins.int + LORA_CONFIG_FIELD_NUMBER: builtins.int + @property + def settings(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[meshtastic.protobuf.channel_pb2.ChannelSettings]: + """ + Channel list with settings + """ + + @property + def lora_config(self) -> meshtastic.protobuf.config_pb2.Config.LoRaConfig: + """ + LoRa config + """ + + def __init__( + self, + *, + settings: collections.abc.Iterable[meshtastic.protobuf.channel_pb2.ChannelSettings] | None = ..., + lora_config: meshtastic.protobuf.config_pb2.Config.LoRaConfig | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["lora_config", b"lora_config"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["lora_config", b"lora_config", "settings", b"settings"]) -> None: ... + +global___ChannelSet = ChannelSet diff --git a/meshtastic/protobuf/atak_pb2.py b/meshtastic/protobuf/atak_pb2.py new file mode 100644 index 00000000..b0fee1ae --- /dev/null +++ b/meshtastic/protobuf/atak_pb2.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: meshtastic/protobuf/atak.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1emeshtastic/protobuf/atak.proto\x12\x13meshtastic.protobuf\"\xa5\x02\n\tTAKPacket\x12\x15\n\ris_compressed\x18\x01 \x01(\x08\x12-\n\x07\x63ontact\x18\x02 \x01(\x0b\x32\x1c.meshtastic.protobuf.Contact\x12)\n\x05group\x18\x03 \x01(\x0b\x32\x1a.meshtastic.protobuf.Group\x12+\n\x06status\x18\x04 \x01(\x0b\x32\x1b.meshtastic.protobuf.Status\x12\'\n\x03pli\x18\x05 \x01(\x0b\x32\x18.meshtastic.protobuf.PLIH\x00\x12,\n\x04\x63hat\x18\x06 \x01(\x0b\x32\x1c.meshtastic.protobuf.GeoChatH\x00\x12\x10\n\x06\x64\x65tail\x18\x07 \x01(\x0cH\x00\x42\x11\n\x0fpayload_variant\"\\\n\x07GeoChat\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\x0f\n\x02to\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0bto_callsign\x18\x03 \x01(\tH\x01\x88\x01\x01\x42\x05\n\x03_toB\x0e\n\x0c_to_callsign\"_\n\x05Group\x12-\n\x04role\x18\x01 \x01(\x0e\x32\x1f.meshtastic.protobuf.MemberRole\x12\'\n\x04team\x18\x02 \x01(\x0e\x32\x19.meshtastic.protobuf.Team\"\x19\n\x06Status\x12\x0f\n\x07\x62\x61ttery\x18\x01 \x01(\r\"4\n\x07\x43ontact\x12\x10\n\x08\x63\x61llsign\x18\x01 \x01(\t\x12\x17\n\x0f\x64\x65vice_callsign\x18\x02 \x01(\t\"_\n\x03PLI\x12\x12\n\nlatitude_i\x18\x01 \x01(\x0f\x12\x13\n\x0blongitude_i\x18\x02 \x01(\x0f\x12\x10\n\x08\x61ltitude\x18\x03 \x01(\x05\x12\r\n\x05speed\x18\x04 \x01(\r\x12\x0e\n\x06\x63ourse\x18\x05 \x01(\r*\xc0\x01\n\x04Team\x12\x14\n\x10Unspecifed_Color\x10\x00\x12\t\n\x05White\x10\x01\x12\n\n\x06Yellow\x10\x02\x12\n\n\x06Orange\x10\x03\x12\x0b\n\x07Magenta\x10\x04\x12\x07\n\x03Red\x10\x05\x12\n\n\x06Maroon\x10\x06\x12\n\n\x06Purple\x10\x07\x12\r\n\tDark_Blue\x10\x08\x12\x08\n\x04\x42lue\x10\t\x12\x08\n\x04\x43yan\x10\n\x12\x08\n\x04Teal\x10\x0b\x12\t\n\x05Green\x10\x0c\x12\x0e\n\nDark_Green\x10\r\x12\t\n\x05\x42rown\x10\x0e*\x7f\n\nMemberRole\x12\x0e\n\nUnspecifed\x10\x00\x12\x0e\n\nTeamMember\x10\x01\x12\x0c\n\x08TeamLead\x10\x02\x12\x06\n\x02HQ\x10\x03\x12\n\n\x06Sniper\x10\x04\x12\t\n\x05Medic\x10\x05\x12\x13\n\x0f\x46orwardObserver\x10\x06\x12\x07\n\x03RTO\x10\x07\x12\x06\n\x02K9\x10\x08\x42`\n\x14org.meshtastic.protoB\nATAKProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.atak_pb2', _globals) +if _descriptor._USE_C_DESCRIPTORS == False: + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\nATAKProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' + _globals['_TEAM']._serialized_start=721 + _globals['_TEAM']._serialized_end=913 + _globals['_MEMBERROLE']._serialized_start=915 + _globals['_MEMBERROLE']._serialized_end=1042 + _globals['_TAKPACKET']._serialized_start=56 + _globals['_TAKPACKET']._serialized_end=349 + _globals['_GEOCHAT']._serialized_start=351 + _globals['_GEOCHAT']._serialized_end=443 + _globals['_GROUP']._serialized_start=445 + _globals['_GROUP']._serialized_end=540 + _globals['_STATUS']._serialized_start=542 + _globals['_STATUS']._serialized_end=567 + _globals['_CONTACT']._serialized_start=569 + _globals['_CONTACT']._serialized_end=621 + _globals['_PLI']._serialized_start=623 + _globals['_PLI']._serialized_end=718 +# @@protoc_insertion_point(module_scope) diff --git a/meshtastic/protobuf/atak_pb2.pyi b/meshtastic/protobuf/atak_pb2.pyi new file mode 100644 index 00000000..34814f90 --- /dev/null +++ b/meshtastic/protobuf/atak_pb2.pyi @@ -0,0 +1,477 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" + +import builtins +import google.protobuf.descriptor +import google.protobuf.internal.enum_type_wrapper +import google.protobuf.message +import sys +import typing + +if sys.version_info >= (3, 10): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +class _Team: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _TeamEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_Team.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + Unspecifed_Color: _Team.ValueType # 0 + """ + Unspecifed + """ + White: _Team.ValueType # 1 + """ + White + """ + Yellow: _Team.ValueType # 2 + """ + Yellow + """ + Orange: _Team.ValueType # 3 + """ + Orange + """ + Magenta: _Team.ValueType # 4 + """ + Magenta + """ + Red: _Team.ValueType # 5 + """ + Red + """ + Maroon: _Team.ValueType # 6 + """ + Maroon + """ + Purple: _Team.ValueType # 7 + """ + Purple + """ + Dark_Blue: _Team.ValueType # 8 + """ + Dark Blue + """ + Blue: _Team.ValueType # 9 + """ + Blue + """ + Cyan: _Team.ValueType # 10 + """ + Cyan + """ + Teal: _Team.ValueType # 11 + """ + Teal + """ + Green: _Team.ValueType # 12 + """ + Green + """ + Dark_Green: _Team.ValueType # 13 + """ + Dark Green + """ + Brown: _Team.ValueType # 14 + """ + Brown + """ + +class Team(_Team, metaclass=_TeamEnumTypeWrapper): ... + +Unspecifed_Color: Team.ValueType # 0 +""" +Unspecifed +""" +White: Team.ValueType # 1 +""" +White +""" +Yellow: Team.ValueType # 2 +""" +Yellow +""" +Orange: Team.ValueType # 3 +""" +Orange +""" +Magenta: Team.ValueType # 4 +""" +Magenta +""" +Red: Team.ValueType # 5 +""" +Red +""" +Maroon: Team.ValueType # 6 +""" +Maroon +""" +Purple: Team.ValueType # 7 +""" +Purple +""" +Dark_Blue: Team.ValueType # 8 +""" +Dark Blue +""" +Blue: Team.ValueType # 9 +""" +Blue +""" +Cyan: Team.ValueType # 10 +""" +Cyan +""" +Teal: Team.ValueType # 11 +""" +Teal +""" +Green: Team.ValueType # 12 +""" +Green +""" +Dark_Green: Team.ValueType # 13 +""" +Dark Green +""" +Brown: Team.ValueType # 14 +""" +Brown +""" +global___Team = Team + +class _MemberRole: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _MemberRoleEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_MemberRole.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + Unspecifed: _MemberRole.ValueType # 0 + """ + Unspecifed + """ + TeamMember: _MemberRole.ValueType # 1 + """ + Team Member + """ + TeamLead: _MemberRole.ValueType # 2 + """ + Team Lead + """ + HQ: _MemberRole.ValueType # 3 + """ + Headquarters + """ + Sniper: _MemberRole.ValueType # 4 + """ + Airsoft enthusiast + """ + Medic: _MemberRole.ValueType # 5 + """ + Medic + """ + ForwardObserver: _MemberRole.ValueType # 6 + """ + ForwardObserver + """ + RTO: _MemberRole.ValueType # 7 + """ + Radio Telephone Operator + """ + K9: _MemberRole.ValueType # 8 + """ + Doggo + """ + +class MemberRole(_MemberRole, metaclass=_MemberRoleEnumTypeWrapper): + """ + Role of the group member + """ + +Unspecifed: MemberRole.ValueType # 0 +""" +Unspecifed +""" +TeamMember: MemberRole.ValueType # 1 +""" +Team Member +""" +TeamLead: MemberRole.ValueType # 2 +""" +Team Lead +""" +HQ: MemberRole.ValueType # 3 +""" +Headquarters +""" +Sniper: MemberRole.ValueType # 4 +""" +Airsoft enthusiast +""" +Medic: MemberRole.ValueType # 5 +""" +Medic +""" +ForwardObserver: MemberRole.ValueType # 6 +""" +ForwardObserver +""" +RTO: MemberRole.ValueType # 7 +""" +Radio Telephone Operator +""" +K9: MemberRole.ValueType # 8 +""" +Doggo +""" +global___MemberRole = MemberRole + +@typing.final +class TAKPacket(google.protobuf.message.Message): + """ + Packets for the official ATAK Plugin + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + IS_COMPRESSED_FIELD_NUMBER: builtins.int + CONTACT_FIELD_NUMBER: builtins.int + GROUP_FIELD_NUMBER: builtins.int + STATUS_FIELD_NUMBER: builtins.int + PLI_FIELD_NUMBER: builtins.int + CHAT_FIELD_NUMBER: builtins.int + DETAIL_FIELD_NUMBER: builtins.int + is_compressed: builtins.bool + """ + Are the payloads strings compressed for LoRA transport? + """ + detail: builtins.bytes + """ + Generic CoT detail XML + May be compressed / truncated by the sender (EUD) + """ + @property + def contact(self) -> global___Contact: + """ + The contact / callsign for ATAK user + """ + + @property + def group(self) -> global___Group: + """ + The group for ATAK user + """ + + @property + def status(self) -> global___Status: + """ + The status of the ATAK EUD + """ + + @property + def pli(self) -> global___PLI: + """ + TAK position report + """ + + @property + def chat(self) -> global___GeoChat: + """ + ATAK GeoChat message + """ + + def __init__( + self, + *, + is_compressed: builtins.bool = ..., + contact: global___Contact | None = ..., + group: global___Group | None = ..., + status: global___Status | None = ..., + pli: global___PLI | None = ..., + chat: global___GeoChat | None = ..., + detail: builtins.bytes = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["chat", b"chat", "contact", b"contact", "detail", b"detail", "group", b"group", "payload_variant", b"payload_variant", "pli", b"pli", "status", b"status"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["chat", b"chat", "contact", b"contact", "detail", b"detail", "group", b"group", "is_compressed", b"is_compressed", "payload_variant", b"payload_variant", "pli", b"pli", "status", b"status"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["payload_variant", b"payload_variant"]) -> typing.Literal["pli", "chat", "detail"] | None: ... + +global___TAKPacket = TAKPacket + +@typing.final +class GeoChat(google.protobuf.message.Message): + """ + ATAK GeoChat message + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + MESSAGE_FIELD_NUMBER: builtins.int + TO_FIELD_NUMBER: builtins.int + TO_CALLSIGN_FIELD_NUMBER: builtins.int + message: builtins.str + """ + The text message + """ + to: builtins.str + """ + Uid recipient of the message + """ + to_callsign: builtins.str + """ + Callsign of the recipient for the message + """ + def __init__( + self, + *, + message: builtins.str = ..., + to: builtins.str | None = ..., + to_callsign: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["_to", b"_to", "_to_callsign", b"_to_callsign", "to", b"to", "to_callsign", b"to_callsign"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["_to", b"_to", "_to_callsign", b"_to_callsign", "message", b"message", "to", b"to", "to_callsign", b"to_callsign"]) -> None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_to", b"_to"]) -> typing.Literal["to"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_to_callsign", b"_to_callsign"]) -> typing.Literal["to_callsign"] | None: ... + +global___GeoChat = GeoChat + +@typing.final +class Group(google.protobuf.message.Message): + """ + ATAK Group + <__group role='Team Member' name='Cyan'/> + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ROLE_FIELD_NUMBER: builtins.int + TEAM_FIELD_NUMBER: builtins.int + role: global___MemberRole.ValueType + """ + Role of the group member + """ + team: global___Team.ValueType + """ + Team (color) + Default Cyan + """ + def __init__( + self, + *, + role: global___MemberRole.ValueType = ..., + team: global___Team.ValueType = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["role", b"role", "team", b"team"]) -> None: ... + +global___Group = Group + +@typing.final +class Status(google.protobuf.message.Message): + """ + ATAK EUD Status + + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + BATTERY_FIELD_NUMBER: builtins.int + battery: builtins.int + """ + Battery level + """ + def __init__( + self, + *, + battery: builtins.int = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["battery", b"battery"]) -> None: ... + +global___Status = Status + +@typing.final +class Contact(google.protobuf.message.Message): + """ + ATAK Contact + + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + CALLSIGN_FIELD_NUMBER: builtins.int + DEVICE_CALLSIGN_FIELD_NUMBER: builtins.int + callsign: builtins.str + """ + Callsign + """ + device_callsign: builtins.str + """ + Device callsign + + IP address of endpoint in integer form (0.0.0.0 default) + """ + def __init__( + self, + *, + callsign: builtins.str = ..., + device_callsign: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["callsign", b"callsign", "device_callsign", b"device_callsign"]) -> None: ... + +global___Contact = Contact + +@typing.final +class PLI(google.protobuf.message.Message): + """ + Position Location Information from ATAK + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + LATITUDE_I_FIELD_NUMBER: builtins.int + LONGITUDE_I_FIELD_NUMBER: builtins.int + ALTITUDE_FIELD_NUMBER: builtins.int + SPEED_FIELD_NUMBER: builtins.int + COURSE_FIELD_NUMBER: builtins.int + latitude_i: builtins.int + """ + The new preferred location encoding, multiply by 1e-7 to get degrees + in floating point + """ + longitude_i: builtins.int + """ + The new preferred location encoding, multiply by 1e-7 to get degrees + in floating point + """ + altitude: builtins.int + """ + Altitude (ATAK prefers HAE) + """ + speed: builtins.int + """ + Speed + """ + course: builtins.int + """ + Course in degrees + """ + def __init__( + self, + *, + latitude_i: builtins.int = ..., + longitude_i: builtins.int = ..., + altitude: builtins.int = ..., + speed: builtins.int = ..., + course: builtins.int = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["altitude", b"altitude", "course", b"course", "latitude_i", b"latitude_i", "longitude_i", b"longitude_i", "speed", b"speed"]) -> None: ... + +global___PLI = PLI diff --git a/meshtastic/protobuf/cannedmessages_pb2.py b/meshtastic/protobuf/cannedmessages_pb2.py new file mode 100644 index 00000000..569fe149 --- /dev/null +++ b/meshtastic/protobuf/cannedmessages_pb2.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: meshtastic/protobuf/cannedmessages.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n(meshtastic/protobuf/cannedmessages.proto\x12\x13meshtastic.protobuf\"-\n\x19\x43\x61nnedMessageModuleConfig\x12\x10\n\x08messages\x18\x01 \x01(\tBo\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) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.cannedmessages_pb2', _globals) +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 +# @@protoc_insertion_point(module_scope) diff --git a/meshtastic/protobuf/cannedmessages_pb2.pyi b/meshtastic/protobuf/cannedmessages_pb2.pyi new file mode 100644 index 00000000..33c5e2ba --- /dev/null +++ b/meshtastic/protobuf/cannedmessages_pb2.pyi @@ -0,0 +1,33 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" + +import builtins +import google.protobuf.descriptor +import google.protobuf.message +import typing + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing.final +class CannedMessageModuleConfig(google.protobuf.message.Message): + """ + Canned message module configuration. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + MESSAGES_FIELD_NUMBER: builtins.int + messages: builtins.str + """ + Predefined messages for canned message module separated by '|' characters. + """ + def __init__( + self, + *, + messages: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["messages", b"messages"]) -> None: ... + +global___CannedMessageModuleConfig = CannedMessageModuleConfig diff --git a/meshtastic/protobuf/channel_pb2.py b/meshtastic/protobuf/channel_pb2.py new file mode 100644 index 00000000..94a5f001 --- /dev/null +++ b/meshtastic/protobuf/channel_pb2.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: meshtastic/protobuf/channel.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n!meshtastic/protobuf/channel.proto\x12\x13meshtastic.protobuf\"\xc1\x01\n\x0f\x43hannelSettings\x12\x17\n\x0b\x63hannel_num\x18\x01 \x01(\rB\x02\x18\x01\x12\x0b\n\x03psk\x18\x02 \x01(\x0c\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\n\n\x02id\x18\x04 \x01(\x07\x12\x16\n\x0euplink_enabled\x18\x05 \x01(\x08\x12\x18\n\x10\x64ownlink_enabled\x18\x06 \x01(\x08\x12<\n\x0fmodule_settings\x18\x07 \x01(\x0b\x32#.meshtastic.protobuf.ModuleSettings\">\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') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.channel_pb2', _globals) +if _descriptor._USE_C_DESCRIPTORS == False: + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'\n\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 +# @@protoc_insertion_point(module_scope) diff --git a/meshtastic/protobuf/channel_pb2.pyi b/meshtastic/protobuf/channel_pb2.pyi new file mode 100644 index 00000000..6e2be597 --- /dev/null +++ b/meshtastic/protobuf/channel_pb2.pyi @@ -0,0 +1,234 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" + +import builtins +import google.protobuf.descriptor +import google.protobuf.internal.enum_type_wrapper +import google.protobuf.message +import sys +import typing + +if sys.version_info >= (3, 10): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing.final +class ChannelSettings(google.protobuf.message.Message): + """ + This information can be encoded as a QRcode/url so that other users can configure + their radio to join the same channel. + A note about how channel names are shown to users: channelname-X + poundsymbol is a prefix used to indicate this is a channel name (idea from @professr). + Where X is a letter from A-Z (base 26) representing a hash of the PSK for this + channel - so that if the user changes anything about the channel (which does + force a new PSK) this letter will also change. Thus preventing user confusion if + two friends try to type in a channel name of "BobsChan" and then can't talk + because their PSKs will be different. + The PSK is hashed into this letter by "0x41 + [xor all bytes of the psk ] modulo 26" + This also allows the option of someday if people have the PSK off (zero), the + users COULD type in a channel name and be able to talk. + FIXME: Add description of multi-channel support and how primary vs secondary channels are used. + FIXME: explain how apps use channels for security. + explain how remote settings and remote gpio are managed as an example + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + CHANNEL_NUM_FIELD_NUMBER: builtins.int + PSK_FIELD_NUMBER: builtins.int + NAME_FIELD_NUMBER: builtins.int + ID_FIELD_NUMBER: builtins.int + UPLINK_ENABLED_FIELD_NUMBER: builtins.int + DOWNLINK_ENABLED_FIELD_NUMBER: builtins.int + MODULE_SETTINGS_FIELD_NUMBER: builtins.int + channel_num: builtins.int + """ + Deprecated in favor of LoraConfig.channel_num + """ + psk: builtins.bytes + """ + A simple pre-shared key for now for crypto. + Must be either 0 bytes (no crypto), 16 bytes (AES128), or 32 bytes (AES256). + A special shorthand is used for 1 byte long psks. + These psks should be treated as only minimally secure, + because they are listed in this source code. + Those bytes are mapped using the following scheme: + `0` = No crypto + `1` = The special "default" channel key: {0xd4, 0xf1, 0xbb, 0x3a, 0x20, 0x29, 0x07, 0x59, 0xf0, 0xbc, 0xff, 0xab, 0xcf, 0x4e, 0x69, 0x01} + `2` through 10 = The default channel key, except with 1 through 9 added to the last byte. + Shown to user as simple1 through 10 + """ + name: builtins.str + """ + A SHORT name that will be packed into the URL. + Less than 12 bytes. + Something for end users to call the channel + If this is the empty string it is assumed that this channel + is the special (minimally secure) "Default"channel. + In user interfaces it should be rendered as a local language translation of "X". + For channel_num hashing empty string will be treated as "X". + Where "X" is selected based on the English words listed above for ModemPreset + """ + id: builtins.int + """ + Used to construct a globally unique channel ID. + The full globally unique ID will be: "name.id" where ID is shown as base36. + Assuming that the number of meshtastic users is below 20K (true for a long time) + the chance of this 64 bit random number colliding with anyone else is super low. + And the penalty for collision is low as well, it just means that anyone trying to decrypt channel messages might need to + try multiple candidate channels. + Any time a non wire compatible change is made to a channel, this field should be regenerated. + There are a small number of 'special' globally known (and fairly) insecure standard channels. + Those channels do not have a numeric id included in the settings, but instead it is pulled from + a table of well known IDs. + (see Well Known Channels FIXME) + """ + uplink_enabled: builtins.bool + """ + If true, messages on the mesh will be sent to the *public* internet by any gateway ndoe + """ + downlink_enabled: builtins.bool + """ + If true, messages seen on the internet will be forwarded to the local mesh. + """ + @property + def module_settings(self) -> global___ModuleSettings: + """ + Per-channel module settings. + """ + + def __init__( + self, + *, + channel_num: builtins.int = ..., + psk: builtins.bytes = ..., + name: builtins.str = ..., + id: builtins.int = ..., + uplink_enabled: builtins.bool = ..., + downlink_enabled: builtins.bool = ..., + module_settings: global___ModuleSettings | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["module_settings", b"module_settings"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["channel_num", b"channel_num", "downlink_enabled", b"downlink_enabled", "id", b"id", "module_settings", b"module_settings", "name", b"name", "psk", b"psk", "uplink_enabled", b"uplink_enabled"]) -> None: ... + +global___ChannelSettings = ChannelSettings + +@typing.final +class ModuleSettings(google.protobuf.message.Message): + """ + This message is specifically for modules to store per-channel configuration data. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + POSITION_PRECISION_FIELD_NUMBER: builtins.int + IS_MUTED_FIELD_NUMBER: builtins.int + position_precision: builtins.int + """ + Bits of precision for the location sent in position packets. + """ + is_muted: builtins.bool + """ + Controls whether or not the client / device should mute the current channel + Useful for noisy public channels you don't necessarily want to disable + """ + def __init__( + self, + *, + position_precision: builtins.int = ..., + is_muted: builtins.bool = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["is_muted", b"is_muted", "position_precision", b"position_precision"]) -> None: ... + +global___ModuleSettings = ModuleSettings + +@typing.final +class Channel(google.protobuf.message.Message): + """ + A pair of a channel number, mode and the (sharable) settings for that channel + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + class _Role: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + + class _RoleEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[Channel._Role.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + DISABLED: Channel._Role.ValueType # 0 + """ + This channel is not in use right now + """ + PRIMARY: Channel._Role.ValueType # 1 + """ + This channel is used to set the frequency for the radio - all other enabled channels must be SECONDARY + """ + SECONDARY: Channel._Role.ValueType # 2 + """ + Secondary channels are only used for encryption/decryption/authentication purposes. + Their radio settings (freq etc) are ignored, only psk is used. + """ + + class Role(_Role, metaclass=_RoleEnumTypeWrapper): + """ + How this channel is being used (or not). + Note: this field is an enum to give us options for the future. + In particular, someday we might make a 'SCANNING' option. + SCANNING channels could have different frequencies and the radio would + occasionally check that freq to see if anything is being transmitted. + For devices that have multiple physical radios attached, we could keep multiple PRIMARY/SCANNING channels active at once to allow + cross band routing as needed. + If a device has only a single radio (the common case) only one channel can be PRIMARY at a time + (but any number of SECONDARY channels can't be sent received on that common frequency) + """ + + DISABLED: Channel.Role.ValueType # 0 + """ + This channel is not in use right now + """ + PRIMARY: Channel.Role.ValueType # 1 + """ + This channel is used to set the frequency for the radio - all other enabled channels must be SECONDARY + """ + SECONDARY: Channel.Role.ValueType # 2 + """ + Secondary channels are only used for encryption/decryption/authentication purposes. + Their radio settings (freq etc) are ignored, only psk is used. + """ + + INDEX_FIELD_NUMBER: builtins.int + SETTINGS_FIELD_NUMBER: builtins.int + ROLE_FIELD_NUMBER: builtins.int + index: builtins.int + """ + The index of this channel in the channel table (from 0 to MAX_NUM_CHANNELS-1) + (Someday - not currently implemented) An index of -1 could be used to mean "set by name", + in which case the target node will find and set the channel by settings.name. + """ + role: global___Channel.Role.ValueType + """ + TODO: REPLACE + """ + @property + def settings(self) -> global___ChannelSettings: + """ + The new settings, or NULL to disable that channel + """ + + def __init__( + self, + *, + index: builtins.int = ..., + settings: global___ChannelSettings | None = ..., + role: global___Channel.Role.ValueType = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["settings", b"settings"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["index", b"index", "role", b"role", "settings", b"settings"]) -> None: ... + +global___Channel = Channel diff --git a/meshtastic/protobuf/clientonly_pb2.py b/meshtastic/protobuf/clientonly_pb2.py new file mode 100644 index 00000000..72455382 --- /dev/null +++ b/meshtastic/protobuf/clientonly_pb2.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: meshtastic/protobuf/clientonly.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from meshtastic.protobuf import localonly_pb2 as meshtastic_dot_protobuf_dot_localonly__pb2 +from meshtastic.protobuf import mesh_pb2 as meshtastic_dot_protobuf_dot_mesh__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') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.clientonly_pb2', _globals) +if _descriptor._USE_C_DESCRIPTORS == False: + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'\n\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 +# @@protoc_insertion_point(module_scope) diff --git a/meshtastic/protobuf/clientonly_pb2.pyi b/meshtastic/protobuf/clientonly_pb2.pyi new file mode 100644 index 00000000..78725be0 --- /dev/null +++ b/meshtastic/protobuf/clientonly_pb2.pyi @@ -0,0 +1,101 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" + +import builtins +import google.protobuf.descriptor +import google.protobuf.message +import meshtastic.protobuf.localonly_pb2 +import meshtastic.protobuf.mesh_pb2 +import typing + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing.final +class DeviceProfile(google.protobuf.message.Message): + """ + This abstraction is used to contain any configuration for provisioning a node on any client. + It is useful for importing and exporting configurations. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + LONG_NAME_FIELD_NUMBER: builtins.int + SHORT_NAME_FIELD_NUMBER: builtins.int + CHANNEL_URL_FIELD_NUMBER: builtins.int + CONFIG_FIELD_NUMBER: builtins.int + MODULE_CONFIG_FIELD_NUMBER: builtins.int + FIXED_POSITION_FIELD_NUMBER: builtins.int + RINGTONE_FIELD_NUMBER: builtins.int + CANNED_MESSAGES_FIELD_NUMBER: builtins.int + long_name: builtins.str + """ + Long name for the node + """ + short_name: builtins.str + """ + Short name of the node + """ + channel_url: builtins.str + """ + The url of the channels from our node + """ + ringtone: builtins.str + """ + Ringtone for ExternalNotification + """ + canned_messages: builtins.str + """ + Predefined messages for CannedMessage + """ + @property + def config(self) -> meshtastic.protobuf.localonly_pb2.LocalConfig: + """ + The Config of the node + """ + + @property + def module_config(self) -> meshtastic.protobuf.localonly_pb2.LocalModuleConfig: + """ + The ModuleConfig of the node + """ + + @property + def fixed_position(self) -> meshtastic.protobuf.mesh_pb2.Position: + """ + Fixed position data + """ + + def __init__( + self, + *, + long_name: builtins.str | None = ..., + short_name: builtins.str | None = ..., + channel_url: builtins.str | None = ..., + config: meshtastic.protobuf.localonly_pb2.LocalConfig | None = ..., + module_config: meshtastic.protobuf.localonly_pb2.LocalModuleConfig | None = ..., + fixed_position: meshtastic.protobuf.mesh_pb2.Position | None = ..., + ringtone: builtins.str | None = ..., + canned_messages: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["_canned_messages", b"_canned_messages", "_channel_url", b"_channel_url", "_config", b"_config", "_fixed_position", b"_fixed_position", "_long_name", b"_long_name", "_module_config", b"_module_config", "_ringtone", b"_ringtone", "_short_name", b"_short_name", "canned_messages", b"canned_messages", "channel_url", b"channel_url", "config", b"config", "fixed_position", b"fixed_position", "long_name", b"long_name", "module_config", b"module_config", "ringtone", b"ringtone", "short_name", b"short_name"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["_canned_messages", b"_canned_messages", "_channel_url", b"_channel_url", "_config", b"_config", "_fixed_position", b"_fixed_position", "_long_name", b"_long_name", "_module_config", b"_module_config", "_ringtone", b"_ringtone", "_short_name", b"_short_name", "canned_messages", b"canned_messages", "channel_url", b"channel_url", "config", b"config", "fixed_position", b"fixed_position", "long_name", b"long_name", "module_config", b"module_config", "ringtone", b"ringtone", "short_name", b"short_name"]) -> None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_canned_messages", b"_canned_messages"]) -> typing.Literal["canned_messages"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_channel_url", b"_channel_url"]) -> typing.Literal["channel_url"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_config", b"_config"]) -> typing.Literal["config"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_fixed_position", b"_fixed_position"]) -> typing.Literal["fixed_position"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_long_name", b"_long_name"]) -> typing.Literal["long_name"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_module_config", b"_module_config"]) -> typing.Literal["module_config"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_ringtone", b"_ringtone"]) -> typing.Literal["ringtone"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_short_name", b"_short_name"]) -> typing.Literal["short_name"] | None: ... + +global___DeviceProfile = DeviceProfile diff --git a/meshtastic/protobuf/config_pb2.py b/meshtastic/protobuf/config_pb2.py new file mode 100644 index 00000000..85efc94d --- /dev/null +++ b/meshtastic/protobuf/config_pb2.py @@ -0,0 +1,95 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: meshtastic/protobuf/config.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from meshtastic.protobuf import device_ui_pb2 as meshtastic_dot_protobuf_dot_device__ui__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n meshtastic/protobuf/config.proto\x12\x13meshtastic.protobuf\x1a#meshtastic/protobuf/device_ui.proto\"\xe5*\n\x06\x43onfig\x12:\n\x06\x64\x65vice\x18\x01 \x01(\x0b\x32(.meshtastic.protobuf.Config.DeviceConfigH\x00\x12>\n\x08position\x18\x02 \x01(\x0b\x32*.meshtastic.protobuf.Config.PositionConfigH\x00\x12\x38\n\x05power\x18\x03 \x01(\x0b\x32\'.meshtastic.protobuf.Config.PowerConfigH\x00\x12<\n\x07network\x18\x04 \x01(\x0b\x32).meshtastic.protobuf.Config.NetworkConfigH\x00\x12<\n\x07\x64isplay\x18\x05 \x01(\x0b\x32).meshtastic.protobuf.Config.DisplayConfigH\x00\x12\x36\n\x04lora\x18\x06 \x01(\x0b\x32&.meshtastic.protobuf.Config.LoRaConfigH\x00\x12@\n\tbluetooth\x18\x07 \x01(\x0b\x32+.meshtastic.protobuf.Config.BluetoothConfigH\x00\x12>\n\x08security\x18\x08 \x01(\x0b\x32*.meshtastic.protobuf.Config.SecurityConfigH\x00\x12\x42\n\nsessionkey\x18\t \x01(\x0b\x32,.meshtastic.protobuf.Config.SessionkeyConfigH\x00\x12\x38\n\tdevice_ui\x18\n \x01(\x0b\x32#.meshtastic.protobuf.DeviceUIConfigH\x00\x1a\x91\x07\n\x0c\x44\x65viceConfig\x12;\n\x04role\x18\x01 \x01(\x0e\x32-.meshtastic.protobuf.Config.DeviceConfig.Role\x12\x1a\n\x0eserial_enabled\x18\x02 \x01(\x08\x42\x02\x18\x01\x12\x13\n\x0b\x62utton_gpio\x18\x04 \x01(\r\x12\x13\n\x0b\x62uzzer_gpio\x18\x05 \x01(\r\x12R\n\x10rebroadcast_mode\x18\x06 \x01(\x0e\x32\x38.meshtastic.protobuf.Config.DeviceConfig.RebroadcastMode\x12 \n\x18node_info_broadcast_secs\x18\x07 \x01(\r\x12\"\n\x1a\x64ouble_tap_as_button_press\x18\x08 \x01(\x08\x12\x16\n\nis_managed\x18\t \x01(\x08\x42\x02\x18\x01\x12\x1c\n\x14\x64isable_triple_click\x18\n \x01(\x08\x12\r\n\x05tzdef\x18\x0b \x01(\t\x12\x1e\n\x16led_heartbeat_disabled\x18\x0c \x01(\x08\x12H\n\x0b\x62uzzer_mode\x18\r \x01(\x0e\x32\x33.meshtastic.protobuf.Config.DeviceConfig.BuzzerMode\"\xd4\x01\n\x04Role\x12\n\n\x06\x43LIENT\x10\x00\x12\x0f\n\x0b\x43LIENT_MUTE\x10\x01\x12\n\n\x06ROUTER\x10\x02\x12\x15\n\rROUTER_CLIENT\x10\x03\x1a\x02\x08\x01\x12\x10\n\x08REPEATER\x10\x04\x1a\x02\x08\x01\x12\x0b\n\x07TRACKER\x10\x05\x12\n\n\x06SENSOR\x10\x06\x12\x07\n\x03TAK\x10\x07\x12\x11\n\rCLIENT_HIDDEN\x10\x08\x12\x12\n\x0eLOST_AND_FOUND\x10\t\x12\x0f\n\x0bTAK_TRACKER\x10\n\x12\x0f\n\x0bROUTER_LATE\x10\x0b\x12\x0f\n\x0b\x43LIENT_BASE\x10\x0c\"s\n\x0fRebroadcastMode\x12\x07\n\x03\x41LL\x10\x00\x12\x15\n\x11\x41LL_SKIP_DECODING\x10\x01\x12\x0e\n\nLOCAL_ONLY\x10\x02\x12\x0e\n\nKNOWN_ONLY\x10\x03\x12\x08\n\x04NONE\x10\x04\x12\x16\n\x12\x43ORE_PORTNUMS_ONLY\x10\x05\"i\n\nBuzzerMode\x12\x0f\n\x0b\x41LL_ENABLED\x10\x00\x12\x0c\n\x08\x44ISABLED\x10\x01\x12\x16\n\x12NOTIFICATIONS_ONLY\x10\x02\x12\x0f\n\x0bSYSTEM_ONLY\x10\x03\x12\x13\n\x0f\x44IRECT_MSG_ONLY\x10\x04\x1a\x9a\x05\n\x0ePositionConfig\x12\x1f\n\x17position_broadcast_secs\x18\x01 \x01(\r\x12(\n position_broadcast_smart_enabled\x18\x02 \x01(\x08\x12\x16\n\x0e\x66ixed_position\x18\x03 \x01(\x08\x12\x17\n\x0bgps_enabled\x18\x04 \x01(\x08\x42\x02\x18\x01\x12\x1b\n\x13gps_update_interval\x18\x05 \x01(\r\x12\x1c\n\x10gps_attempt_time\x18\x06 \x01(\rB\x02\x18\x01\x12\x16\n\x0eposition_flags\x18\x07 \x01(\r\x12\x0f\n\x07rx_gpio\x18\x08 \x01(\r\x12\x0f\n\x07tx_gpio\x18\t \x01(\r\x12(\n broadcast_smart_minimum_distance\x18\n \x01(\r\x12-\n%broadcast_smart_minimum_interval_secs\x18\x0b \x01(\r\x12\x13\n\x0bgps_en_gpio\x18\x0c \x01(\r\x12\x44\n\x08gps_mode\x18\r \x01(\x0e\x32\x32.meshtastic.protobuf.Config.PositionConfig.GpsMode\"\xab\x01\n\rPositionFlags\x12\t\n\x05UNSET\x10\x00\x12\x0c\n\x08\x41LTITUDE\x10\x01\x12\x10\n\x0c\x41LTITUDE_MSL\x10\x02\x12\x16\n\x12GEOIDAL_SEPARATION\x10\x04\x12\x07\n\x03\x44OP\x10\x08\x12\t\n\x05HVDOP\x10\x10\x12\r\n\tSATINVIEW\x10 \x12\n\n\x06SEQ_NO\x10@\x12\x0e\n\tTIMESTAMP\x10\x80\x01\x12\x0c\n\x07HEADING\x10\x80\x02\x12\n\n\x05SPEED\x10\x80\x04\"5\n\x07GpsMode\x12\x0c\n\x08\x44ISABLED\x10\x00\x12\x0b\n\x07\x45NABLED\x10\x01\x12\x0f\n\x0bNOT_PRESENT\x10\x02\x1a\x84\x02\n\x0bPowerConfig\x12\x17\n\x0fis_power_saving\x18\x01 \x01(\x08\x12&\n\x1eon_battery_shutdown_after_secs\x18\x02 \x01(\r\x12\x1f\n\x17\x61\x64\x63_multiplier_override\x18\x03 \x01(\x02\x12\x1b\n\x13wait_bluetooth_secs\x18\x04 \x01(\r\x12\x10\n\x08sds_secs\x18\x06 \x01(\r\x12\x0f\n\x07ls_secs\x18\x07 \x01(\r\x12\x15\n\rmin_wake_secs\x18\x08 \x01(\r\x12\"\n\x1a\x64\x65vice_battery_ina_address\x18\t \x01(\r\x12\x18\n\x10powermon_enables\x18 \x01(\x04\x1a\xf7\x03\n\rNetworkConfig\x12\x14\n\x0cwifi_enabled\x18\x01 \x01(\x08\x12\x11\n\twifi_ssid\x18\x03 \x01(\t\x12\x10\n\x08wifi_psk\x18\x04 \x01(\t\x12\x12\n\nntp_server\x18\x05 \x01(\t\x12\x13\n\x0b\x65th_enabled\x18\x06 \x01(\x08\x12K\n\x0c\x61\x64\x64ress_mode\x18\x07 \x01(\x0e\x32\x35.meshtastic.protobuf.Config.NetworkConfig.AddressMode\x12I\n\x0bipv4_config\x18\x08 \x01(\x0b\x32\x34.meshtastic.protobuf.Config.NetworkConfig.IpV4Config\x12\x16\n\x0ersyslog_server\x18\t \x01(\t\x12\x19\n\x11\x65nabled_protocols\x18\n \x01(\r\x12\x14\n\x0cipv6_enabled\x18\x0b \x01(\x08\x1a\x46\n\nIpV4Config\x12\n\n\x02ip\x18\x01 \x01(\x07\x12\x0f\n\x07gateway\x18\x02 \x01(\x07\x12\x0e\n\x06subnet\x18\x03 \x01(\x07\x12\x0b\n\x03\x64ns\x18\x04 \x01(\x07\"#\n\x0b\x41\x64\x64ressMode\x12\x08\n\x04\x44HCP\x10\x00\x12\n\n\x06STATIC\x10\x01\"4\n\rProtocolFlags\x12\x10\n\x0cNO_BROADCAST\x10\x00\x12\x11\n\rUDP_BROADCAST\x10\x01\x1a\xb6\x08\n\rDisplayConfig\x12\x16\n\x0escreen_on_secs\x18\x01 \x01(\r\x12_\n\ngps_format\x18\x02 \x01(\x0e\x32G.meshtastic.protobuf.Config.DisplayConfig.DeprecatedGpsCoordinateFormatB\x02\x18\x01\x12!\n\x19\x61uto_screen_carousel_secs\x18\x03 \x01(\r\x12\x1d\n\x11\x63ompass_north_top\x18\x04 \x01(\x08\x42\x02\x18\x01\x12\x13\n\x0b\x66lip_screen\x18\x05 \x01(\x08\x12\x45\n\x05units\x18\x06 \x01(\x0e\x32\x36.meshtastic.protobuf.Config.DisplayConfig.DisplayUnits\x12@\n\x04oled\x18\x07 \x01(\x0e\x32\x32.meshtastic.protobuf.Config.DisplayConfig.OledType\x12J\n\x0b\x64isplaymode\x18\x08 \x01(\x0e\x32\x35.meshtastic.protobuf.Config.DisplayConfig.DisplayMode\x12\x14\n\x0cheading_bold\x18\t \x01(\x08\x12\x1d\n\x15wake_on_tap_or_motion\x18\n \x01(\x08\x12Y\n\x13\x63ompass_orientation\x18\x0b \x01(\x0e\x32<.meshtastic.protobuf.Config.DisplayConfig.CompassOrientation\x12\x15\n\ruse_12h_clock\x18\x0c \x01(\x08\x12\x1a\n\x12use_long_node_name\x18\r \x01(\x08\"+\n\x1d\x44\x65precatedGpsCoordinateFormat\x12\n\n\x06UNUSED\x10\x00\"(\n\x0c\x44isplayUnits\x12\n\n\x06METRIC\x10\x00\x12\x0c\n\x08IMPERIAL\x10\x01\"f\n\x08OledType\x12\r\n\tOLED_AUTO\x10\x00\x12\x10\n\x0cOLED_SSD1306\x10\x01\x12\x0f\n\x0bOLED_SH1106\x10\x02\x12\x0f\n\x0bOLED_SH1107\x10\x03\x12\x17\n\x13OLED_SH1107_128_128\x10\x04\"A\n\x0b\x44isplayMode\x12\x0b\n\x07\x44\x45\x46\x41ULT\x10\x00\x12\x0c\n\x08TWOCOLOR\x10\x01\x12\x0c\n\x08INVERTED\x10\x02\x12\t\n\x05\x43OLOR\x10\x03\"\xba\x01\n\x12\x43ompassOrientation\x12\r\n\tDEGREES_0\x10\x00\x12\x0e\n\nDEGREES_90\x10\x01\x12\x0f\n\x0b\x44\x45GREES_180\x10\x02\x12\x0f\n\x0b\x44\x45GREES_270\x10\x03\x12\x16\n\x12\x44\x45GREES_0_INVERTED\x10\x04\x12\x17\n\x13\x44\x45GREES_90_INVERTED\x10\x05\x12\x18\n\x14\x44\x45GREES_180_INVERTED\x10\x06\x12\x18\n\x14\x44\x45GREES_270_INVERTED\x10\x07\x1a\xec\x07\n\nLoRaConfig\x12\x12\n\nuse_preset\x18\x01 \x01(\x08\x12H\n\x0cmodem_preset\x18\x02 \x01(\x0e\x32\x32.meshtastic.protobuf.Config.LoRaConfig.ModemPreset\x12\x11\n\tbandwidth\x18\x03 \x01(\r\x12\x15\n\rspread_factor\x18\x04 \x01(\r\x12\x13\n\x0b\x63oding_rate\x18\x05 \x01(\r\x12\x18\n\x10\x66requency_offset\x18\x06 \x01(\x02\x12\x41\n\x06region\x18\x07 \x01(\x0e\x32\x31.meshtastic.protobuf.Config.LoRaConfig.RegionCode\x12\x11\n\thop_limit\x18\x08 \x01(\r\x12\x12\n\ntx_enabled\x18\t \x01(\x08\x12\x10\n\x08tx_power\x18\n \x01(\x05\x12\x13\n\x0b\x63hannel_num\x18\x0b \x01(\r\x12\x1b\n\x13override_duty_cycle\x18\x0c \x01(\x08\x12\x1e\n\x16sx126x_rx_boosted_gain\x18\r \x01(\x08\x12\x1a\n\x12override_frequency\x18\x0e \x01(\x02\x12\x17\n\x0fpa_fan_disabled\x18\x0f \x01(\x08\x12\x17\n\x0fignore_incoming\x18g \x03(\r\x12\x13\n\x0bignore_mqtt\x18h \x01(\x08\x12\x19\n\x11\x63onfig_ok_to_mqtt\x18i \x01(\x08\"\xae\x02\n\nRegionCode\x12\t\n\x05UNSET\x10\x00\x12\x06\n\x02US\x10\x01\x12\n\n\x06\x45U_433\x10\x02\x12\n\n\x06\x45U_868\x10\x03\x12\x06\n\x02\x43N\x10\x04\x12\x06\n\x02JP\x10\x05\x12\x07\n\x03\x41NZ\x10\x06\x12\x06\n\x02KR\x10\x07\x12\x06\n\x02TW\x10\x08\x12\x06\n\x02RU\x10\t\x12\x06\n\x02IN\x10\n\x12\n\n\x06NZ_865\x10\x0b\x12\x06\n\x02TH\x10\x0c\x12\x0b\n\x07LORA_24\x10\r\x12\n\n\x06UA_433\x10\x0e\x12\n\n\x06UA_868\x10\x0f\x12\n\n\x06MY_433\x10\x10\x12\n\n\x06MY_919\x10\x11\x12\n\n\x06SG_923\x10\x12\x12\n\n\x06PH_433\x10\x13\x12\n\n\x06PH_868\x10\x14\x12\n\n\x06PH_915\x10\x15\x12\x0b\n\x07\x41NZ_433\x10\x16\x12\n\n\x06KZ_433\x10\x17\x12\n\n\x06KZ_863\x10\x18\x12\n\n\x06NP_865\x10\x19\x12\n\n\x06\x42R_902\x10\x1a\"\xa9\x01\n\x0bModemPreset\x12\r\n\tLONG_FAST\x10\x00\x12\r\n\tLONG_SLOW\x10\x01\x12\x16\n\x0eVERY_LONG_SLOW\x10\x02\x1a\x02\x08\x01\x12\x0f\n\x0bMEDIUM_SLOW\x10\x03\x12\x0f\n\x0bMEDIUM_FAST\x10\x04\x12\x0e\n\nSHORT_SLOW\x10\x05\x12\x0e\n\nSHORT_FAST\x10\x06\x12\x11\n\rLONG_MODERATE\x10\x07\x12\x0f\n\x0bSHORT_TURBO\x10\x08\x1a\xb6\x01\n\x0f\x42luetoothConfig\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x45\n\x04mode\x18\x02 \x01(\x0e\x32\x37.meshtastic.protobuf.Config.BluetoothConfig.PairingMode\x12\x11\n\tfixed_pin\x18\x03 \x01(\r\"8\n\x0bPairingMode\x12\x0e\n\nRANDOM_PIN\x10\x00\x12\r\n\tFIXED_PIN\x10\x01\x12\n\n\x06NO_PIN\x10\x02\x1a\xb6\x01\n\x0eSecurityConfig\x12\x12\n\npublic_key\x18\x01 \x01(\x0c\x12\x13\n\x0bprivate_key\x18\x02 \x01(\x0c\x12\x11\n\tadmin_key\x18\x03 \x03(\x0c\x12\x12\n\nis_managed\x18\x04 \x01(\x08\x12\x16\n\x0eserial_enabled\x18\x05 \x01(\x08\x12\x1d\n\x15\x64\x65\x62ug_log_api_enabled\x18\x06 \x01(\x08\x12\x1d\n\x15\x61\x64min_channel_enabled\x18\x08 \x01(\x08\x1a\x12\n\x10SessionkeyConfigB\x11\n\x0fpayload_variantBb\n\x14org.meshtastic.protoB\x0c\x43onfigProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.config_pb2', _globals) +if _descriptor._USE_C_DESCRIPTORS == False: + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\014ConfigProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' + _CONFIG_DEVICECONFIG_ROLE.values_by_name["ROUTER_CLIENT"]._options = None + _CONFIG_DEVICECONFIG_ROLE.values_by_name["ROUTER_CLIENT"]._serialized_options = b'\010\001' + _CONFIG_DEVICECONFIG_ROLE.values_by_name["REPEATER"]._options = None + _CONFIG_DEVICECONFIG_ROLE.values_by_name["REPEATER"]._serialized_options = b'\010\001' + _CONFIG_DEVICECONFIG.fields_by_name['serial_enabled']._options = None + _CONFIG_DEVICECONFIG.fields_by_name['serial_enabled']._serialized_options = b'\030\001' + _CONFIG_DEVICECONFIG.fields_by_name['is_managed']._options = None + _CONFIG_DEVICECONFIG.fields_by_name['is_managed']._serialized_options = b'\030\001' + _CONFIG_POSITIONCONFIG.fields_by_name['gps_enabled']._options = None + _CONFIG_POSITIONCONFIG.fields_by_name['gps_enabled']._serialized_options = b'\030\001' + _CONFIG_POSITIONCONFIG.fields_by_name['gps_attempt_time']._options = None + _CONFIG_POSITIONCONFIG.fields_by_name['gps_attempt_time']._serialized_options = b'\030\001' + _CONFIG_DISPLAYCONFIG.fields_by_name['gps_format']._options = None + _CONFIG_DISPLAYCONFIG.fields_by_name['gps_format']._serialized_options = b'\030\001' + _CONFIG_DISPLAYCONFIG.fields_by_name['compass_north_top']._options = None + _CONFIG_DISPLAYCONFIG.fields_by_name['compass_north_top']._serialized_options = b'\030\001' + _CONFIG_LORACONFIG_MODEMPRESET.values_by_name["VERY_LONG_SLOW"]._options = None + _CONFIG_LORACONFIG_MODEMPRESET.values_by_name["VERY_LONG_SLOW"]._serialized_options = b'\010\001' + _globals['_CONFIG']._serialized_start=95 + _globals['_CONFIG']._serialized_end=5572 + _globals['_CONFIG_DEVICECONFIG']._serialized_start=724 + _globals['_CONFIG_DEVICECONFIG']._serialized_end=1637 + _globals['_CONFIG_DEVICECONFIG_ROLE']._serialized_start=1201 + _globals['_CONFIG_DEVICECONFIG_ROLE']._serialized_end=1413 + _globals['_CONFIG_DEVICECONFIG_REBROADCASTMODE']._serialized_start=1415 + _globals['_CONFIG_DEVICECONFIG_REBROADCASTMODE']._serialized_end=1530 + _globals['_CONFIG_DEVICECONFIG_BUZZERMODE']._serialized_start=1532 + _globals['_CONFIG_DEVICECONFIG_BUZZERMODE']._serialized_end=1637 + _globals['_CONFIG_POSITIONCONFIG']._serialized_start=1640 + _globals['_CONFIG_POSITIONCONFIG']._serialized_end=2306 + _globals['_CONFIG_POSITIONCONFIG_POSITIONFLAGS']._serialized_start=2080 + _globals['_CONFIG_POSITIONCONFIG_POSITIONFLAGS']._serialized_end=2251 + _globals['_CONFIG_POSITIONCONFIG_GPSMODE']._serialized_start=2253 + _globals['_CONFIG_POSITIONCONFIG_GPSMODE']._serialized_end=2306 + _globals['_CONFIG_POWERCONFIG']._serialized_start=2309 + _globals['_CONFIG_POWERCONFIG']._serialized_end=2569 + _globals['_CONFIG_NETWORKCONFIG']._serialized_start=2572 + _globals['_CONFIG_NETWORKCONFIG']._serialized_end=3075 + _globals['_CONFIG_NETWORKCONFIG_IPV4CONFIG']._serialized_start=2914 + _globals['_CONFIG_NETWORKCONFIG_IPV4CONFIG']._serialized_end=2984 + _globals['_CONFIG_NETWORKCONFIG_ADDRESSMODE']._serialized_start=2986 + _globals['_CONFIG_NETWORKCONFIG_ADDRESSMODE']._serialized_end=3021 + _globals['_CONFIG_NETWORKCONFIG_PROTOCOLFLAGS']._serialized_start=3023 + _globals['_CONFIG_NETWORKCONFIG_PROTOCOLFLAGS']._serialized_end=3075 + _globals['_CONFIG_DISPLAYCONFIG']._serialized_start=3078 + _globals['_CONFIG_DISPLAYCONFIG']._serialized_end=4156 + _globals['_CONFIG_DISPLAYCONFIG_DEPRECATEDGPSCOORDINATEFORMAT']._serialized_start=3711 + _globals['_CONFIG_DISPLAYCONFIG_DEPRECATEDGPSCOORDINATEFORMAT']._serialized_end=3754 + _globals['_CONFIG_DISPLAYCONFIG_DISPLAYUNITS']._serialized_start=3756 + _globals['_CONFIG_DISPLAYCONFIG_DISPLAYUNITS']._serialized_end=3796 + _globals['_CONFIG_DISPLAYCONFIG_OLEDTYPE']._serialized_start=3798 + _globals['_CONFIG_DISPLAYCONFIG_OLEDTYPE']._serialized_end=3900 + _globals['_CONFIG_DISPLAYCONFIG_DISPLAYMODE']._serialized_start=3902 + _globals['_CONFIG_DISPLAYCONFIG_DISPLAYMODE']._serialized_end=3967 + _globals['_CONFIG_DISPLAYCONFIG_COMPASSORIENTATION']._serialized_start=3970 + _globals['_CONFIG_DISPLAYCONFIG_COMPASSORIENTATION']._serialized_end=4156 + _globals['_CONFIG_LORACONFIG']._serialized_start=4159 + _globals['_CONFIG_LORACONFIG']._serialized_end=5163 + _globals['_CONFIG_LORACONFIG_REGIONCODE']._serialized_start=4689 + _globals['_CONFIG_LORACONFIG_REGIONCODE']._serialized_end=4991 + _globals['_CONFIG_LORACONFIG_MODEMPRESET']._serialized_start=4994 + _globals['_CONFIG_LORACONFIG_MODEMPRESET']._serialized_end=5163 + _globals['_CONFIG_BLUETOOTHCONFIG']._serialized_start=5166 + _globals['_CONFIG_BLUETOOTHCONFIG']._serialized_end=5348 + _globals['_CONFIG_BLUETOOTHCONFIG_PAIRINGMODE']._serialized_start=5292 + _globals['_CONFIG_BLUETOOTHCONFIG_PAIRINGMODE']._serialized_end=5348 + _globals['_CONFIG_SECURITYCONFIG']._serialized_start=5351 + _globals['_CONFIG_SECURITYCONFIG']._serialized_end=5533 + _globals['_CONFIG_SESSIONKEYCONFIG']._serialized_start=5535 + _globals['_CONFIG_SESSIONKEYCONFIG']._serialized_end=5553 +# @@protoc_insertion_point(module_scope) diff --git a/meshtastic/protobuf/config_pb2.pyi b/meshtastic/protobuf/config_pb2.pyi new file mode 100644 index 00000000..e504e2fd --- /dev/null +++ b/meshtastic/protobuf/config_pb2.pyi @@ -0,0 +1,1895 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" + +import builtins +import collections.abc +import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.internal.enum_type_wrapper +import google.protobuf.message +import meshtastic.protobuf.device_ui_pb2 +import sys +import typing + +if sys.version_info >= (3, 10): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing.final +class Config(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + @typing.final + class DeviceConfig(google.protobuf.message.Message): + """ + Configuration + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + class _Role: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + + class _RoleEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[Config.DeviceConfig._Role.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + CLIENT: Config.DeviceConfig._Role.ValueType # 0 + """ + Description: App connected or stand alone messaging device. + Technical Details: Default Role + """ + CLIENT_MUTE: Config.DeviceConfig._Role.ValueType # 1 + """ + Description: Device that does not forward packets from other devices. + """ + ROUTER: Config.DeviceConfig._Role.ValueType # 2 + """ + Description: Infrastructure node for extending network coverage by relaying messages. Visible in Nodes list. + Technical Details: Mesh packets will prefer to be routed over this node. This node will not be used by client apps. + The wifi radio and the oled screen will be put to sleep. + This mode may still potentially have higher power usage due to it's preference in message rebroadcasting on the mesh. + """ + ROUTER_CLIENT: Config.DeviceConfig._Role.ValueType # 3 + """ + Description: Combination of both ROUTER and CLIENT. Not for mobile devices. + Deprecated in v2.3.15 because improper usage is impacting public meshes: Use ROUTER or CLIENT instead. + """ + REPEATER: Config.DeviceConfig._Role.ValueType # 4 + """ + Description: Infrastructure node for extending network coverage by relaying messages with minimal overhead. Not visible in Nodes list. + Technical Details: Mesh packets will simply be rebroadcasted over this node. Nodes configured with this role will not originate NodeInfo, Position, Telemetry + or any other packet type. They will simply rebroadcast any mesh packets on the same frequency, channel num, spread factor, and coding rate. + Deprecated in v2.7.11 because it creates "holes" in the mesh rebroadcast chain. + """ + TRACKER: Config.DeviceConfig._Role.ValueType # 5 + """ + Description: Broadcasts GPS position packets as priority. + Technical Details: Position Mesh packets will be prioritized higher and sent more frequently by default. + When used in conjunction with power.is_power_saving = true, nodes will wake up, + send position, and then sleep for position.position_broadcast_secs seconds. + """ + SENSOR: Config.DeviceConfig._Role.ValueType # 6 + """ + Description: Broadcasts telemetry packets as priority. + Technical Details: Telemetry Mesh packets will be prioritized higher and sent more frequently by default. + When used in conjunction with power.is_power_saving = true, nodes will wake up, + send environment telemetry, and then sleep for telemetry.environment_update_interval seconds. + """ + TAK: Config.DeviceConfig._Role.ValueType # 7 + """ + Description: Optimized for ATAK system communication and reduces routine broadcasts. + Technical Details: Used for nodes dedicated for connection to an ATAK EUD. + Turns off many of the routine broadcasts to favor CoT packet stream + from the Meshtastic ATAK plugin -> IMeshService -> Node + """ + CLIENT_HIDDEN: Config.DeviceConfig._Role.ValueType # 8 + """ + Description: Device that only broadcasts as needed for stealth or power savings. + Technical Details: Used for nodes that "only speak when spoken to" + Turns all of the routine broadcasts but allows for ad-hoc communication + Still rebroadcasts, but with local only rebroadcast mode (known meshes only) + Can be used for clandestine operation or to dramatically reduce airtime / power consumption + """ + LOST_AND_FOUND: Config.DeviceConfig._Role.ValueType # 9 + """ + Description: Broadcasts location as message to default channel regularly for to assist with device recovery. + Technical Details: Used to automatically send a text message to the mesh + with the current position of the device on a frequent interval: + "I'm lost! Position: lat / long" + """ + TAK_TRACKER: Config.DeviceConfig._Role.ValueType # 10 + """ + Description: Enables automatic TAK PLI broadcasts and reduces routine broadcasts. + Technical Details: Turns off many of the routine broadcasts to favor ATAK CoT packet stream + and automatic TAK PLI (position location information) broadcasts. + Uses position module configuration to determine TAK PLI broadcast interval. + """ + ROUTER_LATE: Config.DeviceConfig._Role.ValueType # 11 + """ + Description: Will always rebroadcast packets, but will do so after all other modes. + Technical Details: Used for router nodes that are intended to provide additional coverage + in areas not already covered by other routers, or to bridge around problematic terrain, + but should not be given priority over other routers in order to avoid unnecessaraily + consuming hops. + """ + CLIENT_BASE: Config.DeviceConfig._Role.ValueType # 12 + """ + Description: Treats packets from or to favorited nodes as ROUTER, and all other packets as CLIENT. + Technical Details: Used for stronger attic/roof nodes to distribute messages more widely + from weaker, indoor, or less-well-positioned nodes. Recommended for users with multiple nodes + where one CLIENT_BASE acts as a more powerful base station, such as an attic/roof node. + """ + + class Role(_Role, metaclass=_RoleEnumTypeWrapper): + """ + Defines the device's role on the Mesh network + """ + + CLIENT: Config.DeviceConfig.Role.ValueType # 0 + """ + Description: App connected or stand alone messaging device. + Technical Details: Default Role + """ + CLIENT_MUTE: Config.DeviceConfig.Role.ValueType # 1 + """ + Description: Device that does not forward packets from other devices. + """ + ROUTER: Config.DeviceConfig.Role.ValueType # 2 + """ + Description: Infrastructure node for extending network coverage by relaying messages. Visible in Nodes list. + Technical Details: Mesh packets will prefer to be routed over this node. This node will not be used by client apps. + The wifi radio and the oled screen will be put to sleep. + This mode may still potentially have higher power usage due to it's preference in message rebroadcasting on the mesh. + """ + ROUTER_CLIENT: Config.DeviceConfig.Role.ValueType # 3 + """ + Description: Combination of both ROUTER and CLIENT. Not for mobile devices. + Deprecated in v2.3.15 because improper usage is impacting public meshes: Use ROUTER or CLIENT instead. + """ + REPEATER: Config.DeviceConfig.Role.ValueType # 4 + """ + Description: Infrastructure node for extending network coverage by relaying messages with minimal overhead. Not visible in Nodes list. + Technical Details: Mesh packets will simply be rebroadcasted over this node. Nodes configured with this role will not originate NodeInfo, Position, Telemetry + or any other packet type. They will simply rebroadcast any mesh packets on the same frequency, channel num, spread factor, and coding rate. + Deprecated in v2.7.11 because it creates "holes" in the mesh rebroadcast chain. + """ + TRACKER: Config.DeviceConfig.Role.ValueType # 5 + """ + Description: Broadcasts GPS position packets as priority. + Technical Details: Position Mesh packets will be prioritized higher and sent more frequently by default. + When used in conjunction with power.is_power_saving = true, nodes will wake up, + send position, and then sleep for position.position_broadcast_secs seconds. + """ + SENSOR: Config.DeviceConfig.Role.ValueType # 6 + """ + Description: Broadcasts telemetry packets as priority. + Technical Details: Telemetry Mesh packets will be prioritized higher and sent more frequently by default. + When used in conjunction with power.is_power_saving = true, nodes will wake up, + send environment telemetry, and then sleep for telemetry.environment_update_interval seconds. + """ + TAK: Config.DeviceConfig.Role.ValueType # 7 + """ + Description: Optimized for ATAK system communication and reduces routine broadcasts. + Technical Details: Used for nodes dedicated for connection to an ATAK EUD. + Turns off many of the routine broadcasts to favor CoT packet stream + from the Meshtastic ATAK plugin -> IMeshService -> Node + """ + CLIENT_HIDDEN: Config.DeviceConfig.Role.ValueType # 8 + """ + Description: Device that only broadcasts as needed for stealth or power savings. + Technical Details: Used for nodes that "only speak when spoken to" + Turns all of the routine broadcasts but allows for ad-hoc communication + Still rebroadcasts, but with local only rebroadcast mode (known meshes only) + Can be used for clandestine operation or to dramatically reduce airtime / power consumption + """ + LOST_AND_FOUND: Config.DeviceConfig.Role.ValueType # 9 + """ + Description: Broadcasts location as message to default channel regularly for to assist with device recovery. + Technical Details: Used to automatically send a text message to the mesh + with the current position of the device on a frequent interval: + "I'm lost! Position: lat / long" + """ + TAK_TRACKER: Config.DeviceConfig.Role.ValueType # 10 + """ + Description: Enables automatic TAK PLI broadcasts and reduces routine broadcasts. + Technical Details: Turns off many of the routine broadcasts to favor ATAK CoT packet stream + and automatic TAK PLI (position location information) broadcasts. + Uses position module configuration to determine TAK PLI broadcast interval. + """ + ROUTER_LATE: Config.DeviceConfig.Role.ValueType # 11 + """ + Description: Will always rebroadcast packets, but will do so after all other modes. + Technical Details: Used for router nodes that are intended to provide additional coverage + in areas not already covered by other routers, or to bridge around problematic terrain, + but should not be given priority over other routers in order to avoid unnecessaraily + consuming hops. + """ + CLIENT_BASE: Config.DeviceConfig.Role.ValueType # 12 + """ + Description: Treats packets from or to favorited nodes as ROUTER, and all other packets as CLIENT. + Technical Details: Used for stronger attic/roof nodes to distribute messages more widely + from weaker, indoor, or less-well-positioned nodes. Recommended for users with multiple nodes + where one CLIENT_BASE acts as a more powerful base station, such as an attic/roof node. + """ + + class _RebroadcastMode: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + + class _RebroadcastModeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[Config.DeviceConfig._RebroadcastMode.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + ALL: Config.DeviceConfig._RebroadcastMode.ValueType # 0 + """ + Default behavior. + Rebroadcast any observed message, if it was on our private channel or from another mesh with the same lora params. + """ + ALL_SKIP_DECODING: Config.DeviceConfig._RebroadcastMode.ValueType # 1 + """ + Same as behavior as ALL but skips packet decoding and simply rebroadcasts them. + Only available in Repeater role. Setting this on any other roles will result in ALL behavior. + """ + LOCAL_ONLY: Config.DeviceConfig._RebroadcastMode.ValueType # 2 + """ + Ignores observed messages from foreign meshes that are open or those which it cannot decrypt. + Only rebroadcasts message on the nodes local primary / secondary channels. + """ + KNOWN_ONLY: Config.DeviceConfig._RebroadcastMode.ValueType # 3 + """ + Ignores observed messages from foreign meshes like LOCAL_ONLY, + but takes it step further by also ignoring messages from nodenums not in the node's known list (NodeDB) + """ + NONE: Config.DeviceConfig._RebroadcastMode.ValueType # 4 + """ + Only permitted for SENSOR, TRACKER and TAK_TRACKER roles, this will inhibit all rebroadcasts, not unlike CLIENT_MUTE role. + """ + CORE_PORTNUMS_ONLY: Config.DeviceConfig._RebroadcastMode.ValueType # 5 + """ + Ignores packets from non-standard portnums such as: TAK, RangeTest, PaxCounter, etc. + Only rebroadcasts packets with standard portnums: NodeInfo, Text, Position, Telemetry, and Routing. + """ + + class RebroadcastMode(_RebroadcastMode, metaclass=_RebroadcastModeEnumTypeWrapper): + """ + Defines the device's behavior for how messages are rebroadcast + """ + + ALL: Config.DeviceConfig.RebroadcastMode.ValueType # 0 + """ + Default behavior. + Rebroadcast any observed message, if it was on our private channel or from another mesh with the same lora params. + """ + ALL_SKIP_DECODING: Config.DeviceConfig.RebroadcastMode.ValueType # 1 + """ + Same as behavior as ALL but skips packet decoding and simply rebroadcasts them. + Only available in Repeater role. Setting this on any other roles will result in ALL behavior. + """ + LOCAL_ONLY: Config.DeviceConfig.RebroadcastMode.ValueType # 2 + """ + Ignores observed messages from foreign meshes that are open or those which it cannot decrypt. + Only rebroadcasts message on the nodes local primary / secondary channels. + """ + KNOWN_ONLY: Config.DeviceConfig.RebroadcastMode.ValueType # 3 + """ + Ignores observed messages from foreign meshes like LOCAL_ONLY, + but takes it step further by also ignoring messages from nodenums not in the node's known list (NodeDB) + """ + NONE: Config.DeviceConfig.RebroadcastMode.ValueType # 4 + """ + Only permitted for SENSOR, TRACKER and TAK_TRACKER roles, this will inhibit all rebroadcasts, not unlike CLIENT_MUTE role. + """ + CORE_PORTNUMS_ONLY: Config.DeviceConfig.RebroadcastMode.ValueType # 5 + """ + Ignores packets from non-standard portnums such as: TAK, RangeTest, PaxCounter, etc. + Only rebroadcasts packets with standard portnums: NodeInfo, Text, Position, Telemetry, and Routing. + """ + + class _BuzzerMode: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + + class _BuzzerModeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[Config.DeviceConfig._BuzzerMode.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + ALL_ENABLED: Config.DeviceConfig._BuzzerMode.ValueType # 0 + """ + Default behavior. + Buzzer is enabled for all audio feedback including button presses and alerts. + """ + DISABLED: Config.DeviceConfig._BuzzerMode.ValueType # 1 + """ + Disabled. + All buzzer audio feedback is disabled. + """ + NOTIFICATIONS_ONLY: Config.DeviceConfig._BuzzerMode.ValueType # 2 + """ + Notifications Only. + Buzzer is enabled only for notifications and alerts, but not for button presses. + External notification config determines the specifics of the notification behavior. + """ + SYSTEM_ONLY: Config.DeviceConfig._BuzzerMode.ValueType # 3 + """ + Non-notification system buzzer tones only. + Buzzer is enabled only for non-notification tones such as button presses, startup, shutdown, but not for alerts. + """ + DIRECT_MSG_ONLY: Config.DeviceConfig._BuzzerMode.ValueType # 4 + """ + Direct Message notifications only. + Buzzer is enabled only for direct messages and alerts, but not for button presses. + External notification config determines the specifics of the notification behavior. + """ + + class BuzzerMode(_BuzzerMode, metaclass=_BuzzerModeEnumTypeWrapper): + """ + Defines buzzer behavior for audio feedback + """ + + ALL_ENABLED: Config.DeviceConfig.BuzzerMode.ValueType # 0 + """ + Default behavior. + Buzzer is enabled for all audio feedback including button presses and alerts. + """ + DISABLED: Config.DeviceConfig.BuzzerMode.ValueType # 1 + """ + Disabled. + All buzzer audio feedback is disabled. + """ + NOTIFICATIONS_ONLY: Config.DeviceConfig.BuzzerMode.ValueType # 2 + """ + Notifications Only. + Buzzer is enabled only for notifications and alerts, but not for button presses. + External notification config determines the specifics of the notification behavior. + """ + SYSTEM_ONLY: Config.DeviceConfig.BuzzerMode.ValueType # 3 + """ + Non-notification system buzzer tones only. + Buzzer is enabled only for non-notification tones such as button presses, startup, shutdown, but not for alerts. + """ + DIRECT_MSG_ONLY: Config.DeviceConfig.BuzzerMode.ValueType # 4 + """ + Direct Message notifications only. + Buzzer is enabled only for direct messages and alerts, but not for button presses. + External notification config determines the specifics of the notification behavior. + """ + + ROLE_FIELD_NUMBER: builtins.int + SERIAL_ENABLED_FIELD_NUMBER: builtins.int + BUTTON_GPIO_FIELD_NUMBER: builtins.int + BUZZER_GPIO_FIELD_NUMBER: builtins.int + REBROADCAST_MODE_FIELD_NUMBER: builtins.int + NODE_INFO_BROADCAST_SECS_FIELD_NUMBER: builtins.int + DOUBLE_TAP_AS_BUTTON_PRESS_FIELD_NUMBER: builtins.int + IS_MANAGED_FIELD_NUMBER: builtins.int + DISABLE_TRIPLE_CLICK_FIELD_NUMBER: builtins.int + TZDEF_FIELD_NUMBER: builtins.int + LED_HEARTBEAT_DISABLED_FIELD_NUMBER: builtins.int + BUZZER_MODE_FIELD_NUMBER: builtins.int + role: global___Config.DeviceConfig.Role.ValueType + """ + Sets the role of node + """ + serial_enabled: builtins.bool + """ + Disabling this will disable the SerialConsole by not initilizing the StreamAPI + Moved to SecurityConfig + """ + button_gpio: builtins.int + """ + For boards without a hard wired button, this is the pin number that will be used + Boards that have more than one button can swap the function with this one. defaults to BUTTON_PIN if defined. + """ + buzzer_gpio: builtins.int + """ + For boards without a PWM buzzer, this is the pin number that will be used + Defaults to PIN_BUZZER if defined. + """ + rebroadcast_mode: global___Config.DeviceConfig.RebroadcastMode.ValueType + """ + Sets the role of node + """ + node_info_broadcast_secs: builtins.int + """ + Send our nodeinfo this often + Defaults to 900 Seconds (15 minutes) + """ + double_tap_as_button_press: builtins.bool + """ + Treat double tap interrupt on supported accelerometers as a button press if set to true + """ + is_managed: builtins.bool + """ + If true, device is considered to be "managed" by a mesh administrator + Clients should then limit available configuration and administrative options inside the user interface + Moved to SecurityConfig + """ + disable_triple_click: builtins.bool + """ + Disables the triple-press of user button to enable or disable GPS + """ + tzdef: builtins.str + """ + POSIX Timezone definition string from https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv. + """ + led_heartbeat_disabled: builtins.bool + """ + If true, disable the default blinking LED (LED_PIN) behavior on the device + """ + buzzer_mode: global___Config.DeviceConfig.BuzzerMode.ValueType + """ + Controls buzzer behavior for audio feedback + Defaults to ENABLED + """ + def __init__( + self, + *, + role: global___Config.DeviceConfig.Role.ValueType = ..., + serial_enabled: builtins.bool = ..., + button_gpio: builtins.int = ..., + buzzer_gpio: builtins.int = ..., + rebroadcast_mode: global___Config.DeviceConfig.RebroadcastMode.ValueType = ..., + node_info_broadcast_secs: builtins.int = ..., + double_tap_as_button_press: builtins.bool = ..., + is_managed: builtins.bool = ..., + disable_triple_click: builtins.bool = ..., + tzdef: builtins.str = ..., + led_heartbeat_disabled: builtins.bool = ..., + buzzer_mode: global___Config.DeviceConfig.BuzzerMode.ValueType = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["button_gpio", b"button_gpio", "buzzer_gpio", b"buzzer_gpio", "buzzer_mode", b"buzzer_mode", "disable_triple_click", b"disable_triple_click", "double_tap_as_button_press", b"double_tap_as_button_press", "is_managed", b"is_managed", "led_heartbeat_disabled", b"led_heartbeat_disabled", "node_info_broadcast_secs", b"node_info_broadcast_secs", "rebroadcast_mode", b"rebroadcast_mode", "role", b"role", "serial_enabled", b"serial_enabled", "tzdef", b"tzdef"]) -> None: ... + + @typing.final + class PositionConfig(google.protobuf.message.Message): + """ + Position Config + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + class _PositionFlags: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + + class _PositionFlagsEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[Config.PositionConfig._PositionFlags.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + UNSET: Config.PositionConfig._PositionFlags.ValueType # 0 + """ + Required for compilation + """ + ALTITUDE: Config.PositionConfig._PositionFlags.ValueType # 1 + """ + Include an altitude value (if available) + """ + ALTITUDE_MSL: Config.PositionConfig._PositionFlags.ValueType # 2 + """ + Altitude value is MSL + """ + GEOIDAL_SEPARATION: Config.PositionConfig._PositionFlags.ValueType # 4 + """ + Include geoidal separation + """ + DOP: Config.PositionConfig._PositionFlags.ValueType # 8 + """ + Include the DOP value ; PDOP used by default, see below + """ + HVDOP: Config.PositionConfig._PositionFlags.ValueType # 16 + """ + If POS_DOP set, send separate HDOP / VDOP values instead of PDOP + """ + SATINVIEW: Config.PositionConfig._PositionFlags.ValueType # 32 + """ + Include number of "satellites in view" + """ + SEQ_NO: Config.PositionConfig._PositionFlags.ValueType # 64 + """ + Include a sequence number incremented per packet + """ + TIMESTAMP: Config.PositionConfig._PositionFlags.ValueType # 128 + """ + Include positional timestamp (from GPS solution) + """ + HEADING: Config.PositionConfig._PositionFlags.ValueType # 256 + """ + Include positional heading + Intended for use with vehicle not walking speeds + walking speeds are likely to be error prone like the compass + """ + SPEED: Config.PositionConfig._PositionFlags.ValueType # 512 + """ + Include positional speed + Intended for use with vehicle not walking speeds + walking speeds are likely to be error prone like the compass + """ + + class PositionFlags(_PositionFlags, metaclass=_PositionFlagsEnumTypeWrapper): + """ + Bit field of boolean configuration options, indicating which optional + fields to include when assembling POSITION messages. + Longitude, latitude, altitude, speed, heading, and DOP + are always included (also time if GPS-synced) + NOTE: the more fields are included, the larger the message will be - + leading to longer airtime and a higher risk of packet loss + """ + + UNSET: Config.PositionConfig.PositionFlags.ValueType # 0 + """ + Required for compilation + """ + ALTITUDE: Config.PositionConfig.PositionFlags.ValueType # 1 + """ + Include an altitude value (if available) + """ + ALTITUDE_MSL: Config.PositionConfig.PositionFlags.ValueType # 2 + """ + Altitude value is MSL + """ + GEOIDAL_SEPARATION: Config.PositionConfig.PositionFlags.ValueType # 4 + """ + Include geoidal separation + """ + DOP: Config.PositionConfig.PositionFlags.ValueType # 8 + """ + Include the DOP value ; PDOP used by default, see below + """ + HVDOP: Config.PositionConfig.PositionFlags.ValueType # 16 + """ + If POS_DOP set, send separate HDOP / VDOP values instead of PDOP + """ + SATINVIEW: Config.PositionConfig.PositionFlags.ValueType # 32 + """ + Include number of "satellites in view" + """ + SEQ_NO: Config.PositionConfig.PositionFlags.ValueType # 64 + """ + Include a sequence number incremented per packet + """ + TIMESTAMP: Config.PositionConfig.PositionFlags.ValueType # 128 + """ + Include positional timestamp (from GPS solution) + """ + HEADING: Config.PositionConfig.PositionFlags.ValueType # 256 + """ + Include positional heading + Intended for use with vehicle not walking speeds + walking speeds are likely to be error prone like the compass + """ + SPEED: Config.PositionConfig.PositionFlags.ValueType # 512 + """ + Include positional speed + Intended for use with vehicle not walking speeds + walking speeds are likely to be error prone like the compass + """ + + class _GpsMode: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + + class _GpsModeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[Config.PositionConfig._GpsMode.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + DISABLED: Config.PositionConfig._GpsMode.ValueType # 0 + """ + GPS is present but disabled + """ + ENABLED: Config.PositionConfig._GpsMode.ValueType # 1 + """ + GPS is present and enabled + """ + NOT_PRESENT: Config.PositionConfig._GpsMode.ValueType # 2 + """ + GPS is not present on the device + """ + + class GpsMode(_GpsMode, metaclass=_GpsModeEnumTypeWrapper): ... + DISABLED: Config.PositionConfig.GpsMode.ValueType # 0 + """ + GPS is present but disabled + """ + ENABLED: Config.PositionConfig.GpsMode.ValueType # 1 + """ + GPS is present and enabled + """ + NOT_PRESENT: Config.PositionConfig.GpsMode.ValueType # 2 + """ + GPS is not present on the device + """ + + POSITION_BROADCAST_SECS_FIELD_NUMBER: builtins.int + POSITION_BROADCAST_SMART_ENABLED_FIELD_NUMBER: builtins.int + FIXED_POSITION_FIELD_NUMBER: builtins.int + GPS_ENABLED_FIELD_NUMBER: builtins.int + GPS_UPDATE_INTERVAL_FIELD_NUMBER: builtins.int + GPS_ATTEMPT_TIME_FIELD_NUMBER: builtins.int + POSITION_FLAGS_FIELD_NUMBER: builtins.int + RX_GPIO_FIELD_NUMBER: builtins.int + TX_GPIO_FIELD_NUMBER: builtins.int + BROADCAST_SMART_MINIMUM_DISTANCE_FIELD_NUMBER: builtins.int + BROADCAST_SMART_MINIMUM_INTERVAL_SECS_FIELD_NUMBER: builtins.int + GPS_EN_GPIO_FIELD_NUMBER: builtins.int + GPS_MODE_FIELD_NUMBER: builtins.int + position_broadcast_secs: builtins.int + """ + We should send our position this often (but only if it has changed significantly) + Defaults to 15 minutes + """ + position_broadcast_smart_enabled: builtins.bool + """ + Adaptive position braoadcast, which is now the default. + """ + fixed_position: builtins.bool + """ + If set, this node is at a fixed position. + We will generate GPS position updates at the regular interval, but use whatever the last lat/lon/alt we have for the node. + The lat/lon/alt can be set by an internal GPS or with the help of the app. + """ + gps_enabled: builtins.bool + """ + Is GPS enabled for this node? + """ + gps_update_interval: builtins.int + """ + How often should we try to get GPS position (in seconds) + or zero for the default of once every 30 seconds + or a very large value (maxint) to update only once at boot. + """ + gps_attempt_time: builtins.int + """ + Deprecated in favor of using smart / regular broadcast intervals as implicit attempt time + """ + position_flags: builtins.int + """ + Bit field of boolean configuration options for POSITION messages + (bitwise OR of PositionFlags) + """ + rx_gpio: builtins.int + """ + (Re)define GPS_RX_PIN for your board. + """ + tx_gpio: builtins.int + """ + (Re)define GPS_TX_PIN for your board. + """ + broadcast_smart_minimum_distance: builtins.int + """ + The minimum distance in meters traveled (since the last send) before we can send a position to the mesh if position_broadcast_smart_enabled + """ + broadcast_smart_minimum_interval_secs: builtins.int + """ + The minimum number of seconds (since the last send) before we can send a position to the mesh if position_broadcast_smart_enabled + """ + gps_en_gpio: builtins.int + """ + (Re)define PIN_GPS_EN for your board. + """ + gps_mode: global___Config.PositionConfig.GpsMode.ValueType + """ + Set where GPS is enabled, disabled, or not present + """ + def __init__( + self, + *, + position_broadcast_secs: builtins.int = ..., + position_broadcast_smart_enabled: builtins.bool = ..., + fixed_position: builtins.bool = ..., + gps_enabled: builtins.bool = ..., + gps_update_interval: builtins.int = ..., + gps_attempt_time: builtins.int = ..., + position_flags: builtins.int = ..., + rx_gpio: builtins.int = ..., + tx_gpio: builtins.int = ..., + broadcast_smart_minimum_distance: builtins.int = ..., + broadcast_smart_minimum_interval_secs: builtins.int = ..., + gps_en_gpio: builtins.int = ..., + gps_mode: global___Config.PositionConfig.GpsMode.ValueType = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["broadcast_smart_minimum_distance", b"broadcast_smart_minimum_distance", "broadcast_smart_minimum_interval_secs", b"broadcast_smart_minimum_interval_secs", "fixed_position", b"fixed_position", "gps_attempt_time", b"gps_attempt_time", "gps_en_gpio", b"gps_en_gpio", "gps_enabled", b"gps_enabled", "gps_mode", b"gps_mode", "gps_update_interval", b"gps_update_interval", "position_broadcast_secs", b"position_broadcast_secs", "position_broadcast_smart_enabled", b"position_broadcast_smart_enabled", "position_flags", b"position_flags", "rx_gpio", b"rx_gpio", "tx_gpio", b"tx_gpio"]) -> None: ... + + @typing.final + class PowerConfig(google.protobuf.message.Message): + """ + Power Config\\ + See [Power Config](/docs/settings/config/power) for additional power config details. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + IS_POWER_SAVING_FIELD_NUMBER: builtins.int + ON_BATTERY_SHUTDOWN_AFTER_SECS_FIELD_NUMBER: builtins.int + ADC_MULTIPLIER_OVERRIDE_FIELD_NUMBER: builtins.int + WAIT_BLUETOOTH_SECS_FIELD_NUMBER: builtins.int + SDS_SECS_FIELD_NUMBER: builtins.int + LS_SECS_FIELD_NUMBER: builtins.int + MIN_WAKE_SECS_FIELD_NUMBER: builtins.int + DEVICE_BATTERY_INA_ADDRESS_FIELD_NUMBER: builtins.int + POWERMON_ENABLES_FIELD_NUMBER: builtins.int + is_power_saving: builtins.bool + """ + Description: Will sleep everything as much as possible, for the tracker and sensor role this will also include the lora radio. + Don't use this setting if you want to use your device with the phone apps or are using a device without a user button. + Technical Details: Works for ESP32 devices and NRF52 devices in the Sensor or Tracker roles + """ + on_battery_shutdown_after_secs: builtins.int + """ + Description: If non-zero, the device will fully power off this many seconds after external power is removed. + """ + adc_multiplier_override: builtins.float + """ + Ratio of voltage divider for battery pin eg. 3.20 (R1=100k, R2=220k) + Overrides the ADC_MULTIPLIER defined in variant for battery voltage calculation. + https://meshtastic.org/docs/configuration/radio/power/#adc-multiplier-override + Should be set to floating point value between 2 and 6 + """ + wait_bluetooth_secs: builtins.int + """ + Description: The number of seconds for to wait before turning off BLE in No Bluetooth states + Technical Details: ESP32 Only 0 for default of 1 minute + """ + sds_secs: builtins.int + """ + Super Deep Sleep Seconds + While in Light Sleep if mesh_sds_timeout_secs is exceeded we will lower into super deep sleep + for this value (default 1 year) or a button press + 0 for default of one year + """ + ls_secs: builtins.int + """ + Description: In light sleep the CPU is suspended, LoRa radio is on, BLE is off an GPS is on + Technical Details: ESP32 Only 0 for default of 300 + """ + min_wake_secs: builtins.int + """ + Description: While in light sleep when we receive packets on the LoRa radio we will wake and handle them and stay awake in no BLE mode for this value + Technical Details: ESP32 Only 0 for default of 10 seconds + """ + device_battery_ina_address: builtins.int + """ + I2C address of INA_2XX to use for reading device battery voltage + """ + powermon_enables: builtins.int + """ + If non-zero, we want powermon log outputs. With the particular (bitfield) sources enabled. + Note: we picked an ID of 32 so that lower more efficient IDs can be used for more frequently used options. + """ + def __init__( + self, + *, + is_power_saving: builtins.bool = ..., + on_battery_shutdown_after_secs: builtins.int = ..., + adc_multiplier_override: builtins.float = ..., + wait_bluetooth_secs: builtins.int = ..., + sds_secs: builtins.int = ..., + ls_secs: builtins.int = ..., + min_wake_secs: builtins.int = ..., + device_battery_ina_address: builtins.int = ..., + powermon_enables: builtins.int = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["adc_multiplier_override", b"adc_multiplier_override", "device_battery_ina_address", b"device_battery_ina_address", "is_power_saving", b"is_power_saving", "ls_secs", b"ls_secs", "min_wake_secs", b"min_wake_secs", "on_battery_shutdown_after_secs", b"on_battery_shutdown_after_secs", "powermon_enables", b"powermon_enables", "sds_secs", b"sds_secs", "wait_bluetooth_secs", b"wait_bluetooth_secs"]) -> None: ... + + @typing.final + class NetworkConfig(google.protobuf.message.Message): + """ + Network Config + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + class _AddressMode: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + + class _AddressModeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[Config.NetworkConfig._AddressMode.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + DHCP: Config.NetworkConfig._AddressMode.ValueType # 0 + """ + obtain ip address via DHCP + """ + STATIC: Config.NetworkConfig._AddressMode.ValueType # 1 + """ + use static ip address + """ + + class AddressMode(_AddressMode, metaclass=_AddressModeEnumTypeWrapper): ... + DHCP: Config.NetworkConfig.AddressMode.ValueType # 0 + """ + obtain ip address via DHCP + """ + STATIC: Config.NetworkConfig.AddressMode.ValueType # 1 + """ + use static ip address + """ + + class _ProtocolFlags: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + + class _ProtocolFlagsEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[Config.NetworkConfig._ProtocolFlags.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + NO_BROADCAST: Config.NetworkConfig._ProtocolFlags.ValueType # 0 + """ + Do not broadcast packets over any network protocol + """ + UDP_BROADCAST: Config.NetworkConfig._ProtocolFlags.ValueType # 1 + """ + Enable broadcasting packets via UDP over the local network + """ + + class ProtocolFlags(_ProtocolFlags, metaclass=_ProtocolFlagsEnumTypeWrapper): + """ + Available flags auxiliary network protocols + """ + + NO_BROADCAST: Config.NetworkConfig.ProtocolFlags.ValueType # 0 + """ + Do not broadcast packets over any network protocol + """ + UDP_BROADCAST: Config.NetworkConfig.ProtocolFlags.ValueType # 1 + """ + Enable broadcasting packets via UDP over the local network + """ + + @typing.final + class IpV4Config(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + IP_FIELD_NUMBER: builtins.int + GATEWAY_FIELD_NUMBER: builtins.int + SUBNET_FIELD_NUMBER: builtins.int + DNS_FIELD_NUMBER: builtins.int + ip: builtins.int + """ + Static IP address + """ + gateway: builtins.int + """ + Static gateway address + """ + subnet: builtins.int + """ + Static subnet mask + """ + dns: builtins.int + """ + Static DNS server address + """ + def __init__( + self, + *, + ip: builtins.int = ..., + gateway: builtins.int = ..., + subnet: builtins.int = ..., + dns: builtins.int = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["dns", b"dns", "gateway", b"gateway", "ip", b"ip", "subnet", b"subnet"]) -> None: ... + + WIFI_ENABLED_FIELD_NUMBER: builtins.int + WIFI_SSID_FIELD_NUMBER: builtins.int + WIFI_PSK_FIELD_NUMBER: builtins.int + NTP_SERVER_FIELD_NUMBER: builtins.int + ETH_ENABLED_FIELD_NUMBER: builtins.int + ADDRESS_MODE_FIELD_NUMBER: builtins.int + IPV4_CONFIG_FIELD_NUMBER: builtins.int + RSYSLOG_SERVER_FIELD_NUMBER: builtins.int + ENABLED_PROTOCOLS_FIELD_NUMBER: builtins.int + IPV6_ENABLED_FIELD_NUMBER: builtins.int + wifi_enabled: builtins.bool + """ + Enable WiFi (disables Bluetooth) + """ + wifi_ssid: builtins.str + """ + If set, this node will try to join the specified wifi network and + acquire an address via DHCP + """ + wifi_psk: builtins.str + """ + If set, will be use to authenticate to the named wifi + """ + ntp_server: builtins.str + """ + NTP server to use if WiFi is conneced, defaults to `meshtastic.pool.ntp.org` + """ + eth_enabled: builtins.bool + """ + Enable Ethernet + """ + address_mode: global___Config.NetworkConfig.AddressMode.ValueType + """ + acquire an address via DHCP or assign static + """ + rsyslog_server: builtins.str + """ + rsyslog Server and Port + """ + enabled_protocols: builtins.int + """ + Flags for enabling/disabling network protocols + """ + ipv6_enabled: builtins.bool + """ + Enable/Disable ipv6 support + """ + @property + def ipv4_config(self) -> global___Config.NetworkConfig.IpV4Config: + """ + struct to keep static address + """ + + def __init__( + self, + *, + wifi_enabled: builtins.bool = ..., + wifi_ssid: builtins.str = ..., + wifi_psk: builtins.str = ..., + ntp_server: builtins.str = ..., + eth_enabled: builtins.bool = ..., + address_mode: global___Config.NetworkConfig.AddressMode.ValueType = ..., + ipv4_config: global___Config.NetworkConfig.IpV4Config | None = ..., + rsyslog_server: builtins.str = ..., + enabled_protocols: builtins.int = ..., + ipv6_enabled: builtins.bool = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["ipv4_config", b"ipv4_config"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["address_mode", b"address_mode", "enabled_protocols", b"enabled_protocols", "eth_enabled", b"eth_enabled", "ipv4_config", b"ipv4_config", "ipv6_enabled", b"ipv6_enabled", "ntp_server", b"ntp_server", "rsyslog_server", b"rsyslog_server", "wifi_enabled", b"wifi_enabled", "wifi_psk", b"wifi_psk", "wifi_ssid", b"wifi_ssid"]) -> None: ... + + @typing.final + class DisplayConfig(google.protobuf.message.Message): + """ + Display Config + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + class _DeprecatedGpsCoordinateFormat: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + + class _DeprecatedGpsCoordinateFormatEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[Config.DisplayConfig._DeprecatedGpsCoordinateFormat.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + UNUSED: Config.DisplayConfig._DeprecatedGpsCoordinateFormat.ValueType # 0 + + class DeprecatedGpsCoordinateFormat(_DeprecatedGpsCoordinateFormat, metaclass=_DeprecatedGpsCoordinateFormatEnumTypeWrapper): + """ + Deprecated in 2.7.4: Unused + """ + + UNUSED: Config.DisplayConfig.DeprecatedGpsCoordinateFormat.ValueType # 0 + + class _DisplayUnits: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + + class _DisplayUnitsEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[Config.DisplayConfig._DisplayUnits.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + METRIC: Config.DisplayConfig._DisplayUnits.ValueType # 0 + """ + Metric (Default) + """ + IMPERIAL: Config.DisplayConfig._DisplayUnits.ValueType # 1 + """ + Imperial + """ + + class DisplayUnits(_DisplayUnits, metaclass=_DisplayUnitsEnumTypeWrapper): + """ + Unit display preference + """ + + METRIC: Config.DisplayConfig.DisplayUnits.ValueType # 0 + """ + Metric (Default) + """ + IMPERIAL: Config.DisplayConfig.DisplayUnits.ValueType # 1 + """ + Imperial + """ + + class _OledType: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + + class _OledTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[Config.DisplayConfig._OledType.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + OLED_AUTO: Config.DisplayConfig._OledType.ValueType # 0 + """ + Default / Autodetect + """ + OLED_SSD1306: Config.DisplayConfig._OledType.ValueType # 1 + """ + Default / Autodetect + """ + OLED_SH1106: Config.DisplayConfig._OledType.ValueType # 2 + """ + Default / Autodetect + """ + OLED_SH1107: Config.DisplayConfig._OledType.ValueType # 3 + """ + Can not be auto detected but set by proto. Used for 128x64 screens + """ + OLED_SH1107_128_128: Config.DisplayConfig._OledType.ValueType # 4 + """ + Can not be auto detected but set by proto. Used for 128x128 screens + """ + + class OledType(_OledType, metaclass=_OledTypeEnumTypeWrapper): + """ + Override OLED outo detect with this if it fails. + """ + + OLED_AUTO: Config.DisplayConfig.OledType.ValueType # 0 + """ + Default / Autodetect + """ + OLED_SSD1306: Config.DisplayConfig.OledType.ValueType # 1 + """ + Default / Autodetect + """ + OLED_SH1106: Config.DisplayConfig.OledType.ValueType # 2 + """ + Default / Autodetect + """ + OLED_SH1107: Config.DisplayConfig.OledType.ValueType # 3 + """ + Can not be auto detected but set by proto. Used for 128x64 screens + """ + OLED_SH1107_128_128: Config.DisplayConfig.OledType.ValueType # 4 + """ + Can not be auto detected but set by proto. Used for 128x128 screens + """ + + class _DisplayMode: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + + class _DisplayModeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[Config.DisplayConfig._DisplayMode.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + DEFAULT: Config.DisplayConfig._DisplayMode.ValueType # 0 + """ + Default. The old style for the 128x64 OLED screen + """ + TWOCOLOR: Config.DisplayConfig._DisplayMode.ValueType # 1 + """ + Rearrange display elements to cater for bicolor OLED displays + """ + INVERTED: Config.DisplayConfig._DisplayMode.ValueType # 2 + """ + Same as TwoColor, but with inverted top bar. Not so good for Epaper displays + """ + COLOR: Config.DisplayConfig._DisplayMode.ValueType # 3 + """ + TFT Full Color Displays (not implemented yet) + """ + + class DisplayMode(_DisplayMode, metaclass=_DisplayModeEnumTypeWrapper): ... + DEFAULT: Config.DisplayConfig.DisplayMode.ValueType # 0 + """ + Default. The old style for the 128x64 OLED screen + """ + TWOCOLOR: Config.DisplayConfig.DisplayMode.ValueType # 1 + """ + Rearrange display elements to cater for bicolor OLED displays + """ + INVERTED: Config.DisplayConfig.DisplayMode.ValueType # 2 + """ + Same as TwoColor, but with inverted top bar. Not so good for Epaper displays + """ + COLOR: Config.DisplayConfig.DisplayMode.ValueType # 3 + """ + TFT Full Color Displays (not implemented yet) + """ + + class _CompassOrientation: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + + class _CompassOrientationEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[Config.DisplayConfig._CompassOrientation.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + DEGREES_0: Config.DisplayConfig._CompassOrientation.ValueType # 0 + """ + The compass and the display are in the same orientation. + """ + DEGREES_90: Config.DisplayConfig._CompassOrientation.ValueType # 1 + """ + Rotate the compass by 90 degrees. + """ + DEGREES_180: Config.DisplayConfig._CompassOrientation.ValueType # 2 + """ + Rotate the compass by 180 degrees. + """ + DEGREES_270: Config.DisplayConfig._CompassOrientation.ValueType # 3 + """ + Rotate the compass by 270 degrees. + """ + DEGREES_0_INVERTED: Config.DisplayConfig._CompassOrientation.ValueType # 4 + """ + Don't rotate the compass, but invert the result. + """ + DEGREES_90_INVERTED: Config.DisplayConfig._CompassOrientation.ValueType # 5 + """ + Rotate the compass by 90 degrees and invert. + """ + DEGREES_180_INVERTED: Config.DisplayConfig._CompassOrientation.ValueType # 6 + """ + Rotate the compass by 180 degrees and invert. + """ + DEGREES_270_INVERTED: Config.DisplayConfig._CompassOrientation.ValueType # 7 + """ + Rotate the compass by 270 degrees and invert. + """ + + class CompassOrientation(_CompassOrientation, metaclass=_CompassOrientationEnumTypeWrapper): ... + DEGREES_0: Config.DisplayConfig.CompassOrientation.ValueType # 0 + """ + The compass and the display are in the same orientation. + """ + DEGREES_90: Config.DisplayConfig.CompassOrientation.ValueType # 1 + """ + Rotate the compass by 90 degrees. + """ + DEGREES_180: Config.DisplayConfig.CompassOrientation.ValueType # 2 + """ + Rotate the compass by 180 degrees. + """ + DEGREES_270: Config.DisplayConfig.CompassOrientation.ValueType # 3 + """ + Rotate the compass by 270 degrees. + """ + DEGREES_0_INVERTED: Config.DisplayConfig.CompassOrientation.ValueType # 4 + """ + Don't rotate the compass, but invert the result. + """ + DEGREES_90_INVERTED: Config.DisplayConfig.CompassOrientation.ValueType # 5 + """ + Rotate the compass by 90 degrees and invert. + """ + DEGREES_180_INVERTED: Config.DisplayConfig.CompassOrientation.ValueType # 6 + """ + Rotate the compass by 180 degrees and invert. + """ + DEGREES_270_INVERTED: Config.DisplayConfig.CompassOrientation.ValueType # 7 + """ + Rotate the compass by 270 degrees and invert. + """ + + SCREEN_ON_SECS_FIELD_NUMBER: builtins.int + GPS_FORMAT_FIELD_NUMBER: builtins.int + AUTO_SCREEN_CAROUSEL_SECS_FIELD_NUMBER: builtins.int + COMPASS_NORTH_TOP_FIELD_NUMBER: builtins.int + FLIP_SCREEN_FIELD_NUMBER: builtins.int + UNITS_FIELD_NUMBER: builtins.int + OLED_FIELD_NUMBER: builtins.int + DISPLAYMODE_FIELD_NUMBER: builtins.int + HEADING_BOLD_FIELD_NUMBER: builtins.int + WAKE_ON_TAP_OR_MOTION_FIELD_NUMBER: builtins.int + COMPASS_ORIENTATION_FIELD_NUMBER: builtins.int + USE_12H_CLOCK_FIELD_NUMBER: builtins.int + USE_LONG_NODE_NAME_FIELD_NUMBER: builtins.int + screen_on_secs: builtins.int + """ + Number of seconds the screen stays on after pressing the user button or receiving a message + 0 for default of one minute MAXUINT for always on + """ + gps_format: global___Config.DisplayConfig.DeprecatedGpsCoordinateFormat.ValueType + """ + Deprecated in 2.7.4: Unused + How the GPS coordinates are formatted on the OLED screen. + """ + auto_screen_carousel_secs: builtins.int + """ + Automatically toggles to the next page on the screen like a carousel, based the specified interval in seconds. + Potentially useful for devices without user buttons. + """ + compass_north_top: builtins.bool + """ + If this is set, the displayed compass will always point north. if unset, the old behaviour + (top of display is heading direction) is used. + """ + flip_screen: builtins.bool + """ + Flip screen vertically, for cases that mount the screen upside down + """ + units: global___Config.DisplayConfig.DisplayUnits.ValueType + """ + Perferred display units + """ + oled: global___Config.DisplayConfig.OledType.ValueType + """ + Override auto-detect in screen + """ + displaymode: global___Config.DisplayConfig.DisplayMode.ValueType + """ + Display Mode + """ + heading_bold: builtins.bool + """ + Print first line in pseudo-bold? FALSE is original style, TRUE is bold + """ + wake_on_tap_or_motion: builtins.bool + """ + Should we wake the screen up on accelerometer detected motion or tap + """ + compass_orientation: global___Config.DisplayConfig.CompassOrientation.ValueType + """ + Indicates how to rotate or invert the compass output to accurate display on the display. + """ + use_12h_clock: builtins.bool + """ + If false (default), the device will display the time in 24-hour format on screen. + If true, the device will display the time in 12-hour format on screen. + """ + use_long_node_name: builtins.bool + """ + If false (default), the device will use short names for various display screens. + If true, node names will show in long format + """ + def __init__( + self, + *, + screen_on_secs: builtins.int = ..., + gps_format: global___Config.DisplayConfig.DeprecatedGpsCoordinateFormat.ValueType = ..., + auto_screen_carousel_secs: builtins.int = ..., + compass_north_top: builtins.bool = ..., + flip_screen: builtins.bool = ..., + units: global___Config.DisplayConfig.DisplayUnits.ValueType = ..., + oled: global___Config.DisplayConfig.OledType.ValueType = ..., + displaymode: global___Config.DisplayConfig.DisplayMode.ValueType = ..., + heading_bold: builtins.bool = ..., + wake_on_tap_or_motion: builtins.bool = ..., + compass_orientation: global___Config.DisplayConfig.CompassOrientation.ValueType = ..., + use_12h_clock: builtins.bool = ..., + use_long_node_name: 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: ... + + @typing.final + class LoRaConfig(google.protobuf.message.Message): + """ + Lora Config + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + class _RegionCode: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + + class _RegionCodeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[Config.LoRaConfig._RegionCode.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + UNSET: Config.LoRaConfig._RegionCode.ValueType # 0 + """ + Region is not set + """ + US: Config.LoRaConfig._RegionCode.ValueType # 1 + """ + United States + """ + EU_433: Config.LoRaConfig._RegionCode.ValueType # 2 + """ + European Union 433mhz + """ + EU_868: Config.LoRaConfig._RegionCode.ValueType # 3 + """ + European Union 868mhz + """ + CN: Config.LoRaConfig._RegionCode.ValueType # 4 + """ + China + """ + JP: Config.LoRaConfig._RegionCode.ValueType # 5 + """ + Japan + """ + ANZ: Config.LoRaConfig._RegionCode.ValueType # 6 + """ + Australia / New Zealand + """ + KR: Config.LoRaConfig._RegionCode.ValueType # 7 + """ + Korea + """ + TW: Config.LoRaConfig._RegionCode.ValueType # 8 + """ + Taiwan + """ + RU: Config.LoRaConfig._RegionCode.ValueType # 9 + """ + Russia + """ + IN: Config.LoRaConfig._RegionCode.ValueType # 10 + """ + India + """ + NZ_865: Config.LoRaConfig._RegionCode.ValueType # 11 + """ + New Zealand 865mhz + """ + TH: Config.LoRaConfig._RegionCode.ValueType # 12 + """ + Thailand + """ + LORA_24: Config.LoRaConfig._RegionCode.ValueType # 13 + """ + WLAN Band + """ + UA_433: Config.LoRaConfig._RegionCode.ValueType # 14 + """ + Ukraine 433mhz + """ + UA_868: Config.LoRaConfig._RegionCode.ValueType # 15 + """ + Ukraine 868mhz + """ + MY_433: Config.LoRaConfig._RegionCode.ValueType # 16 + """ + Malaysia 433mhz + """ + MY_919: Config.LoRaConfig._RegionCode.ValueType # 17 + """ + Malaysia 919mhz + """ + SG_923: Config.LoRaConfig._RegionCode.ValueType # 18 + """ + Singapore 923mhz + """ + PH_433: Config.LoRaConfig._RegionCode.ValueType # 19 + """ + Philippines 433mhz + """ + PH_868: Config.LoRaConfig._RegionCode.ValueType # 20 + """ + Philippines 868mhz + """ + PH_915: Config.LoRaConfig._RegionCode.ValueType # 21 + """ + Philippines 915mhz + """ + ANZ_433: Config.LoRaConfig._RegionCode.ValueType # 22 + """ + Australia / New Zealand 433MHz + """ + KZ_433: Config.LoRaConfig._RegionCode.ValueType # 23 + """ + Kazakhstan 433MHz + """ + KZ_863: Config.LoRaConfig._RegionCode.ValueType # 24 + """ + Kazakhstan 863MHz + """ + NP_865: Config.LoRaConfig._RegionCode.ValueType # 25 + """ + Nepal 865MHz + """ + BR_902: Config.LoRaConfig._RegionCode.ValueType # 26 + """ + Brazil 902MHz + """ + + class RegionCode(_RegionCode, metaclass=_RegionCodeEnumTypeWrapper): ... + UNSET: Config.LoRaConfig.RegionCode.ValueType # 0 + """ + Region is not set + """ + US: Config.LoRaConfig.RegionCode.ValueType # 1 + """ + United States + """ + EU_433: Config.LoRaConfig.RegionCode.ValueType # 2 + """ + European Union 433mhz + """ + EU_868: Config.LoRaConfig.RegionCode.ValueType # 3 + """ + European Union 868mhz + """ + CN: Config.LoRaConfig.RegionCode.ValueType # 4 + """ + China + """ + JP: Config.LoRaConfig.RegionCode.ValueType # 5 + """ + Japan + """ + ANZ: Config.LoRaConfig.RegionCode.ValueType # 6 + """ + Australia / New Zealand + """ + KR: Config.LoRaConfig.RegionCode.ValueType # 7 + """ + Korea + """ + TW: Config.LoRaConfig.RegionCode.ValueType # 8 + """ + Taiwan + """ + RU: Config.LoRaConfig.RegionCode.ValueType # 9 + """ + Russia + """ + IN: Config.LoRaConfig.RegionCode.ValueType # 10 + """ + India + """ + NZ_865: Config.LoRaConfig.RegionCode.ValueType # 11 + """ + New Zealand 865mhz + """ + TH: Config.LoRaConfig.RegionCode.ValueType # 12 + """ + Thailand + """ + LORA_24: Config.LoRaConfig.RegionCode.ValueType # 13 + """ + WLAN Band + """ + UA_433: Config.LoRaConfig.RegionCode.ValueType # 14 + """ + Ukraine 433mhz + """ + UA_868: Config.LoRaConfig.RegionCode.ValueType # 15 + """ + Ukraine 868mhz + """ + MY_433: Config.LoRaConfig.RegionCode.ValueType # 16 + """ + Malaysia 433mhz + """ + MY_919: Config.LoRaConfig.RegionCode.ValueType # 17 + """ + Malaysia 919mhz + """ + SG_923: Config.LoRaConfig.RegionCode.ValueType # 18 + """ + Singapore 923mhz + """ + PH_433: Config.LoRaConfig.RegionCode.ValueType # 19 + """ + Philippines 433mhz + """ + PH_868: Config.LoRaConfig.RegionCode.ValueType # 20 + """ + Philippines 868mhz + """ + PH_915: Config.LoRaConfig.RegionCode.ValueType # 21 + """ + Philippines 915mhz + """ + ANZ_433: Config.LoRaConfig.RegionCode.ValueType # 22 + """ + Australia / New Zealand 433MHz + """ + KZ_433: Config.LoRaConfig.RegionCode.ValueType # 23 + """ + Kazakhstan 433MHz + """ + KZ_863: Config.LoRaConfig.RegionCode.ValueType # 24 + """ + Kazakhstan 863MHz + """ + NP_865: Config.LoRaConfig.RegionCode.ValueType # 25 + """ + Nepal 865MHz + """ + BR_902: Config.LoRaConfig.RegionCode.ValueType # 26 + """ + Brazil 902MHz + """ + + class _ModemPreset: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + + class _ModemPresetEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[Config.LoRaConfig._ModemPreset.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + LONG_FAST: Config.LoRaConfig._ModemPreset.ValueType # 0 + """ + Long Range - Fast + """ + LONG_SLOW: Config.LoRaConfig._ModemPreset.ValueType # 1 + """ + Long Range - Slow + """ + VERY_LONG_SLOW: Config.LoRaConfig._ModemPreset.ValueType # 2 + """ + Very Long Range - Slow + Deprecated in 2.5: Works only with txco and is unusably slow + """ + MEDIUM_SLOW: Config.LoRaConfig._ModemPreset.ValueType # 3 + """ + Medium Range - Slow + """ + MEDIUM_FAST: Config.LoRaConfig._ModemPreset.ValueType # 4 + """ + Medium Range - Fast + """ + SHORT_SLOW: Config.LoRaConfig._ModemPreset.ValueType # 5 + """ + Short Range - Slow + """ + SHORT_FAST: Config.LoRaConfig._ModemPreset.ValueType # 6 + """ + Short Range - Fast + """ + LONG_MODERATE: Config.LoRaConfig._ModemPreset.ValueType # 7 + """ + Long Range - Moderately Fast + """ + SHORT_TURBO: Config.LoRaConfig._ModemPreset.ValueType # 8 + """ + Short Range - Turbo + This is the fastest preset and the only one with 500kHz bandwidth. + It is not legal to use in all regions due to this wider bandwidth. + """ + + class ModemPreset(_ModemPreset, metaclass=_ModemPresetEnumTypeWrapper): + """ + Standard predefined channel settings + Note: these mappings must match ModemPreset Choice in the device code. + """ + + LONG_FAST: Config.LoRaConfig.ModemPreset.ValueType # 0 + """ + Long Range - Fast + """ + LONG_SLOW: Config.LoRaConfig.ModemPreset.ValueType # 1 + """ + Long Range - Slow + """ + VERY_LONG_SLOW: Config.LoRaConfig.ModemPreset.ValueType # 2 + """ + Very Long Range - Slow + Deprecated in 2.5: Works only with txco and is unusably slow + """ + MEDIUM_SLOW: Config.LoRaConfig.ModemPreset.ValueType # 3 + """ + Medium Range - Slow + """ + MEDIUM_FAST: Config.LoRaConfig.ModemPreset.ValueType # 4 + """ + Medium Range - Fast + """ + SHORT_SLOW: Config.LoRaConfig.ModemPreset.ValueType # 5 + """ + Short Range - Slow + """ + SHORT_FAST: Config.LoRaConfig.ModemPreset.ValueType # 6 + """ + Short Range - Fast + """ + LONG_MODERATE: Config.LoRaConfig.ModemPreset.ValueType # 7 + """ + Long Range - Moderately Fast + """ + SHORT_TURBO: Config.LoRaConfig.ModemPreset.ValueType # 8 + """ + Short Range - Turbo + This is the fastest preset and the only one with 500kHz bandwidth. + It is not legal to use in all regions due to this wider bandwidth. + """ + + USE_PRESET_FIELD_NUMBER: builtins.int + MODEM_PRESET_FIELD_NUMBER: builtins.int + BANDWIDTH_FIELD_NUMBER: builtins.int + SPREAD_FACTOR_FIELD_NUMBER: builtins.int + CODING_RATE_FIELD_NUMBER: builtins.int + FREQUENCY_OFFSET_FIELD_NUMBER: builtins.int + REGION_FIELD_NUMBER: builtins.int + HOP_LIMIT_FIELD_NUMBER: builtins.int + TX_ENABLED_FIELD_NUMBER: builtins.int + TX_POWER_FIELD_NUMBER: builtins.int + CHANNEL_NUM_FIELD_NUMBER: builtins.int + OVERRIDE_DUTY_CYCLE_FIELD_NUMBER: builtins.int + SX126X_RX_BOOSTED_GAIN_FIELD_NUMBER: builtins.int + OVERRIDE_FREQUENCY_FIELD_NUMBER: builtins.int + PA_FAN_DISABLED_FIELD_NUMBER: builtins.int + IGNORE_INCOMING_FIELD_NUMBER: builtins.int + IGNORE_MQTT_FIELD_NUMBER: builtins.int + CONFIG_OK_TO_MQTT_FIELD_NUMBER: builtins.int + use_preset: builtins.bool + """ + When enabled, the `modem_preset` fields will be adhered to, else the `bandwidth`/`spread_factor`/`coding_rate` + will be taked from their respective manually defined fields + """ + modem_preset: global___Config.LoRaConfig.ModemPreset.ValueType + """ + Either modem_config or bandwidth/spreading/coding will be specified - NOT BOTH. + As a heuristic: If bandwidth is specified, do not use modem_config. + Because protobufs take ZERO space when the value is zero this works out nicely. + This value is replaced by bandwidth/spread_factor/coding_rate. + If you'd like to experiment with other options add them to MeshRadio.cpp in the device code. + """ + bandwidth: builtins.int + """ + Bandwidth in MHz + Certain bandwidth numbers are 'special' and will be converted to the + appropriate floating point value: 31 -> 31.25MHz + """ + spread_factor: builtins.int + """ + A number from 7 to 12. + Indicates number of chirps per symbol as 1< 7 results in the default + """ + tx_enabled: builtins.bool + """ + Disable TX from the LoRa radio. Useful for hot-swapping antennas and other tests. + Defaults to false + """ + tx_power: builtins.int + """ + If zero, then use default max legal continuous power (ie. something that won't + burn out the radio hardware) + In most cases you should use zero here. + Units are in dBm. + """ + channel_num: builtins.int + """ + This controls the actual hardware frequency the radio transmits on. + Most users should never need to be exposed to this field/concept. + A channel number between 1 and NUM_CHANNELS (whatever the max is in the current region). + If ZERO then the rule is "use the old channel name hash based + algorithm to derive the channel number") + If using the hash algorithm the channel number will be: hash(channel_name) % + NUM_CHANNELS (Where num channels depends on the regulatory region). + """ + override_duty_cycle: builtins.bool + """ + If true, duty cycle limits will be exceeded and thus you're possibly not following + the local regulations if you're not a HAM. + Has no effect if the duty cycle of the used region is 100%. + """ + sx126x_rx_boosted_gain: builtins.bool + """ + If true, sets RX boosted gain mode on SX126X based radios + """ + override_frequency: builtins.float + """ + This parameter is for advanced users and licensed HAM radio operators. + Ignore Channel Calculation and use this frequency instead. The frequency_offset + will still be applied. This will allow you to use out-of-band frequencies. + Please respect your local laws and regulations. If you are a HAM, make sure you + enable HAM mode and turn off encryption. + """ + pa_fan_disabled: builtins.bool + """ + If true, disable the build-in PA FAN using pin define in RF95_FAN_EN. + """ + ignore_mqtt: builtins.bool + """ + If true, the device will not process any packets received via LoRa that passed via MQTT anywhere on the path towards it. + """ + config_ok_to_mqtt: builtins.bool + """ + Sets the ok_to_mqtt bit on outgoing packets + """ + @property + def ignore_incoming(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.int]: + """ + For testing it is useful sometimes to force a node to never listen to + particular other nodes (simulating radio out of range). All nodenums listed + in ignore_incoming will have packets they send dropped on receive (by router.cpp) + """ + + def __init__( + self, + *, + use_preset: builtins.bool = ..., + modem_preset: global___Config.LoRaConfig.ModemPreset.ValueType = ..., + bandwidth: builtins.int = ..., + spread_factor: builtins.int = ..., + coding_rate: builtins.int = ..., + frequency_offset: builtins.float = ..., + region: global___Config.LoRaConfig.RegionCode.ValueType = ..., + hop_limit: builtins.int = ..., + tx_enabled: builtins.bool = ..., + tx_power: builtins.int = ..., + channel_num: builtins.int = ..., + override_duty_cycle: builtins.bool = ..., + sx126x_rx_boosted_gain: builtins.bool = ..., + override_frequency: builtins.float = ..., + pa_fan_disabled: builtins.bool = ..., + ignore_incoming: collections.abc.Iterable[builtins.int] | None = ..., + ignore_mqtt: builtins.bool = ..., + config_ok_to_mqtt: 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: ... + + @typing.final + class BluetoothConfig(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + class _PairingMode: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + + class _PairingModeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[Config.BluetoothConfig._PairingMode.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + RANDOM_PIN: Config.BluetoothConfig._PairingMode.ValueType # 0 + """ + Device generates a random PIN that will be shown on the screen of the device for pairing + """ + FIXED_PIN: Config.BluetoothConfig._PairingMode.ValueType # 1 + """ + Device requires a specified fixed PIN for pairing + """ + NO_PIN: Config.BluetoothConfig._PairingMode.ValueType # 2 + """ + Device requires no PIN for pairing + """ + + class PairingMode(_PairingMode, metaclass=_PairingModeEnumTypeWrapper): ... + RANDOM_PIN: Config.BluetoothConfig.PairingMode.ValueType # 0 + """ + Device generates a random PIN that will be shown on the screen of the device for pairing + """ + FIXED_PIN: Config.BluetoothConfig.PairingMode.ValueType # 1 + """ + Device requires a specified fixed PIN for pairing + """ + NO_PIN: Config.BluetoothConfig.PairingMode.ValueType # 2 + """ + Device requires no PIN for pairing + """ + + ENABLED_FIELD_NUMBER: builtins.int + MODE_FIELD_NUMBER: builtins.int + FIXED_PIN_FIELD_NUMBER: builtins.int + enabled: builtins.bool + """ + Enable Bluetooth on the device + """ + mode: global___Config.BluetoothConfig.PairingMode.ValueType + """ + Determines the pairing strategy for the device + """ + fixed_pin: builtins.int + """ + Specified PIN for PairingMode.FixedPin + """ + def __init__( + self, + *, + enabled: builtins.bool = ..., + mode: global___Config.BluetoothConfig.PairingMode.ValueType = ..., + fixed_pin: builtins.int = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["enabled", b"enabled", "fixed_pin", b"fixed_pin", "mode", b"mode"]) -> None: ... + + @typing.final + class SecurityConfig(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + PUBLIC_KEY_FIELD_NUMBER: builtins.int + PRIVATE_KEY_FIELD_NUMBER: builtins.int + ADMIN_KEY_FIELD_NUMBER: builtins.int + IS_MANAGED_FIELD_NUMBER: builtins.int + SERIAL_ENABLED_FIELD_NUMBER: builtins.int + DEBUG_LOG_API_ENABLED_FIELD_NUMBER: builtins.int + ADMIN_CHANNEL_ENABLED_FIELD_NUMBER: builtins.int + public_key: builtins.bytes + """ + The public key of the user's device. + Sent out to other nodes on the mesh to allow them to compute a shared secret key. + """ + private_key: builtins.bytes + """ + The private key of the device. + Used to create a shared key with a remote device. + """ + is_managed: builtins.bool + """ + If true, device is considered to be "managed" by a mesh administrator via admin messages + Device is managed by a mesh administrator. + """ + serial_enabled: builtins.bool + """ + Serial Console over the Stream API." + """ + debug_log_api_enabled: builtins.bool + """ + By default we turn off logging as soon as an API client connects (to keep shared serial link quiet). + Output live debug logging over serial or bluetooth is set to true. + """ + admin_channel_enabled: builtins.bool + """ + Allow incoming device control over the insecure legacy admin channel. + """ + @property + def admin_key(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.bytes]: + """ + The public key authorized to send admin messages to this node. + """ + + def __init__( + self, + *, + public_key: builtins.bytes = ..., + private_key: builtins.bytes = ..., + admin_key: collections.abc.Iterable[builtins.bytes] | None = ..., + is_managed: builtins.bool = ..., + serial_enabled: builtins.bool = ..., + debug_log_api_enabled: builtins.bool = ..., + admin_channel_enabled: builtins.bool = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["admin_channel_enabled", b"admin_channel_enabled", "admin_key", b"admin_key", "debug_log_api_enabled", b"debug_log_api_enabled", "is_managed", b"is_managed", "private_key", b"private_key", "public_key", b"public_key", "serial_enabled", b"serial_enabled"]) -> None: ... + + @typing.final + class SessionkeyConfig(google.protobuf.message.Message): + """ + Blank config request, strictly for getting the session key + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__( + self, + ) -> None: ... + + DEVICE_FIELD_NUMBER: builtins.int + POSITION_FIELD_NUMBER: builtins.int + POWER_FIELD_NUMBER: builtins.int + NETWORK_FIELD_NUMBER: builtins.int + DISPLAY_FIELD_NUMBER: builtins.int + LORA_FIELD_NUMBER: builtins.int + BLUETOOTH_FIELD_NUMBER: builtins.int + SECURITY_FIELD_NUMBER: builtins.int + SESSIONKEY_FIELD_NUMBER: builtins.int + DEVICE_UI_FIELD_NUMBER: builtins.int + @property + def device(self) -> global___Config.DeviceConfig: ... + @property + def position(self) -> global___Config.PositionConfig: ... + @property + def power(self) -> global___Config.PowerConfig: ... + @property + def network(self) -> global___Config.NetworkConfig: ... + @property + def display(self) -> global___Config.DisplayConfig: ... + @property + def lora(self) -> global___Config.LoRaConfig: ... + @property + def bluetooth(self) -> global___Config.BluetoothConfig: ... + @property + def security(self) -> global___Config.SecurityConfig: ... + @property + def sessionkey(self) -> global___Config.SessionkeyConfig: ... + @property + def device_ui(self) -> meshtastic.protobuf.device_ui_pb2.DeviceUIConfig: ... + def __init__( + self, + *, + device: global___Config.DeviceConfig | None = ..., + position: global___Config.PositionConfig | None = ..., + power: global___Config.PowerConfig | None = ..., + network: global___Config.NetworkConfig | None = ..., + display: global___Config.DisplayConfig | None = ..., + lora: global___Config.LoRaConfig | None = ..., + bluetooth: global___Config.BluetoothConfig | None = ..., + security: global___Config.SecurityConfig | None = ..., + sessionkey: global___Config.SessionkeyConfig | None = ..., + device_ui: meshtastic.protobuf.device_ui_pb2.DeviceUIConfig | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["bluetooth", b"bluetooth", "device", b"device", "device_ui", b"device_ui", "display", b"display", "lora", b"lora", "network", b"network", "payload_variant", b"payload_variant", "position", b"position", "power", b"power", "security", b"security", "sessionkey", b"sessionkey"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["bluetooth", b"bluetooth", "device", b"device", "device_ui", b"device_ui", "display", b"display", "lora", b"lora", "network", b"network", "payload_variant", b"payload_variant", "position", b"position", "power", b"power", "security", b"security", "sessionkey", b"sessionkey"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["payload_variant", b"payload_variant"]) -> typing.Literal["device", "position", "power", "network", "display", "lora", "bluetooth", "security", "sessionkey", "device_ui"] | None: ... + +global___Config = Config diff --git a/meshtastic/protobuf/connection_status_pb2.py b/meshtastic/protobuf/connection_status_pb2.py new file mode 100644 index 00000000..050e3302 --- /dev/null +++ b/meshtastic/protobuf/connection_status_pb2.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: meshtastic/protobuf/connection_status.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n+meshtastic/protobuf/connection_status.proto\x12\x13meshtastic.protobuf\"\xd5\x02\n\x16\x44\x65viceConnectionStatus\x12<\n\x04wifi\x18\x01 \x01(\x0b\x32).meshtastic.protobuf.WifiConnectionStatusH\x00\x88\x01\x01\x12\x44\n\x08\x65thernet\x18\x02 \x01(\x0b\x32-.meshtastic.protobuf.EthernetConnectionStatusH\x01\x88\x01\x01\x12\x46\n\tbluetooth\x18\x03 \x01(\x0b\x32..meshtastic.protobuf.BluetoothConnectionStatusH\x02\x88\x01\x01\x12@\n\x06serial\x18\x04 \x01(\x0b\x32+.meshtastic.protobuf.SerialConnectionStatusH\x03\x88\x01\x01\x42\x07\n\x05_wifiB\x0b\n\t_ethernetB\x0c\n\n_bluetoothB\t\n\x07_serial\"p\n\x14WifiConnectionStatus\x12<\n\x06status\x18\x01 \x01(\x0b\x32,.meshtastic.protobuf.NetworkConnectionStatus\x12\x0c\n\x04ssid\x18\x02 \x01(\t\x12\x0c\n\x04rssi\x18\x03 \x01(\x05\"X\n\x18\x45thernetConnectionStatus\x12<\n\x06status\x18\x01 \x01(\x0b\x32,.meshtastic.protobuf.NetworkConnectionStatus\"{\n\x17NetworkConnectionStatus\x12\x12\n\nip_address\x18\x01 \x01(\x07\x12\x14\n\x0cis_connected\x18\x02 \x01(\x08\x12\x19\n\x11is_mqtt_connected\x18\x03 \x01(\x08\x12\x1b\n\x13is_syslog_connected\x18\x04 \x01(\x08\"L\n\x19\x42luetoothConnectionStatus\x12\x0b\n\x03pin\x18\x01 \x01(\r\x12\x0c\n\x04rssi\x18\x02 \x01(\x05\x12\x14\n\x0cis_connected\x18\x03 \x01(\x08\"<\n\x16SerialConnectionStatus\x12\x0c\n\x04\x62\x61ud\x18\x01 \x01(\r\x12\x14\n\x0cis_connected\x18\x02 \x01(\x08\x42\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) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.connection_status_pb2', _globals) +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 +# @@protoc_insertion_point(module_scope) diff --git a/meshtastic/protobuf/connection_status_pb2.pyi b/meshtastic/protobuf/connection_status_pb2.pyi new file mode 100644 index 00000000..7b69140a --- /dev/null +++ b/meshtastic/protobuf/connection_status_pb2.pyi @@ -0,0 +1,228 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" + +import builtins +import google.protobuf.descriptor +import google.protobuf.message +import typing + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing.final +class DeviceConnectionStatus(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + WIFI_FIELD_NUMBER: builtins.int + ETHERNET_FIELD_NUMBER: builtins.int + BLUETOOTH_FIELD_NUMBER: builtins.int + SERIAL_FIELD_NUMBER: builtins.int + @property + def wifi(self) -> global___WifiConnectionStatus: + """ + WiFi Status + """ + + @property + def ethernet(self) -> global___EthernetConnectionStatus: + """ + WiFi Status + """ + + @property + def bluetooth(self) -> global___BluetoothConnectionStatus: + """ + Bluetooth Status + """ + + @property + def serial(self) -> global___SerialConnectionStatus: + """ + Serial Status + """ + + def __init__( + self, + *, + wifi: global___WifiConnectionStatus | None = ..., + ethernet: global___EthernetConnectionStatus | None = ..., + bluetooth: global___BluetoothConnectionStatus | None = ..., + serial: global___SerialConnectionStatus | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["_bluetooth", b"_bluetooth", "_ethernet", b"_ethernet", "_serial", b"_serial", "_wifi", b"_wifi", "bluetooth", b"bluetooth", "ethernet", b"ethernet", "serial", b"serial", "wifi", b"wifi"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["_bluetooth", b"_bluetooth", "_ethernet", b"_ethernet", "_serial", b"_serial", "_wifi", b"_wifi", "bluetooth", b"bluetooth", "ethernet", b"ethernet", "serial", b"serial", "wifi", b"wifi"]) -> None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_bluetooth", b"_bluetooth"]) -> typing.Literal["bluetooth"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_ethernet", b"_ethernet"]) -> typing.Literal["ethernet"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_serial", b"_serial"]) -> typing.Literal["serial"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_wifi", b"_wifi"]) -> typing.Literal["wifi"] | None: ... + +global___DeviceConnectionStatus = DeviceConnectionStatus + +@typing.final +class WifiConnectionStatus(google.protobuf.message.Message): + """ + WiFi connection status + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + STATUS_FIELD_NUMBER: builtins.int + SSID_FIELD_NUMBER: builtins.int + RSSI_FIELD_NUMBER: builtins.int + ssid: builtins.str + """ + WiFi access point SSID + """ + rssi: builtins.int + """ + RSSI of wireless connection + """ + @property + def status(self) -> global___NetworkConnectionStatus: + """ + Connection status + """ + + def __init__( + self, + *, + status: global___NetworkConnectionStatus | None = ..., + ssid: builtins.str = ..., + rssi: builtins.int = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["status", b"status"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["rssi", b"rssi", "ssid", b"ssid", "status", b"status"]) -> None: ... + +global___WifiConnectionStatus = WifiConnectionStatus + +@typing.final +class EthernetConnectionStatus(google.protobuf.message.Message): + """ + Ethernet connection status + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + STATUS_FIELD_NUMBER: builtins.int + @property + def status(self) -> global___NetworkConnectionStatus: + """ + Connection status + """ + + def __init__( + self, + *, + status: global___NetworkConnectionStatus | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["status", b"status"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["status", b"status"]) -> None: ... + +global___EthernetConnectionStatus = EthernetConnectionStatus + +@typing.final +class NetworkConnectionStatus(google.protobuf.message.Message): + """ + Ethernet or WiFi connection status + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + IP_ADDRESS_FIELD_NUMBER: builtins.int + IS_CONNECTED_FIELD_NUMBER: builtins.int + IS_MQTT_CONNECTED_FIELD_NUMBER: builtins.int + IS_SYSLOG_CONNECTED_FIELD_NUMBER: builtins.int + ip_address: builtins.int + """ + IP address of device + """ + is_connected: builtins.bool + """ + Whether the device has an active connection or not + """ + is_mqtt_connected: builtins.bool + """ + Whether the device has an active connection to an MQTT broker or not + """ + is_syslog_connected: builtins.bool + """ + Whether the device is actively remote syslogging or not + """ + def __init__( + self, + *, + ip_address: builtins.int = ..., + is_connected: builtins.bool = ..., + is_mqtt_connected: builtins.bool = ..., + is_syslog_connected: builtins.bool = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["ip_address", b"ip_address", "is_connected", b"is_connected", "is_mqtt_connected", b"is_mqtt_connected", "is_syslog_connected", b"is_syslog_connected"]) -> None: ... + +global___NetworkConnectionStatus = NetworkConnectionStatus + +@typing.final +class BluetoothConnectionStatus(google.protobuf.message.Message): + """ + Bluetooth connection status + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + PIN_FIELD_NUMBER: builtins.int + RSSI_FIELD_NUMBER: builtins.int + IS_CONNECTED_FIELD_NUMBER: builtins.int + pin: builtins.int + """ + The pairing PIN for bluetooth + """ + rssi: builtins.int + """ + RSSI of bluetooth connection + """ + is_connected: builtins.bool + """ + Whether the device has an active connection or not + """ + def __init__( + self, + *, + pin: builtins.int = ..., + rssi: builtins.int = ..., + is_connected: builtins.bool = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["is_connected", b"is_connected", "pin", b"pin", "rssi", b"rssi"]) -> None: ... + +global___BluetoothConnectionStatus = BluetoothConnectionStatus + +@typing.final +class SerialConnectionStatus(google.protobuf.message.Message): + """ + Serial connection status + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + BAUD_FIELD_NUMBER: builtins.int + IS_CONNECTED_FIELD_NUMBER: builtins.int + baud: builtins.int + """ + Serial baud rate + """ + is_connected: builtins.bool + """ + Whether the device has an active connection or not + """ + def __init__( + self, + *, + baud: builtins.int = ..., + is_connected: builtins.bool = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["baud", b"baud", "is_connected", b"is_connected"]) -> None: ... + +global___SerialConnectionStatus = SerialConnectionStatus diff --git a/meshtastic/protobuf/device_ui_pb2.py b/meshtastic/protobuf/device_ui_pb2.py new file mode 100644 index 00000000..28eb04e2 --- /dev/null +++ b/meshtastic/protobuf/device_ui_pb2.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: meshtastic/protobuf/device_ui.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n#meshtastic/protobuf/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') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.device_ui_pb2', _globals) +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 +# @@protoc_insertion_point(module_scope) diff --git a/meshtastic/protobuf/device_ui_pb2.pyi b/meshtastic/protobuf/device_ui_pb2.pyi new file mode 100644 index 00000000..065215b3 --- /dev/null +++ b/meshtastic/protobuf/device_ui_pb2.pyi @@ -0,0 +1,649 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" + +import builtins +import google.protobuf.descriptor +import google.protobuf.internal.enum_type_wrapper +import google.protobuf.message +import sys +import typing + +if sys.version_info >= (3, 10): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +class _CompassMode: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _CompassModeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_CompassMode.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + DYNAMIC: _CompassMode.ValueType # 0 + """ + Compass with dynamic ring and heading + """ + FIXED_RING: _CompassMode.ValueType # 1 + """ + Compass with fixed ring and heading + """ + FREEZE_HEADING: _CompassMode.ValueType # 2 + """ + Compass with heading and freeze option + """ + +class CompassMode(_CompassMode, metaclass=_CompassModeEnumTypeWrapper): ... + +DYNAMIC: CompassMode.ValueType # 0 +""" +Compass with dynamic ring and heading +""" +FIXED_RING: CompassMode.ValueType # 1 +""" +Compass with fixed ring and heading +""" +FREEZE_HEADING: CompassMode.ValueType # 2 +""" +Compass with heading and freeze option +""" +global___CompassMode = CompassMode + +class _Theme: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _ThemeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_Theme.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + DARK: _Theme.ValueType # 0 + """ + Dark + """ + LIGHT: _Theme.ValueType # 1 + """ + Light + """ + RED: _Theme.ValueType # 2 + """ + Red + """ + +class Theme(_Theme, metaclass=_ThemeEnumTypeWrapper): ... + +DARK: Theme.ValueType # 0 +""" +Dark +""" +LIGHT: Theme.ValueType # 1 +""" +Light +""" +RED: Theme.ValueType # 2 +""" +Red +""" +global___Theme = Theme + +class _Language: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _LanguageEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_Language.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + ENGLISH: _Language.ValueType # 0 + """ + English + """ + FRENCH: _Language.ValueType # 1 + """ + French + """ + GERMAN: _Language.ValueType # 2 + """ + German + """ + ITALIAN: _Language.ValueType # 3 + """ + Italian + """ + PORTUGUESE: _Language.ValueType # 4 + """ + Portuguese + """ + SPANISH: _Language.ValueType # 5 + """ + Spanish + """ + SWEDISH: _Language.ValueType # 6 + """ + Swedish + """ + FINNISH: _Language.ValueType # 7 + """ + Finnish + """ + POLISH: _Language.ValueType # 8 + """ + Polish + """ + TURKISH: _Language.ValueType # 9 + """ + Turkish + """ + SERBIAN: _Language.ValueType # 10 + """ + Serbian + """ + RUSSIAN: _Language.ValueType # 11 + """ + Russian + """ + DUTCH: _Language.ValueType # 12 + """ + Dutch + """ + GREEK: _Language.ValueType # 13 + """ + Greek + """ + NORWEGIAN: _Language.ValueType # 14 + """ + Norwegian + """ + SLOVENIAN: _Language.ValueType # 15 + """ + Slovenian + """ + UKRAINIAN: _Language.ValueType # 16 + """ + Ukrainian + """ + BULGARIAN: _Language.ValueType # 17 + """ + Bulgarian + """ + CZECH: _Language.ValueType # 18 + """ + Czech + """ + DANISH: _Language.ValueType # 19 + """ + Danish + """ + SIMPLIFIED_CHINESE: _Language.ValueType # 30 + """ + Simplified Chinese (experimental) + """ + TRADITIONAL_CHINESE: _Language.ValueType # 31 + """ + Traditional Chinese (experimental) + """ + +class Language(_Language, metaclass=_LanguageEnumTypeWrapper): + """ + Localization + """ + +ENGLISH: Language.ValueType # 0 +""" +English +""" +FRENCH: Language.ValueType # 1 +""" +French +""" +GERMAN: Language.ValueType # 2 +""" +German +""" +ITALIAN: Language.ValueType # 3 +""" +Italian +""" +PORTUGUESE: Language.ValueType # 4 +""" +Portuguese +""" +SPANISH: Language.ValueType # 5 +""" +Spanish +""" +SWEDISH: Language.ValueType # 6 +""" +Swedish +""" +FINNISH: Language.ValueType # 7 +""" +Finnish +""" +POLISH: Language.ValueType # 8 +""" +Polish +""" +TURKISH: Language.ValueType # 9 +""" +Turkish +""" +SERBIAN: Language.ValueType # 10 +""" +Serbian +""" +RUSSIAN: Language.ValueType # 11 +""" +Russian +""" +DUTCH: Language.ValueType # 12 +""" +Dutch +""" +GREEK: Language.ValueType # 13 +""" +Greek +""" +NORWEGIAN: Language.ValueType # 14 +""" +Norwegian +""" +SLOVENIAN: Language.ValueType # 15 +""" +Slovenian +""" +UKRAINIAN: Language.ValueType # 16 +""" +Ukrainian +""" +BULGARIAN: Language.ValueType # 17 +""" +Bulgarian +""" +CZECH: Language.ValueType # 18 +""" +Czech +""" +DANISH: Language.ValueType # 19 +""" +Danish +""" +SIMPLIFIED_CHINESE: Language.ValueType # 30 +""" +Simplified Chinese (experimental) +""" +TRADITIONAL_CHINESE: Language.ValueType # 31 +""" +Traditional Chinese (experimental) +""" +global___Language = Language + +@typing.final +class DeviceUIConfig(google.protobuf.message.Message): + """ + Protobuf structures for device-ui persistency + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + class _GpsCoordinateFormat: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + + class _GpsCoordinateFormatEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[DeviceUIConfig._GpsCoordinateFormat.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + DEC: DeviceUIConfig._GpsCoordinateFormat.ValueType # 0 + """ + GPS coordinates are displayed in the normal decimal degrees format: + DD.DDDDDD DDD.DDDDDD + """ + DMS: DeviceUIConfig._GpsCoordinateFormat.ValueType # 1 + """ + GPS coordinates are displayed in the degrees minutes seconds format: + DD°MM'SS"C DDD°MM'SS"C, where C is the compass point representing the locations quadrant + """ + UTM: DeviceUIConfig._GpsCoordinateFormat.ValueType # 2 + """ + Universal Transverse Mercator format: + ZZB EEEEEE NNNNNNN, where Z is zone, B is band, E is easting, N is northing + """ + MGRS: DeviceUIConfig._GpsCoordinateFormat.ValueType # 3 + """ + Military Grid Reference System format: + ZZB CD EEEEE NNNNN, where Z is zone, B is band, C is the east 100k square, D is the north 100k square, + E is easting, N is northing + """ + OLC: DeviceUIConfig._GpsCoordinateFormat.ValueType # 4 + """ + Open Location Code (aka Plus Codes). + """ + OSGR: DeviceUIConfig._GpsCoordinateFormat.ValueType # 5 + """ + Ordnance Survey Grid Reference (the National Grid System of the UK). + Format: AB EEEEE NNNNN, where A is the east 100k square, B is the north 100k square, + E is the easting, N is the northing + """ + MLS: DeviceUIConfig._GpsCoordinateFormat.ValueType # 6 + """ + Maidenhead Locator System + Described here: https://en.wikipedia.org/wiki/Maidenhead_Locator_System + """ + + class GpsCoordinateFormat(_GpsCoordinateFormat, metaclass=_GpsCoordinateFormatEnumTypeWrapper): + """ + How the GPS coordinates are displayed on the OLED screen. + """ + + DEC: DeviceUIConfig.GpsCoordinateFormat.ValueType # 0 + """ + GPS coordinates are displayed in the normal decimal degrees format: + DD.DDDDDD DDD.DDDDDD + """ + DMS: DeviceUIConfig.GpsCoordinateFormat.ValueType # 1 + """ + GPS coordinates are displayed in the degrees minutes seconds format: + DD°MM'SS"C DDD°MM'SS"C, where C is the compass point representing the locations quadrant + """ + UTM: DeviceUIConfig.GpsCoordinateFormat.ValueType # 2 + """ + Universal Transverse Mercator format: + ZZB EEEEEE NNNNNNN, where Z is zone, B is band, E is easting, N is northing + """ + MGRS: DeviceUIConfig.GpsCoordinateFormat.ValueType # 3 + """ + Military Grid Reference System format: + ZZB CD EEEEE NNNNN, where Z is zone, B is band, C is the east 100k square, D is the north 100k square, + E is easting, N is northing + """ + OLC: DeviceUIConfig.GpsCoordinateFormat.ValueType # 4 + """ + Open Location Code (aka Plus Codes). + """ + OSGR: DeviceUIConfig.GpsCoordinateFormat.ValueType # 5 + """ + Ordnance Survey Grid Reference (the National Grid System of the UK). + Format: AB EEEEE NNNNN, where A is the east 100k square, B is the north 100k square, + E is the easting, N is the northing + """ + MLS: DeviceUIConfig.GpsCoordinateFormat.ValueType # 6 + """ + Maidenhead Locator System + Described here: https://en.wikipedia.org/wiki/Maidenhead_Locator_System + """ + + VERSION_FIELD_NUMBER: builtins.int + SCREEN_BRIGHTNESS_FIELD_NUMBER: builtins.int + SCREEN_TIMEOUT_FIELD_NUMBER: builtins.int + SCREEN_LOCK_FIELD_NUMBER: builtins.int + SETTINGS_LOCK_FIELD_NUMBER: builtins.int + PIN_CODE_FIELD_NUMBER: builtins.int + THEME_FIELD_NUMBER: builtins.int + ALERT_ENABLED_FIELD_NUMBER: builtins.int + BANNER_ENABLED_FIELD_NUMBER: builtins.int + RING_TONE_ID_FIELD_NUMBER: builtins.int + LANGUAGE_FIELD_NUMBER: builtins.int + NODE_FILTER_FIELD_NUMBER: builtins.int + NODE_HIGHLIGHT_FIELD_NUMBER: builtins.int + CALIBRATION_DATA_FIELD_NUMBER: builtins.int + MAP_DATA_FIELD_NUMBER: builtins.int + COMPASS_MODE_FIELD_NUMBER: builtins.int + SCREEN_RGB_COLOR_FIELD_NUMBER: builtins.int + IS_CLOCKFACE_ANALOG_FIELD_NUMBER: builtins.int + GPS_FORMAT_FIELD_NUMBER: builtins.int + version: builtins.int + """ + A version integer used to invalidate saved files when we make incompatible changes. + """ + screen_brightness: builtins.int + """ + TFT display brightness 1..255 + """ + screen_timeout: builtins.int + """ + Screen timeout 0..900 + """ + screen_lock: builtins.bool + """ + Screen/Settings lock enabled + """ + settings_lock: builtins.bool + pin_code: builtins.int + theme: global___Theme.ValueType + """ + Color theme + """ + alert_enabled: builtins.bool + """ + Audible message, banner and ring tone + """ + banner_enabled: builtins.bool + ring_tone_id: builtins.int + language: global___Language.ValueType + """ + Localization + """ + calibration_data: builtins.bytes + """ + 8 integers for screen calibration data + """ + compass_mode: global___CompassMode.ValueType + """ + Compass mode + """ + screen_rgb_color: builtins.int + """ + RGB color for BaseUI + 0xRRGGBB format, e.g. 0xFF0000 for red + """ + is_clockface_analog: builtins.bool + """ + Clockface analog style + true for analog clockface, false for digital clockface + """ + gps_format: global___DeviceUIConfig.GpsCoordinateFormat.ValueType + """ + How the GPS coordinates are formatted on the OLED screen. + """ + @property + def node_filter(self) -> global___NodeFilter: + """ + Node list filter + """ + + @property + def node_highlight(self) -> global___NodeHighlight: + """ + Node list highlightening + """ + + @property + def map_data(self) -> global___Map: + """ + Map related data + """ + + def __init__( + self, + *, + version: builtins.int = ..., + screen_brightness: builtins.int = ..., + screen_timeout: builtins.int = ..., + screen_lock: builtins.bool = ..., + settings_lock: builtins.bool = ..., + pin_code: builtins.int = ..., + theme: global___Theme.ValueType = ..., + alert_enabled: builtins.bool = ..., + banner_enabled: builtins.bool = ..., + ring_tone_id: builtins.int = ..., + language: global___Language.ValueType = ..., + node_filter: global___NodeFilter | None = ..., + node_highlight: global___NodeHighlight | None = ..., + calibration_data: builtins.bytes = ..., + map_data: global___Map | None = ..., + compass_mode: global___CompassMode.ValueType = ..., + screen_rgb_color: builtins.int = ..., + is_clockface_analog: builtins.bool = ..., + gps_format: global___DeviceUIConfig.GpsCoordinateFormat.ValueType = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["map_data", b"map_data", "node_filter", b"node_filter", "node_highlight", b"node_highlight"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["alert_enabled", b"alert_enabled", "banner_enabled", b"banner_enabled", "calibration_data", b"calibration_data", "compass_mode", b"compass_mode", "gps_format", b"gps_format", "is_clockface_analog", b"is_clockface_analog", "language", b"language", "map_data", b"map_data", "node_filter", b"node_filter", "node_highlight", b"node_highlight", "pin_code", b"pin_code", "ring_tone_id", b"ring_tone_id", "screen_brightness", b"screen_brightness", "screen_lock", b"screen_lock", "screen_rgb_color", b"screen_rgb_color", "screen_timeout", b"screen_timeout", "settings_lock", b"settings_lock", "theme", b"theme", "version", b"version"]) -> None: ... + +global___DeviceUIConfig = DeviceUIConfig + +@typing.final +class NodeFilter(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + UNKNOWN_SWITCH_FIELD_NUMBER: builtins.int + OFFLINE_SWITCH_FIELD_NUMBER: builtins.int + PUBLIC_KEY_SWITCH_FIELD_NUMBER: builtins.int + HOPS_AWAY_FIELD_NUMBER: builtins.int + POSITION_SWITCH_FIELD_NUMBER: builtins.int + NODE_NAME_FIELD_NUMBER: builtins.int + CHANNEL_FIELD_NUMBER: builtins.int + unknown_switch: builtins.bool + """ + Filter unknown nodes + """ + offline_switch: builtins.bool + """ + Filter offline nodes + """ + public_key_switch: builtins.bool + """ + Filter nodes w/o public key + """ + hops_away: builtins.int + """ + Filter based on hops away + """ + position_switch: builtins.bool + """ + Filter nodes w/o position + """ + node_name: builtins.str + """ + Filter nodes by matching name string + """ + channel: builtins.int + """ + Filter based on channel + """ + def __init__( + self, + *, + unknown_switch: builtins.bool = ..., + offline_switch: builtins.bool = ..., + public_key_switch: builtins.bool = ..., + hops_away: builtins.int = ..., + position_switch: builtins.bool = ..., + node_name: builtins.str = ..., + channel: builtins.int = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["channel", b"channel", "hops_away", b"hops_away", "node_name", b"node_name", "offline_switch", b"offline_switch", "position_switch", b"position_switch", "public_key_switch", b"public_key_switch", "unknown_switch", b"unknown_switch"]) -> None: ... + +global___NodeFilter = NodeFilter + +@typing.final +class NodeHighlight(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + CHAT_SWITCH_FIELD_NUMBER: builtins.int + POSITION_SWITCH_FIELD_NUMBER: builtins.int + TELEMETRY_SWITCH_FIELD_NUMBER: builtins.int + IAQ_SWITCH_FIELD_NUMBER: builtins.int + NODE_NAME_FIELD_NUMBER: builtins.int + chat_switch: builtins.bool + """ + Hightlight nodes w/ active chat + """ + position_switch: builtins.bool + """ + Highlight nodes w/ position + """ + telemetry_switch: builtins.bool + """ + Highlight nodes w/ telemetry data + """ + iaq_switch: builtins.bool + """ + Highlight nodes w/ iaq data + """ + node_name: builtins.str + """ + Highlight nodes by matching name string + """ + def __init__( + self, + *, + chat_switch: builtins.bool = ..., + position_switch: builtins.bool = ..., + telemetry_switch: builtins.bool = ..., + iaq_switch: builtins.bool = ..., + node_name: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["chat_switch", b"chat_switch", "iaq_switch", b"iaq_switch", "node_name", b"node_name", "position_switch", b"position_switch", "telemetry_switch", b"telemetry_switch"]) -> None: ... + +global___NodeHighlight = NodeHighlight + +@typing.final +class GeoPoint(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ZOOM_FIELD_NUMBER: builtins.int + LATITUDE_FIELD_NUMBER: builtins.int + LONGITUDE_FIELD_NUMBER: builtins.int + zoom: builtins.int + """ + Zoom level + """ + latitude: builtins.int + """ + Coordinate: latitude + """ + longitude: builtins.int + """ + Coordinate: longitude + """ + def __init__( + self, + *, + zoom: builtins.int = ..., + latitude: builtins.int = ..., + longitude: builtins.int = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["latitude", b"latitude", "longitude", b"longitude", "zoom", b"zoom"]) -> None: ... + +global___GeoPoint = GeoPoint + +@typing.final +class Map(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + HOME_FIELD_NUMBER: builtins.int + STYLE_FIELD_NUMBER: builtins.int + FOLLOW_GPS_FIELD_NUMBER: builtins.int + style: builtins.str + """ + Map tile style + """ + follow_gps: builtins.bool + """ + Map scroll follows GPS + """ + @property + def home(self) -> global___GeoPoint: + """ + Home coordinates + """ + + def __init__( + self, + *, + home: global___GeoPoint | None = ..., + style: builtins.str = ..., + follow_gps: builtins.bool = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["home", b"home"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["follow_gps", b"follow_gps", "home", b"home", "style", b"style"]) -> None: ... + +global___Map = Map diff --git a/meshtastic/protobuf/deviceonly_pb2.py b/meshtastic/protobuf/deviceonly_pb2.py new file mode 100644 index 00000000..06af5bcb --- /dev/null +++ b/meshtastic/protobuf/deviceonly_pb2.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: meshtastic/protobuf/deviceonly.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from meshtastic.protobuf import channel_pb2 as meshtastic_dot_protobuf_dot_channel__pb2 +from meshtastic.protobuf import config_pb2 as meshtastic_dot_protobuf_dot_config__pb2 +from meshtastic.protobuf import localonly_pb2 as meshtastic_dot_protobuf_dot_localonly__pb2 +from meshtastic.protobuf import mesh_pb2 as meshtastic_dot_protobuf_dot_mesh__pb2 +from meshtastic.protobuf import telemetry_pb2 as meshtastic_dot_protobuf_dot_telemetry__pb2 +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\"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\x08b\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.deviceonly_pb2', _globals) +if _descriptor._USE_C_DESCRIPTORS == False: + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\nDeviceOnlyZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000\222?\013\302\001\010' + _USERLITE.fields_by_name['macaddr']._options = None + _USERLITE.fields_by_name['macaddr']._serialized_options = b'\030\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' + _NODEDATABASE.fields_by_name['nodes']._options = None + _NODEDATABASE.fields_by_name['nodes']._serialized_options = b'\222?\'\222\001$std::vector' + _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 +# @@protoc_insertion_point(module_scope) diff --git a/meshtastic/protobuf/deviceonly_pb2.pyi b/meshtastic/protobuf/deviceonly_pb2.pyi new file mode 100644 index 00000000..684a7fa3 --- /dev/null +++ b/meshtastic/protobuf/deviceonly_pb2.pyi @@ -0,0 +1,458 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" + +import builtins +import collections.abc +import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.message +import meshtastic.protobuf.channel_pb2 +import meshtastic.protobuf.config_pb2 +import meshtastic.protobuf.localonly_pb2 +import meshtastic.protobuf.mesh_pb2 +import meshtastic.protobuf.telemetry_pb2 +import typing + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing.final +class PositionLite(google.protobuf.message.Message): + """ + Position with static location information only for NodeDBLite + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + LATITUDE_I_FIELD_NUMBER: builtins.int + LONGITUDE_I_FIELD_NUMBER: builtins.int + ALTITUDE_FIELD_NUMBER: builtins.int + TIME_FIELD_NUMBER: builtins.int + LOCATION_SOURCE_FIELD_NUMBER: builtins.int + latitude_i: builtins.int + """ + The new preferred location encoding, multiply by 1e-7 to get degrees + in floating point + """ + longitude_i: builtins.int + """ + TODO: REPLACE + """ + altitude: builtins.int + """ + In meters above MSL (but see issue #359) + """ + time: builtins.int + """ + This is usually not sent over the mesh (to save space), but it is sent + from the phone so that the local device can set its RTC If it is sent over + the mesh (because there are devices on the mesh without GPS), it will only + be sent by devices which has a hardware GPS clock. + seconds since 1970 + """ + location_source: meshtastic.protobuf.mesh_pb2.Position.LocSource.ValueType + """ + TODO: REPLACE + """ + def __init__( + self, + *, + latitude_i: builtins.int = ..., + longitude_i: builtins.int = ..., + altitude: builtins.int = ..., + time: builtins.int = ..., + location_source: meshtastic.protobuf.mesh_pb2.Position.LocSource.ValueType = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["altitude", b"altitude", "latitude_i", b"latitude_i", "location_source", b"location_source", "longitude_i", b"longitude_i", "time", b"time"]) -> None: ... + +global___PositionLite = PositionLite + +@typing.final +class UserLite(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + MACADDR_FIELD_NUMBER: builtins.int + LONG_NAME_FIELD_NUMBER: builtins.int + SHORT_NAME_FIELD_NUMBER: builtins.int + HW_MODEL_FIELD_NUMBER: builtins.int + IS_LICENSED_FIELD_NUMBER: builtins.int + ROLE_FIELD_NUMBER: builtins.int + PUBLIC_KEY_FIELD_NUMBER: builtins.int + IS_UNMESSAGABLE_FIELD_NUMBER: builtins.int + macaddr: builtins.bytes + """ + This is the addr of the radio. + """ + long_name: builtins.str + """ + A full name for this user, i.e. "Kevin Hester" + """ + short_name: builtins.str + """ + A VERY short name, ideally two characters. + Suitable for a tiny OLED screen + """ + hw_model: meshtastic.protobuf.mesh_pb2.HardwareModel.ValueType + """ + TBEAM, HELTEC, etc... + Starting in 1.2.11 moved to hw_model enum in the NodeInfo object. + Apps will still need the string here for older builds + (so OTA update can find the right image), but if the enum is available it will be used instead. + """ + is_licensed: builtins.bool + """ + In some regions Ham radio operators have different bandwidth limitations than others. + If this user is a licensed operator, set this flag. + Also, "long_name" should be their licence number. + """ + role: meshtastic.protobuf.config_pb2.Config.DeviceConfig.Role.ValueType + """ + Indicates that the user's role in the mesh + """ + public_key: builtins.bytes + """ + The public key of the user's device. + This is sent out to other nodes on the mesh to allow them to compute a shared secret key. + """ + is_unmessagable: builtins.bool + """ + Whether or not the node can be messaged + """ + def __init__( + self, + *, + macaddr: builtins.bytes = ..., + long_name: builtins.str = ..., + short_name: builtins.str = ..., + hw_model: meshtastic.protobuf.mesh_pb2.HardwareModel.ValueType = ..., + is_licensed: builtins.bool = ..., + role: meshtastic.protobuf.config_pb2.Config.DeviceConfig.Role.ValueType = ..., + public_key: builtins.bytes = ..., + is_unmessagable: builtins.bool | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["_is_unmessagable", b"_is_unmessagable", "is_unmessagable", b"is_unmessagable"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["_is_unmessagable", b"_is_unmessagable", "hw_model", b"hw_model", "is_licensed", b"is_licensed", "is_unmessagable", b"is_unmessagable", "long_name", b"long_name", "macaddr", b"macaddr", "public_key", b"public_key", "role", b"role", "short_name", b"short_name"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["_is_unmessagable", b"_is_unmessagable"]) -> typing.Literal["is_unmessagable"] | None: ... + +global___UserLite = UserLite + +@typing.final +class NodeInfoLite(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + NUM_FIELD_NUMBER: builtins.int + USER_FIELD_NUMBER: builtins.int + POSITION_FIELD_NUMBER: builtins.int + SNR_FIELD_NUMBER: builtins.int + LAST_HEARD_FIELD_NUMBER: builtins.int + DEVICE_METRICS_FIELD_NUMBER: builtins.int + CHANNEL_FIELD_NUMBER: builtins.int + VIA_MQTT_FIELD_NUMBER: builtins.int + HOPS_AWAY_FIELD_NUMBER: builtins.int + IS_FAVORITE_FIELD_NUMBER: builtins.int + IS_IGNORED_FIELD_NUMBER: builtins.int + NEXT_HOP_FIELD_NUMBER: builtins.int + BITFIELD_FIELD_NUMBER: builtins.int + num: builtins.int + """ + The node number + """ + snr: builtins.float + """ + Returns the Signal-to-noise ratio (SNR) of the last received message, + as measured by the receiver. Return SNR of the last received message in dB + """ + last_heard: builtins.int + """ + Set to indicate the last time we received a packet from this node + """ + channel: builtins.int + """ + local channel index we heard that node on. Only populated if its not the default channel. + """ + via_mqtt: builtins.bool + """ + True if we witnessed the node over MQTT instead of LoRA transport + """ + hops_away: builtins.int + """ + Number of hops away from us this node is (0 if direct neighbor) + """ + is_favorite: builtins.bool + """ + True if node is in our favorites list + Persists between NodeDB internal clean ups + """ + is_ignored: builtins.bool + """ + True if node is in our ignored list + Persists between NodeDB internal clean ups + """ + next_hop: builtins.int + """ + Last byte of the node number of the node that should be used as the next hop to reach this node. + """ + bitfield: builtins.int + """ + Bitfield for storing booleans. + LSB 0 is_key_manually_verified + """ + @property + def user(self) -> global___UserLite: + """ + The user info for this node + """ + + @property + def position(self) -> global___PositionLite: + """ + This position data. Note: before 1.2.14 we would also store the last time we've heard from this node in position.time, that is no longer true. + Position.time now indicates the last time we received a POSITION from that node. + """ + + @property + def device_metrics(self) -> meshtastic.protobuf.telemetry_pb2.DeviceMetrics: + """ + The latest device metrics for the node. + """ + + def __init__( + self, + *, + num: builtins.int = ..., + user: global___UserLite | None = ..., + position: global___PositionLite | None = ..., + snr: builtins.float = ..., + last_heard: builtins.int = ..., + device_metrics: meshtastic.protobuf.telemetry_pb2.DeviceMetrics | None = ..., + channel: builtins.int = ..., + via_mqtt: builtins.bool = ..., + hops_away: builtins.int | None = ..., + is_favorite: builtins.bool = ..., + is_ignored: builtins.bool = ..., + next_hop: builtins.int = ..., + bitfield: builtins.int = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["_hops_away", b"_hops_away", "device_metrics", b"device_metrics", "hops_away", b"hops_away", "position", b"position", "user", b"user"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["_hops_away", b"_hops_away", "bitfield", b"bitfield", "channel", b"channel", "device_metrics", b"device_metrics", "hops_away", b"hops_away", "is_favorite", b"is_favorite", "is_ignored", b"is_ignored", "last_heard", b"last_heard", "next_hop", b"next_hop", "num", b"num", "position", b"position", "snr", b"snr", "user", b"user", "via_mqtt", b"via_mqtt"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["_hops_away", b"_hops_away"]) -> typing.Literal["hops_away"] | None: ... + +global___NodeInfoLite = NodeInfoLite + +@typing.final +class DeviceState(google.protobuf.message.Message): + """ + This message is never sent over the wire, but it is used for serializing DB + state to flash in the device code + FIXME, since we write this each time we enter deep sleep (and have infinite + flash) it would be better to use some sort of append only data structure for + the receive queue and use the preferences store for the other stuff + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + MY_NODE_FIELD_NUMBER: builtins.int + OWNER_FIELD_NUMBER: builtins.int + RECEIVE_QUEUE_FIELD_NUMBER: builtins.int + VERSION_FIELD_NUMBER: builtins.int + RX_TEXT_MESSAGE_FIELD_NUMBER: builtins.int + NO_SAVE_FIELD_NUMBER: builtins.int + DID_GPS_RESET_FIELD_NUMBER: builtins.int + RX_WAYPOINT_FIELD_NUMBER: builtins.int + NODE_REMOTE_HARDWARE_PINS_FIELD_NUMBER: builtins.int + version: builtins.int + """ + A version integer used to invalidate old save files when we make + incompatible changes This integer is set at build time and is private to + NodeDB.cpp in the device code. + """ + no_save: builtins.bool + """ + Used only during development. + Indicates developer is testing and changes should never be saved to flash. + Deprecated in 2.3.1 + """ + did_gps_reset: builtins.bool + """ + Previously used to manage GPS factory resets. + Deprecated in 2.5.23 + """ + @property + def my_node(self) -> meshtastic.protobuf.mesh_pb2.MyNodeInfo: + """ + Read only settings/info about this node + """ + + @property + def owner(self) -> meshtastic.protobuf.mesh_pb2.User: + """ + My owner info + """ + + @property + def receive_queue(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[meshtastic.protobuf.mesh_pb2.MeshPacket]: + """ + Received packets saved for delivery to the phone + """ + + @property + def rx_text_message(self) -> meshtastic.protobuf.mesh_pb2.MeshPacket: + """ + We keep the last received text message (only) stored in the device flash, + so we can show it on the screen. + Might be null + """ + + @property + def rx_waypoint(self) -> meshtastic.protobuf.mesh_pb2.MeshPacket: + """ + We keep the last received waypoint stored in the device flash, + so we can show it on the screen. + Might be null + """ + + @property + def node_remote_hardware_pins(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[meshtastic.protobuf.mesh_pb2.NodeRemoteHardwarePin]: + """ + The mesh's nodes with their available gpio pins for RemoteHardware module + """ + + def __init__( + self, + *, + my_node: meshtastic.protobuf.mesh_pb2.MyNodeInfo | None = ..., + owner: meshtastic.protobuf.mesh_pb2.User | None = ..., + receive_queue: collections.abc.Iterable[meshtastic.protobuf.mesh_pb2.MeshPacket] | None = ..., + version: builtins.int = ..., + rx_text_message: meshtastic.protobuf.mesh_pb2.MeshPacket | None = ..., + no_save: builtins.bool = ..., + did_gps_reset: builtins.bool = ..., + rx_waypoint: meshtastic.protobuf.mesh_pb2.MeshPacket | None = ..., + node_remote_hardware_pins: collections.abc.Iterable[meshtastic.protobuf.mesh_pb2.NodeRemoteHardwarePin] | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["my_node", b"my_node", "owner", b"owner", "rx_text_message", b"rx_text_message", "rx_waypoint", b"rx_waypoint"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["did_gps_reset", b"did_gps_reset", "my_node", b"my_node", "no_save", b"no_save", "node_remote_hardware_pins", b"node_remote_hardware_pins", "owner", b"owner", "receive_queue", b"receive_queue", "rx_text_message", b"rx_text_message", "rx_waypoint", b"rx_waypoint", "version", b"version"]) -> None: ... + +global___DeviceState = DeviceState + +@typing.final +class NodeDatabase(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + VERSION_FIELD_NUMBER: builtins.int + NODES_FIELD_NUMBER: builtins.int + version: builtins.int + """ + A version integer used to invalidate old save files when we make + incompatible changes This integer is set at build time and is private to + NodeDB.cpp in the device code. + """ + @property + def nodes(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___NodeInfoLite]: + """ + New lite version of NodeDB to decrease memory footprint + """ + + def __init__( + self, + *, + version: builtins.int = ..., + nodes: collections.abc.Iterable[global___NodeInfoLite] | None = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["nodes", b"nodes", "version", b"version"]) -> None: ... + +global___NodeDatabase = NodeDatabase + +@typing.final +class ChannelFile(google.protobuf.message.Message): + """ + The on-disk saved channels + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + CHANNELS_FIELD_NUMBER: builtins.int + VERSION_FIELD_NUMBER: builtins.int + version: builtins.int + """ + A version integer used to invalidate old save files when we make + incompatible changes This integer is set at build time and is private to + NodeDB.cpp in the device code. + """ + @property + def channels(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[meshtastic.protobuf.channel_pb2.Channel]: + """ + The channels our node knows about + """ + + def __init__( + self, + *, + channels: collections.abc.Iterable[meshtastic.protobuf.channel_pb2.Channel] | None = ..., + version: builtins.int = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["channels", b"channels", "version", b"version"]) -> None: ... + +global___ChannelFile = ChannelFile + +@typing.final +class BackupPreferences(google.protobuf.message.Message): + """ + The on-disk backup of the node's preferences + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + VERSION_FIELD_NUMBER: builtins.int + TIMESTAMP_FIELD_NUMBER: builtins.int + CONFIG_FIELD_NUMBER: builtins.int + MODULE_CONFIG_FIELD_NUMBER: builtins.int + CHANNELS_FIELD_NUMBER: builtins.int + OWNER_FIELD_NUMBER: builtins.int + version: builtins.int + """ + The version of the backup + """ + timestamp: builtins.int + """ + The timestamp of the backup (if node has time) + """ + @property + def config(self) -> meshtastic.protobuf.localonly_pb2.LocalConfig: + """ + The node's configuration + """ + + @property + def module_config(self) -> meshtastic.protobuf.localonly_pb2.LocalModuleConfig: + """ + The node's module configuration + """ + + @property + def channels(self) -> global___ChannelFile: + """ + The node's channels + """ + + @property + def owner(self) -> meshtastic.protobuf.mesh_pb2.User: + """ + The node's user (owner) information + """ + + def __init__( + self, + *, + version: builtins.int = ..., + timestamp: builtins.int = ..., + config: meshtastic.protobuf.localonly_pb2.LocalConfig | None = ..., + module_config: meshtastic.protobuf.localonly_pb2.LocalModuleConfig | None = ..., + channels: global___ChannelFile | None = ..., + owner: meshtastic.protobuf.mesh_pb2.User | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["channels", b"channels", "config", b"config", "module_config", b"module_config", "owner", b"owner"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["channels", b"channels", "config", b"config", "module_config", b"module_config", "owner", b"owner", "timestamp", b"timestamp", "version", b"version"]) -> None: ... + +global___BackupPreferences = BackupPreferences diff --git a/meshtastic/protobuf/interdevice_pb2.py b/meshtastic/protobuf/interdevice_pb2.py new file mode 100644 index 00000000..dda2ad63 --- /dev/null +++ b/meshtastic/protobuf/interdevice_pb2.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: meshtastic/protobuf/interdevice.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +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') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.interdevice_pb2', _globals) +if _descriptor._USE_C_DESCRIPTORS == False: + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\021InterdeviceProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' + _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 +# @@protoc_insertion_point(module_scope) diff --git a/meshtastic/protobuf/interdevice_pb2.pyi b/meshtastic/protobuf/interdevice_pb2.pyi new file mode 100644 index 00000000..6e70f9e2 --- /dev/null +++ b/meshtastic/protobuf/interdevice_pb2.pyi @@ -0,0 +1,105 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" + +import builtins +import google.protobuf.descriptor +import google.protobuf.internal.enum_type_wrapper +import google.protobuf.message +import sys +import typing + +if sys.version_info >= (3, 10): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +class _MessageType: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _MessageTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_MessageType.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + ACK: _MessageType.ValueType # 0 + COLLECT_INTERVAL: _MessageType.ValueType # 160 + """in ms""" + BEEP_ON: _MessageType.ValueType # 161 + """duration ms""" + BEEP_OFF: _MessageType.ValueType # 162 + """cancel prematurely""" + SHUTDOWN: _MessageType.ValueType # 163 + POWER_ON: _MessageType.ValueType # 164 + SCD41_TEMP: _MessageType.ValueType # 176 + SCD41_HUMIDITY: _MessageType.ValueType # 177 + SCD41_CO2: _MessageType.ValueType # 178 + AHT20_TEMP: _MessageType.ValueType # 179 + AHT20_HUMIDITY: _MessageType.ValueType # 180 + TVOC_INDEX: _MessageType.ValueType # 181 + +class MessageType(_MessageType, metaclass=_MessageTypeEnumTypeWrapper): + """encapsulate up to 1k of NMEA string data""" + +ACK: MessageType.ValueType # 0 +COLLECT_INTERVAL: MessageType.ValueType # 160 +"""in ms""" +BEEP_ON: MessageType.ValueType # 161 +"""duration ms""" +BEEP_OFF: MessageType.ValueType # 162 +"""cancel prematurely""" +SHUTDOWN: MessageType.ValueType # 163 +POWER_ON: MessageType.ValueType # 164 +SCD41_TEMP: MessageType.ValueType # 176 +SCD41_HUMIDITY: MessageType.ValueType # 177 +SCD41_CO2: MessageType.ValueType # 178 +AHT20_TEMP: MessageType.ValueType # 179 +AHT20_HUMIDITY: MessageType.ValueType # 180 +TVOC_INDEX: MessageType.ValueType # 181 +global___MessageType = MessageType + +@typing.final +class SensorData(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + TYPE_FIELD_NUMBER: builtins.int + FLOAT_VALUE_FIELD_NUMBER: builtins.int + UINT32_VALUE_FIELD_NUMBER: builtins.int + type: global___MessageType.ValueType + """The message type""" + float_value: builtins.float + uint32_value: builtins.int + def __init__( + self, + *, + type: global___MessageType.ValueType = ..., + float_value: builtins.float = ..., + uint32_value: builtins.int = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["data", b"data", "float_value", b"float_value", "uint32_value", b"uint32_value"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["data", b"data", "float_value", b"float_value", "type", b"type", "uint32_value", b"uint32_value"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["data", b"data"]) -> typing.Literal["float_value", "uint32_value"] | None: ... + +global___SensorData = SensorData + +@typing.final +class InterdeviceMessage(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + NMEA_FIELD_NUMBER: builtins.int + SENSOR_FIELD_NUMBER: builtins.int + nmea: builtins.str + @property + def sensor(self) -> global___SensorData: ... + def __init__( + self, + *, + nmea: builtins.str = ..., + sensor: global___SensorData | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["data", b"data", "nmea", b"nmea", "sensor", b"sensor"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["data", b"data", "nmea", b"nmea", "sensor", b"sensor"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["data", b"data"]) -> typing.Literal["nmea", "sensor"] | None: ... + +global___InterdeviceMessage = InterdeviceMessage diff --git a/meshtastic/protobuf/localonly_pb2.py b/meshtastic/protobuf/localonly_pb2.py new file mode 100644 index 00000000..985aaddb --- /dev/null +++ b/meshtastic/protobuf/localonly_pb2.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: meshtastic/protobuf/localonly.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from meshtastic.protobuf import config_pb2 as meshtastic_dot_protobuf_dot_config__pb2 +from meshtastic.protobuf import module_config_pb2 as meshtastic_dot_protobuf_dot_module__config__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n#meshtastic/protobuf/localonly.proto\x12\x13meshtastic.protobuf\x1a meshtastic/protobuf/config.proto\x1a\'meshtastic/protobuf/module_config.proto\"\xfa\x03\n\x0bLocalConfig\x12\x38\n\x06\x64\x65vice\x18\x01 \x01(\x0b\x32(.meshtastic.protobuf.Config.DeviceConfig\x12<\n\x08position\x18\x02 \x01(\x0b\x32*.meshtastic.protobuf.Config.PositionConfig\x12\x36\n\x05power\x18\x03 \x01(\x0b\x32\'.meshtastic.protobuf.Config.PowerConfig\x12:\n\x07network\x18\x04 \x01(\x0b\x32).meshtastic.protobuf.Config.NetworkConfig\x12:\n\x07\x64isplay\x18\x05 \x01(\x0b\x32).meshtastic.protobuf.Config.DisplayConfig\x12\x34\n\x04lora\x18\x06 \x01(\x0b\x32&.meshtastic.protobuf.Config.LoRaConfig\x12>\n\tbluetooth\x18\x07 \x01(\x0b\x32+.meshtastic.protobuf.Config.BluetoothConfig\x12\x0f\n\x07version\x18\x08 \x01(\r\x12<\n\x08security\x18\t \x01(\x0b\x32*.meshtastic.protobuf.Config.SecurityConfig\"\xf0\x07\n\x11LocalModuleConfig\x12:\n\x04mqtt\x18\x01 \x01(\x0b\x32,.meshtastic.protobuf.ModuleConfig.MQTTConfig\x12>\n\x06serial\x18\x02 \x01(\x0b\x32..meshtastic.protobuf.ModuleConfig.SerialConfig\x12[\n\x15\x65xternal_notification\x18\x03 \x01(\x0b\x32<.meshtastic.protobuf.ModuleConfig.ExternalNotificationConfig\x12K\n\rstore_forward\x18\x04 \x01(\x0b\x32\x34.meshtastic.protobuf.ModuleConfig.StoreForwardConfig\x12\x45\n\nrange_test\x18\x05 \x01(\x0b\x32\x31.meshtastic.protobuf.ModuleConfig.RangeTestConfig\x12\x44\n\ttelemetry\x18\x06 \x01(\x0b\x32\x31.meshtastic.protobuf.ModuleConfig.TelemetryConfig\x12M\n\x0e\x63\x61nned_message\x18\x07 \x01(\x0b\x32\x35.meshtastic.protobuf.ModuleConfig.CannedMessageConfig\x12<\n\x05\x61udio\x18\t \x01(\x0b\x32-.meshtastic.protobuf.ModuleConfig.AudioConfig\x12O\n\x0fremote_hardware\x18\n \x01(\x0b\x32\x36.meshtastic.protobuf.ModuleConfig.RemoteHardwareConfig\x12K\n\rneighbor_info\x18\x0b \x01(\x0b\x32\x34.meshtastic.protobuf.ModuleConfig.NeighborInfoConfig\x12Q\n\x10\x61mbient_lighting\x18\x0c \x01(\x0b\x32\x37.meshtastic.protobuf.ModuleConfig.AmbientLightingConfig\x12Q\n\x10\x64\x65tection_sensor\x18\r \x01(\x0b\x32\x37.meshtastic.protobuf.ModuleConfig.DetectionSensorConfig\x12\x46\n\npaxcounter\x18\x0e \x01(\x0b\x32\x32.meshtastic.protobuf.ModuleConfig.PaxcounterConfig\x12\x0f\n\x07version\x18\x08 \x01(\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) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.localonly_pb2', _globals) +if _descriptor._USE_C_DESCRIPTORS == False: + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\017LocalOnlyProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' + _globals['_LOCALCONFIG']._serialized_start=136 + _globals['_LOCALCONFIG']._serialized_end=642 + _globals['_LOCALMODULECONFIG']._serialized_start=645 + _globals['_LOCALMODULECONFIG']._serialized_end=1653 +# @@protoc_insertion_point(module_scope) diff --git a/meshtastic/protobuf/localonly_pb2.pyi b/meshtastic/protobuf/localonly_pb2.pyi new file mode 100644 index 00000000..059ef1af --- /dev/null +++ b/meshtastic/protobuf/localonly_pb2.pyi @@ -0,0 +1,228 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" + +import builtins +import google.protobuf.descriptor +import google.protobuf.message +import meshtastic.protobuf.config_pb2 +import meshtastic.protobuf.module_config_pb2 +import typing + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing.final +class LocalConfig(google.protobuf.message.Message): + """ + Protobuf structures common to apponly.proto and deviceonly.proto + This is never sent over the wire, only for local use + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + DEVICE_FIELD_NUMBER: builtins.int + POSITION_FIELD_NUMBER: builtins.int + POWER_FIELD_NUMBER: builtins.int + NETWORK_FIELD_NUMBER: builtins.int + DISPLAY_FIELD_NUMBER: builtins.int + LORA_FIELD_NUMBER: builtins.int + BLUETOOTH_FIELD_NUMBER: builtins.int + VERSION_FIELD_NUMBER: builtins.int + SECURITY_FIELD_NUMBER: builtins.int + version: builtins.int + """ + A version integer used to invalidate old save files when we make + incompatible changes This integer is set at build time and is private to + NodeDB.cpp in the device code. + """ + @property + def device(self) -> meshtastic.protobuf.config_pb2.Config.DeviceConfig: + """ + The part of the config that is specific to the Device + """ + + @property + def position(self) -> meshtastic.protobuf.config_pb2.Config.PositionConfig: + """ + The part of the config that is specific to the GPS Position + """ + + @property + def power(self) -> meshtastic.protobuf.config_pb2.Config.PowerConfig: + """ + The part of the config that is specific to the Power settings + """ + + @property + def network(self) -> meshtastic.protobuf.config_pb2.Config.NetworkConfig: + """ + The part of the config that is specific to the Wifi Settings + """ + + @property + def display(self) -> meshtastic.protobuf.config_pb2.Config.DisplayConfig: + """ + The part of the config that is specific to the Display + """ + + @property + def lora(self) -> meshtastic.protobuf.config_pb2.Config.LoRaConfig: + """ + The part of the config that is specific to the Lora Radio + """ + + @property + def bluetooth(self) -> meshtastic.protobuf.config_pb2.Config.BluetoothConfig: + """ + The part of the config that is specific to the Bluetooth settings + """ + + @property + def security(self) -> meshtastic.protobuf.config_pb2.Config.SecurityConfig: + """ + The part of the config that is specific to Security settings + """ + + def __init__( + self, + *, + device: meshtastic.protobuf.config_pb2.Config.DeviceConfig | None = ..., + position: meshtastic.protobuf.config_pb2.Config.PositionConfig | None = ..., + power: meshtastic.protobuf.config_pb2.Config.PowerConfig | None = ..., + network: meshtastic.protobuf.config_pb2.Config.NetworkConfig | None = ..., + display: meshtastic.protobuf.config_pb2.Config.DisplayConfig | None = ..., + lora: meshtastic.protobuf.config_pb2.Config.LoRaConfig | None = ..., + bluetooth: meshtastic.protobuf.config_pb2.Config.BluetoothConfig | None = ..., + version: builtins.int = ..., + security: meshtastic.protobuf.config_pb2.Config.SecurityConfig | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["bluetooth", b"bluetooth", "device", b"device", "display", b"display", "lora", b"lora", "network", b"network", "position", b"position", "power", b"power", "security", b"security"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["bluetooth", b"bluetooth", "device", b"device", "display", b"display", "lora", b"lora", "network", b"network", "position", b"position", "power", b"power", "security", b"security", "version", b"version"]) -> None: ... + +global___LocalConfig = LocalConfig + +@typing.final +class LocalModuleConfig(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + MQTT_FIELD_NUMBER: builtins.int + SERIAL_FIELD_NUMBER: builtins.int + EXTERNAL_NOTIFICATION_FIELD_NUMBER: builtins.int + STORE_FORWARD_FIELD_NUMBER: builtins.int + RANGE_TEST_FIELD_NUMBER: builtins.int + TELEMETRY_FIELD_NUMBER: builtins.int + CANNED_MESSAGE_FIELD_NUMBER: builtins.int + AUDIO_FIELD_NUMBER: builtins.int + REMOTE_HARDWARE_FIELD_NUMBER: builtins.int + NEIGHBOR_INFO_FIELD_NUMBER: builtins.int + AMBIENT_LIGHTING_FIELD_NUMBER: builtins.int + DETECTION_SENSOR_FIELD_NUMBER: builtins.int + PAXCOUNTER_FIELD_NUMBER: builtins.int + VERSION_FIELD_NUMBER: builtins.int + version: builtins.int + """ + A version integer used to invalidate old save files when we make + incompatible changes This integer is set at build time and is private to + NodeDB.cpp in the device code. + """ + @property + def mqtt(self) -> meshtastic.protobuf.module_config_pb2.ModuleConfig.MQTTConfig: + """ + The part of the config that is specific to the MQTT module + """ + + @property + def serial(self) -> meshtastic.protobuf.module_config_pb2.ModuleConfig.SerialConfig: + """ + The part of the config that is specific to the Serial module + """ + + @property + def external_notification(self) -> meshtastic.protobuf.module_config_pb2.ModuleConfig.ExternalNotificationConfig: + """ + The part of the config that is specific to the ExternalNotification module + """ + + @property + def store_forward(self) -> meshtastic.protobuf.module_config_pb2.ModuleConfig.StoreForwardConfig: + """ + The part of the config that is specific to the Store & Forward module + """ + + @property + def range_test(self) -> meshtastic.protobuf.module_config_pb2.ModuleConfig.RangeTestConfig: + """ + The part of the config that is specific to the RangeTest module + """ + + @property + def telemetry(self) -> meshtastic.protobuf.module_config_pb2.ModuleConfig.TelemetryConfig: + """ + The part of the config that is specific to the Telemetry module + """ + + @property + def canned_message(self) -> meshtastic.protobuf.module_config_pb2.ModuleConfig.CannedMessageConfig: + """ + The part of the config that is specific to the Canned Message module + """ + + @property + def audio(self) -> meshtastic.protobuf.module_config_pb2.ModuleConfig.AudioConfig: + """ + The part of the config that is specific to the Audio module + """ + + @property + def remote_hardware(self) -> meshtastic.protobuf.module_config_pb2.ModuleConfig.RemoteHardwareConfig: + """ + The part of the config that is specific to the Remote Hardware module + """ + + @property + def neighbor_info(self) -> meshtastic.protobuf.module_config_pb2.ModuleConfig.NeighborInfoConfig: + """ + The part of the config that is specific to the Neighbor Info module + """ + + @property + def ambient_lighting(self) -> meshtastic.protobuf.module_config_pb2.ModuleConfig.AmbientLightingConfig: + """ + The part of the config that is specific to the Ambient Lighting module + """ + + @property + def detection_sensor(self) -> meshtastic.protobuf.module_config_pb2.ModuleConfig.DetectionSensorConfig: + """ + The part of the config that is specific to the Detection Sensor module + """ + + @property + def paxcounter(self) -> meshtastic.protobuf.module_config_pb2.ModuleConfig.PaxcounterConfig: + """ + Paxcounter Config + """ + + def __init__( + self, + *, + mqtt: meshtastic.protobuf.module_config_pb2.ModuleConfig.MQTTConfig | None = ..., + serial: meshtastic.protobuf.module_config_pb2.ModuleConfig.SerialConfig | None = ..., + external_notification: meshtastic.protobuf.module_config_pb2.ModuleConfig.ExternalNotificationConfig | None = ..., + store_forward: meshtastic.protobuf.module_config_pb2.ModuleConfig.StoreForwardConfig | None = ..., + range_test: meshtastic.protobuf.module_config_pb2.ModuleConfig.RangeTestConfig | None = ..., + telemetry: meshtastic.protobuf.module_config_pb2.ModuleConfig.TelemetryConfig | None = ..., + canned_message: meshtastic.protobuf.module_config_pb2.ModuleConfig.CannedMessageConfig | None = ..., + audio: meshtastic.protobuf.module_config_pb2.ModuleConfig.AudioConfig | None = ..., + remote_hardware: meshtastic.protobuf.module_config_pb2.ModuleConfig.RemoteHardwareConfig | None = ..., + neighbor_info: meshtastic.protobuf.module_config_pb2.ModuleConfig.NeighborInfoConfig | None = ..., + ambient_lighting: meshtastic.protobuf.module_config_pb2.ModuleConfig.AmbientLightingConfig | None = ..., + detection_sensor: meshtastic.protobuf.module_config_pb2.ModuleConfig.DetectionSensorConfig | None = ..., + paxcounter: meshtastic.protobuf.module_config_pb2.ModuleConfig.PaxcounterConfig | None = ..., + version: builtins.int = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["ambient_lighting", b"ambient_lighting", "audio", b"audio", "canned_message", b"canned_message", "detection_sensor", b"detection_sensor", "external_notification", b"external_notification", "mqtt", b"mqtt", "neighbor_info", b"neighbor_info", "paxcounter", b"paxcounter", "range_test", b"range_test", "remote_hardware", b"remote_hardware", "serial", b"serial", "store_forward", b"store_forward", "telemetry", b"telemetry"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["ambient_lighting", b"ambient_lighting", "audio", b"audio", "canned_message", b"canned_message", "detection_sensor", b"detection_sensor", "external_notification", b"external_notification", "mqtt", b"mqtt", "neighbor_info", b"neighbor_info", "paxcounter", b"paxcounter", "range_test", b"range_test", "remote_hardware", b"remote_hardware", "serial", b"serial", "store_forward", b"store_forward", "telemetry", b"telemetry", "version", b"version"]) -> None: ... + +global___LocalModuleConfig = LocalModuleConfig diff --git a/meshtastic/protobuf/mesh_pb2.py b/meshtastic/protobuf/mesh_pb2.py new file mode 100644 index 00000000..0e6142aa --- /dev/null +++ b/meshtastic/protobuf/mesh_pb2.py @@ -0,0 +1,121 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: meshtastic/protobuf/mesh.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from meshtastic.protobuf import channel_pb2 as meshtastic_dot_protobuf_dot_channel__pb2 +from meshtastic.protobuf import config_pb2 as meshtastic_dot_protobuf_dot_config__pb2 +from meshtastic.protobuf import device_ui_pb2 as meshtastic_dot_protobuf_dot_device__ui__pb2 +from meshtastic.protobuf import module_config_pb2 as meshtastic_dot_protobuf_dot_module__config__pb2 +from meshtastic.protobuf import portnums_pb2 as meshtastic_dot_protobuf_dot_portnums__pb2 +from meshtastic.protobuf import telemetry_pb2 as meshtastic_dot_protobuf_dot_telemetry__pb2 +from meshtastic.protobuf import xmodem_pb2 as meshtastic_dot_protobuf_dot_xmodem__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1emeshtastic/protobuf/mesh.proto\x12\x13meshtastic.protobuf\x1a!meshtastic/protobuf/channel.proto\x1a meshtastic/protobuf/config.proto\x1a#meshtastic/protobuf/device_ui.proto\x1a\'meshtastic/protobuf/module_config.proto\x1a\"meshtastic/protobuf/portnums.proto\x1a#meshtastic/protobuf/telemetry.proto\x1a meshtastic/protobuf/xmodem.proto\"\x99\x07\n\x08Position\x12\x17\n\nlatitude_i\x18\x01 \x01(\x0fH\x00\x88\x01\x01\x12\x18\n\x0blongitude_i\x18\x02 \x01(\x0fH\x01\x88\x01\x01\x12\x15\n\x08\x61ltitude\x18\x03 \x01(\x05H\x02\x88\x01\x01\x12\x0c\n\x04time\x18\x04 \x01(\x07\x12@\n\x0flocation_source\x18\x05 \x01(\x0e\x32\'.meshtastic.protobuf.Position.LocSource\x12@\n\x0f\x61ltitude_source\x18\x06 \x01(\x0e\x32\'.meshtastic.protobuf.Position.AltSource\x12\x11\n\ttimestamp\x18\x07 \x01(\x07\x12\x1f\n\x17timestamp_millis_adjust\x18\x08 \x01(\x05\x12\x19\n\x0c\x61ltitude_hae\x18\t \x01(\x11H\x03\x88\x01\x01\x12(\n\x1b\x61ltitude_geoidal_separation\x18\n \x01(\x11H\x04\x88\x01\x01\x12\x0c\n\x04PDOP\x18\x0b \x01(\r\x12\x0c\n\x04HDOP\x18\x0c \x01(\r\x12\x0c\n\x04VDOP\x18\r \x01(\r\x12\x14\n\x0cgps_accuracy\x18\x0e \x01(\r\x12\x19\n\x0cground_speed\x18\x0f \x01(\rH\x05\x88\x01\x01\x12\x19\n\x0cground_track\x18\x10 \x01(\rH\x06\x88\x01\x01\x12\x13\n\x0b\x66ix_quality\x18\x11 \x01(\r\x12\x10\n\x08\x66ix_type\x18\x12 \x01(\r\x12\x14\n\x0csats_in_view\x18\x13 \x01(\r\x12\x11\n\tsensor_id\x18\x14 \x01(\r\x12\x13\n\x0bnext_update\x18\x15 \x01(\r\x12\x12\n\nseq_number\x18\x16 \x01(\r\x12\x16\n\x0eprecision_bits\x18\x17 \x01(\r\"N\n\tLocSource\x12\r\n\tLOC_UNSET\x10\x00\x12\x0e\n\nLOC_MANUAL\x10\x01\x12\x10\n\x0cLOC_INTERNAL\x10\x02\x12\x10\n\x0cLOC_EXTERNAL\x10\x03\"b\n\tAltSource\x12\r\n\tALT_UNSET\x10\x00\x12\x0e\n\nALT_MANUAL\x10\x01\x12\x10\n\x0c\x41LT_INTERNAL\x10\x02\x12\x10\n\x0c\x41LT_EXTERNAL\x10\x03\x12\x12\n\x0e\x41LT_BAROMETRIC\x10\x04\x42\r\n\x0b_latitude_iB\x0e\n\x0c_longitude_iB\x0b\n\t_altitudeB\x0f\n\r_altitude_haeB\x1e\n\x1c_altitude_geoidal_separationB\x0f\n\r_ground_speedB\x0f\n\r_ground_track\"\x9c\x02\n\x04User\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\tlong_name\x18\x02 \x01(\t\x12\x12\n\nshort_name\x18\x03 \x01(\t\x12\x13\n\x07macaddr\x18\x04 \x01(\x0c\x42\x02\x18\x01\x12\x34\n\x08hw_model\x18\x05 \x01(\x0e\x32\".meshtastic.protobuf.HardwareModel\x12\x13\n\x0bis_licensed\x18\x06 \x01(\x08\x12;\n\x04role\x18\x07 \x01(\x0e\x32-.meshtastic.protobuf.Config.DeviceConfig.Role\x12\x12\n\npublic_key\x18\x08 \x01(\x0c\x12\x1c\n\x0fis_unmessagable\x18\t \x01(\x08H\x00\x88\x01\x01\x42\x12\n\x10_is_unmessagable\"Z\n\x0eRouteDiscovery\x12\r\n\x05route\x18\x01 \x03(\x07\x12\x13\n\x0bsnr_towards\x18\x02 \x03(\x05\x12\x12\n\nroute_back\x18\x03 \x03(\x07\x12\x10\n\x08snr_back\x18\x04 \x03(\x05\"\x96\x04\n\x07Routing\x12<\n\rroute_request\x18\x01 \x01(\x0b\x32#.meshtastic.protobuf.RouteDiscoveryH\x00\x12:\n\x0broute_reply\x18\x02 \x01(\x0b\x32#.meshtastic.protobuf.RouteDiscoveryH\x00\x12:\n\x0c\x65rror_reason\x18\x03 \x01(\x0e\x32\".meshtastic.protobuf.Routing.ErrorH\x00\"\xc9\x02\n\x05\x45rror\x12\x08\n\x04NONE\x10\x00\x12\x0c\n\x08NO_ROUTE\x10\x01\x12\x0b\n\x07GOT_NAK\x10\x02\x12\x0b\n\x07TIMEOUT\x10\x03\x12\x10\n\x0cNO_INTERFACE\x10\x04\x12\x12\n\x0eMAX_RETRANSMIT\x10\x05\x12\x0e\n\nNO_CHANNEL\x10\x06\x12\r\n\tTOO_LARGE\x10\x07\x12\x0f\n\x0bNO_RESPONSE\x10\x08\x12\x14\n\x10\x44UTY_CYCLE_LIMIT\x10\t\x12\x0f\n\x0b\x42\x41\x44_REQUEST\x10 \x12\x12\n\x0eNOT_AUTHORIZED\x10!\x12\x0e\n\nPKI_FAILED\x10\"\x12\x16\n\x12PKI_UNKNOWN_PUBKEY\x10#\x12\x19\n\x15\x41\x44MIN_BAD_SESSION_KEY\x10$\x12!\n\x1d\x41\x44MIN_PUBLIC_KEY_UNAUTHORIZED\x10%\x12\x17\n\x13RATE_LIMIT_EXCEEDED\x10&B\t\n\x07variant\"\xd4\x01\n\x04\x44\x61ta\x12-\n\x07portnum\x18\x01 \x01(\x0e\x32\x1c.meshtastic.protobuf.PortNum\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\x12\x15\n\rwant_response\x18\x03 \x01(\x08\x12\x0c\n\x04\x64\x65st\x18\x04 \x01(\x07\x12\x0e\n\x06source\x18\x05 \x01(\x07\x12\x12\n\nrequest_id\x18\x06 \x01(\x07\x12\x10\n\x08reply_id\x18\x07 \x01(\x07\x12\r\n\x05\x65moji\x18\x08 \x01(\x07\x12\x15\n\x08\x62itfield\x18\t \x01(\rH\x00\x88\x01\x01\x42\x0b\n\t_bitfield\">\n\x0fKeyVerification\x12\r\n\x05nonce\x18\x01 \x01(\x04\x12\r\n\x05hash1\x18\x02 \x01(\x0c\x12\r\n\x05hash2\x18\x03 \x01(\x0c\"\xbc\x01\n\x08Waypoint\x12\n\n\x02id\x18\x01 \x01(\r\x12\x17\n\nlatitude_i\x18\x02 \x01(\x0fH\x00\x88\x01\x01\x12\x18\n\x0blongitude_i\x18\x03 \x01(\x0fH\x01\x88\x01\x01\x12\x0e\n\x06\x65xpire\x18\x04 \x01(\r\x12\x11\n\tlocked_to\x18\x05 \x01(\r\x12\x0c\n\x04name\x18\x06 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x07 \x01(\t\x12\x0c\n\x04icon\x18\x08 \x01(\x07\x42\r\n\x0b_latitude_iB\x0e\n\x0c_longitude_i\"l\n\x16MqttClientProxyMessage\x12\r\n\x05topic\x18\x01 \x01(\t\x12\x0e\n\x04\x64\x61ta\x18\x02 \x01(\x0cH\x00\x12\x0e\n\x04text\x18\x03 \x01(\tH\x00\x12\x10\n\x08retained\x18\x04 \x01(\x08\x42\x11\n\x0fpayload_variant\"\xd9\x07\n\nMeshPacket\x12\x0c\n\x04\x66rom\x18\x01 \x01(\x07\x12\n\n\x02to\x18\x02 \x01(\x07\x12\x0f\n\x07\x63hannel\x18\x03 \x01(\r\x12,\n\x07\x64\x65\x63oded\x18\x04 \x01(\x0b\x32\x19.meshtastic.protobuf.DataH\x00\x12\x13\n\tencrypted\x18\x05 \x01(\x0cH\x00\x12\n\n\x02id\x18\x06 \x01(\x07\x12\x0f\n\x07rx_time\x18\x07 \x01(\x07\x12\x0e\n\x06rx_snr\x18\x08 \x01(\x02\x12\x11\n\thop_limit\x18\t \x01(\r\x12\x10\n\x08want_ack\x18\n \x01(\x08\x12:\n\x08priority\x18\x0b \x01(\x0e\x32(.meshtastic.protobuf.MeshPacket.Priority\x12\x0f\n\x07rx_rssi\x18\x0c \x01(\x05\x12<\n\x07\x64\x65layed\x18\r \x01(\x0e\x32\'.meshtastic.protobuf.MeshPacket.DelayedB\x02\x18\x01\x12\x10\n\x08via_mqtt\x18\x0e \x01(\x08\x12\x11\n\thop_start\x18\x0f \x01(\r\x12\x12\n\npublic_key\x18\x10 \x01(\x0c\x12\x15\n\rpki_encrypted\x18\x11 \x01(\x08\x12\x10\n\x08next_hop\x18\x12 \x01(\r\x12\x12\n\nrelay_node\x18\x13 \x01(\r\x12\x10\n\x08tx_after\x18\x14 \x01(\r\x12O\n\x13transport_mechanism\x18\x15 \x01(\x0e\x32\x32.meshtastic.protobuf.MeshPacket.TransportMechanism\"~\n\x08Priority\x12\t\n\x05UNSET\x10\x00\x12\x07\n\x03MIN\x10\x01\x12\x0e\n\nBACKGROUND\x10\n\x12\x0b\n\x07\x44\x45\x46\x41ULT\x10@\x12\x0c\n\x08RELIABLE\x10\x46\x12\x0c\n\x08RESPONSE\x10P\x12\x08\n\x04HIGH\x10\x64\x12\t\n\x05\x41LERT\x10n\x12\x07\n\x03\x41\x43K\x10x\x12\x07\n\x03MAX\x10\x7f\"B\n\x07\x44\x65layed\x12\x0c\n\x08NO_DELAY\x10\x00\x12\x15\n\x11\x44\x45LAYED_BROADCAST\x10\x01\x12\x12\n\x0e\x44\x45LAYED_DIRECT\x10\x02\"\xcf\x01\n\x12TransportMechanism\x12\x16\n\x12TRANSPORT_INTERNAL\x10\x00\x12\x12\n\x0eTRANSPORT_LORA\x10\x01\x12\x17\n\x13TRANSPORT_LORA_ALT1\x10\x02\x12\x17\n\x13TRANSPORT_LORA_ALT2\x10\x03\x12\x17\n\x13TRANSPORT_LORA_ALT3\x10\x04\x12\x12\n\x0eTRANSPORT_MQTT\x10\x05\x12\x1b\n\x17TRANSPORT_MULTICAST_UDP\x10\x06\x12\x11\n\rTRANSPORT_API\x10\x07\x42\x11\n\x0fpayload_variant\"\xe2\x02\n\x08NodeInfo\x12\x0b\n\x03num\x18\x01 \x01(\r\x12\'\n\x04user\x18\x02 \x01(\x0b\x32\x19.meshtastic.protobuf.User\x12/\n\x08position\x18\x03 \x01(\x0b\x32\x1d.meshtastic.protobuf.Position\x12\x0b\n\x03snr\x18\x04 \x01(\x02\x12\x12\n\nlast_heard\x18\x05 \x01(\x07\x12:\n\x0e\x64\x65vice_metrics\x18\x06 \x01(\x0b\x32\".meshtastic.protobuf.DeviceMetrics\x12\x0f\n\x07\x63hannel\x18\x07 \x01(\r\x12\x10\n\x08via_mqtt\x18\x08 \x01(\x08\x12\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 \n\x18is_key_manually_verified\x18\x0c \x01(\x08\x42\x0c\n\n_hops_away\"\xca\x01\n\nMyNodeInfo\x12\x13\n\x0bmy_node_num\x18\x01 \x01(\r\x12\x14\n\x0creboot_count\x18\x08 \x01(\r\x12\x17\n\x0fmin_app_version\x18\x0b \x01(\r\x12\x11\n\tdevice_id\x18\x0c \x01(\x0c\x12\x0f\n\x07pio_env\x18\r \x01(\t\x12>\n\x10\x66irmware_edition\x18\x0e \x01(\x0e\x32$.meshtastic.protobuf.FirmwareEdition\x12\x14\n\x0cnodedb_count\x18\x0f \x01(\r\"\xc9\x01\n\tLogRecord\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\x0c\n\x04time\x18\x02 \x01(\x07\x12\x0e\n\x06source\x18\x03 \x01(\t\x12\x33\n\x05level\x18\x04 \x01(\x0e\x32$.meshtastic.protobuf.LogRecord.Level\"X\n\x05Level\x12\t\n\x05UNSET\x10\x00\x12\x0c\n\x08\x43RITICAL\x10\x32\x12\t\n\x05\x45RROR\x10(\x12\x0b\n\x07WARNING\x10\x1e\x12\x08\n\x04INFO\x10\x14\x12\t\n\x05\x44\x45\x42UG\x10\n\x12\t\n\x05TRACE\x10\x05\"P\n\x0bQueueStatus\x12\x0b\n\x03res\x18\x01 \x01(\x05\x12\x0c\n\x04\x66ree\x18\x02 \x01(\r\x12\x0e\n\x06maxlen\x18\x03 \x01(\r\x12\x16\n\x0emesh_packet_id\x18\x04 \x01(\r\"\xf7\x06\n\tFromRadio\x12\n\n\x02id\x18\x01 \x01(\r\x12\x31\n\x06packet\x18\x02 \x01(\x0b\x32\x1f.meshtastic.protobuf.MeshPacketH\x00\x12\x32\n\x07my_info\x18\x03 \x01(\x0b\x32\x1f.meshtastic.protobuf.MyNodeInfoH\x00\x12\x32\n\tnode_info\x18\x04 \x01(\x0b\x32\x1d.meshtastic.protobuf.NodeInfoH\x00\x12-\n\x06\x63onfig\x18\x05 \x01(\x0b\x32\x1b.meshtastic.protobuf.ConfigH\x00\x12\x34\n\nlog_record\x18\x06 \x01(\x0b\x32\x1e.meshtastic.protobuf.LogRecordH\x00\x12\x1c\n\x12\x63onfig_complete_id\x18\x07 \x01(\rH\x00\x12\x12\n\x08rebooted\x18\x08 \x01(\x08H\x00\x12\x39\n\x0cmoduleConfig\x18\t \x01(\x0b\x32!.meshtastic.protobuf.ModuleConfigH\x00\x12/\n\x07\x63hannel\x18\n \x01(\x0b\x32\x1c.meshtastic.protobuf.ChannelH\x00\x12\x37\n\x0bqueueStatus\x18\x0b \x01(\x0b\x32 .meshtastic.protobuf.QueueStatusH\x00\x12\x33\n\x0cxmodemPacket\x18\x0c \x01(\x0b\x32\x1b.meshtastic.protobuf.XModemH\x00\x12\x37\n\x08metadata\x18\r \x01(\x0b\x32#.meshtastic.protobuf.DeviceMetadataH\x00\x12M\n\x16mqttClientProxyMessage\x18\x0e \x01(\x0b\x32+.meshtastic.protobuf.MqttClientProxyMessageH\x00\x12\x31\n\x08\x66ileInfo\x18\x0f \x01(\x0b\x32\x1d.meshtastic.protobuf.FileInfoH\x00\x12\x45\n\x12\x63lientNotification\x18\x10 \x01(\x0b\x32\'.meshtastic.protobuf.ClientNotificationH\x00\x12=\n\x0e\x64\x65viceuiConfig\x18\x11 \x01(\x0b\x32#.meshtastic.protobuf.DeviceUIConfigH\x00\x42\x11\n\x0fpayload_variant\"\xb0\x04\n\x12\x43lientNotification\x12\x15\n\x08reply_id\x18\x01 \x01(\rH\x01\x88\x01\x01\x12\x0c\n\x04time\x18\x02 \x01(\x07\x12\x33\n\x05level\x18\x03 \x01(\x0e\x32$.meshtastic.protobuf.LogRecord.Level\x12\x0f\n\x07message\x18\x04 \x01(\t\x12Z\n\x1ekey_verification_number_inform\x18\x0b \x01(\x0b\x32\x30.meshtastic.protobuf.KeyVerificationNumberInformH\x00\x12\\\n\x1fkey_verification_number_request\x18\x0c \x01(\x0b\x32\x31.meshtastic.protobuf.KeyVerificationNumberRequestH\x00\x12K\n\x16key_verification_final\x18\r \x01(\x0b\x32).meshtastic.protobuf.KeyVerificationFinalH\x00\x12I\n\x15\x64uplicated_public_key\x18\x0e \x01(\x0b\x32(.meshtastic.protobuf.DuplicatedPublicKeyH\x00\x12=\n\x0flow_entropy_key\x18\x0f \x01(\x0b\x32\".meshtastic.protobuf.LowEntropyKeyH\x00\x42\x11\n\x0fpayload_variantB\x0b\n\t_reply_id\"^\n\x1bKeyVerificationNumberInform\x12\r\n\x05nonce\x18\x01 \x01(\x04\x12\x17\n\x0fremote_longname\x18\x02 \x01(\t\x12\x17\n\x0fsecurity_number\x18\x03 \x01(\r\"F\n\x1cKeyVerificationNumberRequest\x12\r\n\x05nonce\x18\x01 \x01(\x04\x12\x17\n\x0fremote_longname\x18\x02 \x01(\t\"q\n\x14KeyVerificationFinal\x12\r\n\x05nonce\x18\x01 \x01(\x04\x12\x17\n\x0fremote_longname\x18\x02 \x01(\t\x12\x10\n\x08isSender\x18\x03 \x01(\x08\x12\x1f\n\x17verification_characters\x18\x04 \x01(\t\"\x15\n\x13\x44uplicatedPublicKey\"\x0f\n\rLowEntropyKey\"1\n\x08\x46ileInfo\x12\x11\n\tfile_name\x18\x01 \x01(\t\x12\x12\n\nsize_bytes\x18\x02 \x01(\r\"\xb8\x02\n\x07ToRadio\x12\x31\n\x06packet\x18\x01 \x01(\x0b\x32\x1f.meshtastic.protobuf.MeshPacketH\x00\x12\x18\n\x0ewant_config_id\x18\x03 \x01(\rH\x00\x12\x14\n\ndisconnect\x18\x04 \x01(\x08H\x00\x12\x33\n\x0cxmodemPacket\x18\x05 \x01(\x0b\x32\x1b.meshtastic.protobuf.XModemH\x00\x12M\n\x16mqttClientProxyMessage\x18\x06 \x01(\x0b\x32+.meshtastic.protobuf.MqttClientProxyMessageH\x00\x12\x33\n\theartbeat\x18\x07 \x01(\x0b\x32\x1e.meshtastic.protobuf.HeartbeatH\x00\x42\x11\n\x0fpayload_variant\"I\n\nCompressed\x12-\n\x07portnum\x18\x01 \x01(\x0e\x32\x1c.meshtastic.protobuf.PortNum\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\"\x90\x01\n\x0cNeighborInfo\x12\x0f\n\x07node_id\x18\x01 \x01(\r\x12\x17\n\x0flast_sent_by_id\x18\x02 \x01(\r\x12$\n\x1cnode_broadcast_interval_secs\x18\x03 \x01(\r\x12\x30\n\tneighbors\x18\x04 \x03(\x0b\x32\x1d.meshtastic.protobuf.Neighbor\"d\n\x08Neighbor\x12\x0f\n\x07node_id\x18\x01 \x01(\r\x12\x0b\n\x03snr\x18\x02 \x01(\x02\x12\x14\n\x0clast_rx_time\x18\x03 \x01(\x07\x12$\n\x1cnode_broadcast_interval_secs\x18\x04 \x01(\r\"\xe9\x02\n\x0e\x44\x65viceMetadata\x12\x18\n\x10\x66irmware_version\x18\x01 \x01(\t\x12\x1c\n\x14\x64\x65vice_state_version\x18\x02 \x01(\r\x12\x13\n\x0b\x63\x61nShutdown\x18\x03 \x01(\x08\x12\x0f\n\x07hasWifi\x18\x04 \x01(\x08\x12\x14\n\x0chasBluetooth\x18\x05 \x01(\x08\x12\x13\n\x0bhasEthernet\x18\x06 \x01(\x08\x12;\n\x04role\x18\x07 \x01(\x0e\x32-.meshtastic.protobuf.Config.DeviceConfig.Role\x12\x16\n\x0eposition_flags\x18\x08 \x01(\r\x12\x34\n\x08hw_model\x18\t \x01(\x0e\x32\".meshtastic.protobuf.HardwareModel\x12\x19\n\x11hasRemoteHardware\x18\n \x01(\x08\x12\x0e\n\x06hasPKC\x18\x0b \x01(\x08\x12\x18\n\x10\x65xcluded_modules\x18\x0c \x01(\r\"\x1a\n\tHeartbeat\x12\r\n\x05nonce\x18\x01 \x01(\r\"^\n\x15NodeRemoteHardwarePin\x12\x10\n\x08node_num\x18\x01 \x01(\r\x12\x33\n\x03pin\x18\x02 \x01(\x0b\x32&.meshtastic.protobuf.RemoteHardwarePin\"e\n\x0e\x43hunkedPayload\x12\x12\n\npayload_id\x18\x01 \x01(\r\x12\x13\n\x0b\x63hunk_count\x18\x02 \x01(\r\x12\x13\n\x0b\x63hunk_index\x18\x03 \x01(\r\x12\x15\n\rpayload_chunk\x18\x04 \x01(\x0c\"\x1f\n\rresend_chunks\x12\x0e\n\x06\x63hunks\x18\x01 \x03(\r\"\xb3\x01\n\x16\x43hunkedPayloadResponse\x12\x12\n\npayload_id\x18\x01 \x01(\r\x12\x1a\n\x10request_transfer\x18\x02 \x01(\x08H\x00\x12\x19\n\x0f\x61\x63\x63\x65pt_transfer\x18\x03 \x01(\x08H\x00\x12;\n\rresend_chunks\x18\x04 \x01(\x0b\x32\".meshtastic.protobuf.resend_chunksH\x00\x42\x11\n\x0fpayload_variant*\xe2\x11\n\rHardwareModel\x12\t\n\x05UNSET\x10\x00\x12\x0c\n\x08TLORA_V2\x10\x01\x12\x0c\n\x08TLORA_V1\x10\x02\x12\x12\n\x0eTLORA_V2_1_1P6\x10\x03\x12\t\n\x05TBEAM\x10\x04\x12\x0f\n\x0bHELTEC_V2_0\x10\x05\x12\x0e\n\nTBEAM_V0P7\x10\x06\x12\n\n\x06T_ECHO\x10\x07\x12\x10\n\x0cTLORA_V1_1P3\x10\x08\x12\x0b\n\x07RAK4631\x10\t\x12\x0f\n\x0bHELTEC_V2_1\x10\n\x12\r\n\tHELTEC_V1\x10\x0b\x12\x18\n\x14LILYGO_TBEAM_S3_CORE\x10\x0c\x12\x0c\n\x08RAK11200\x10\r\x12\x0b\n\x07NANO_G1\x10\x0e\x12\x12\n\x0eTLORA_V2_1_1P8\x10\x0f\x12\x0f\n\x0bTLORA_T3_S3\x10\x10\x12\x14\n\x10NANO_G1_EXPLORER\x10\x11\x12\x11\n\rNANO_G2_ULTRA\x10\x12\x12\r\n\tLORA_TYPE\x10\x13\x12\x0b\n\x07WIPHONE\x10\x14\x12\x0e\n\nWIO_WM1110\x10\x15\x12\x0b\n\x07RAK2560\x10\x16\x12\x13\n\x0fHELTEC_HRU_3601\x10\x17\x12\x1a\n\x16HELTEC_WIRELESS_BRIDGE\x10\x18\x12\x0e\n\nSTATION_G1\x10\x19\x12\x0c\n\x08RAK11310\x10\x1a\x12\x14\n\x10SENSELORA_RP2040\x10\x1b\x12\x10\n\x0cSENSELORA_S3\x10\x1c\x12\r\n\tCANARYONE\x10\x1d\x12\x0f\n\x0bRP2040_LORA\x10\x1e\x12\x0e\n\nSTATION_G2\x10\x1f\x12\x11\n\rLORA_RELAY_V1\x10 \x12\x0e\n\nNRF52840DK\x10!\x12\x07\n\x03PPR\x10\"\x12\x0f\n\x0bGENIEBLOCKS\x10#\x12\x11\n\rNRF52_UNKNOWN\x10$\x12\r\n\tPORTDUINO\x10%\x12\x0f\n\x0b\x41NDROID_SIM\x10&\x12\n\n\x06\x44IY_V1\x10\'\x12\x15\n\x11NRF52840_PCA10059\x10(\x12\n\n\x06\x44R_DEV\x10)\x12\x0b\n\x07M5STACK\x10*\x12\r\n\tHELTEC_V3\x10+\x12\x11\n\rHELTEC_WSL_V3\x10,\x12\x13\n\x0f\x42\x45TAFPV_2400_TX\x10-\x12\x17\n\x13\x42\x45TAFPV_900_NANO_TX\x10.\x12\x0c\n\x08RPI_PICO\x10/\x12\x1b\n\x17HELTEC_WIRELESS_TRACKER\x10\x30\x12\x19\n\x15HELTEC_WIRELESS_PAPER\x10\x31\x12\n\n\x06T_DECK\x10\x32\x12\x0e\n\nT_WATCH_S3\x10\x33\x12\x11\n\rPICOMPUTER_S3\x10\x34\x12\x0f\n\x0bHELTEC_HT62\x10\x35\x12\x12\n\x0e\x45\x42YTE_ESP32_S3\x10\x36\x12\x11\n\rESP32_S3_PICO\x10\x37\x12\r\n\tCHATTER_2\x10\x38\x12\x1e\n\x1aHELTEC_WIRELESS_PAPER_V1_0\x10\x39\x12 \n\x1cHELTEC_WIRELESS_TRACKER_V1_0\x10:\x12\x0b\n\x07UNPHONE\x10;\x12\x0c\n\x08TD_LORAC\x10<\x12\x13\n\x0f\x43\x44\x45\x42YTE_EORA_S3\x10=\x12\x0f\n\x0bTWC_MESH_V4\x10>\x12\x16\n\x12NRF52_PROMICRO_DIY\x10?\x12\x1f\n\x1bRADIOMASTER_900_BANDIT_NANO\x10@\x12\x1c\n\x18HELTEC_CAPSULE_SENSOR_V3\x10\x41\x12\x1d\n\x19HELTEC_VISION_MASTER_T190\x10\x42\x12\x1d\n\x19HELTEC_VISION_MASTER_E213\x10\x43\x12\x1d\n\x19HELTEC_VISION_MASTER_E290\x10\x44\x12\x19\n\x15HELTEC_MESH_NODE_T114\x10\x45\x12\x16\n\x12SENSECAP_INDICATOR\x10\x46\x12\x13\n\x0fTRACKER_T1000_E\x10G\x12\x0b\n\x07RAK3172\x10H\x12\n\n\x06WIO_E5\x10I\x12\x1a\n\x16RADIOMASTER_900_BANDIT\x10J\x12\x13\n\x0fME25LS01_4Y10TD\x10K\x12\x18\n\x14RP2040_FEATHER_RFM95\x10L\x12\x15\n\x11M5STACK_COREBASIC\x10M\x12\x11\n\rM5STACK_CORE2\x10N\x12\r\n\tRPI_PICO2\x10O\x12\x12\n\x0eM5STACK_CORES3\x10P\x12\x11\n\rSEEED_XIAO_S3\x10Q\x12\x0b\n\x07MS24SF1\x10R\x12\x0c\n\x08TLORA_C6\x10S\x12\x0f\n\x0bWISMESH_TAP\x10T\x12\r\n\tROUTASTIC\x10U\x12\x0c\n\x08MESH_TAB\x10V\x12\x0c\n\x08MESHLINK\x10W\x12\x12\n\x0eXIAO_NRF52_KIT\x10X\x12\x10\n\x0cTHINKNODE_M1\x10Y\x12\x10\n\x0cTHINKNODE_M2\x10Z\x12\x0f\n\x0bT_ETH_ELITE\x10[\x12\x15\n\x11HELTEC_SENSOR_HUB\x10\\\x12\x1a\n\x16RESERVED_FRIED_CHICKEN\x10]\x12\x16\n\x12HELTEC_MESH_POCKET\x10^\x12\x14\n\x10SEEED_SOLAR_NODE\x10_\x12\x18\n\x14NOMADSTAR_METEOR_PRO\x10`\x12\r\n\tCROWPANEL\x10\x61\x12\x0b\n\x07LINK_32\x10\x62\x12\x18\n\x14SEEED_WIO_TRACKER_L1\x10\x63\x12\x1d\n\x19SEEED_WIO_TRACKER_L1_EINK\x10\x64\x12\x0f\n\x0bMUZI_R1_NEO\x10\x65\x12\x0e\n\nT_DECK_PRO\x10\x66\x12\x10\n\x0cT_LORA_PAGER\x10g\x12\x14\n\x10M5STACK_RESERVED\x10h\x12\x0f\n\x0bWISMESH_TAG\x10i\x12\x0b\n\x07RAK3312\x10j\x12\x10\n\x0cTHINKNODE_M5\x10k\x12\x15\n\x11HELTEC_MESH_SOLAR\x10l\x12\x0f\n\x0bT_ECHO_LITE\x10m\x12\r\n\tHELTEC_V4\x10n\x12\x0f\n\x0bM5STACK_C6L\x10o\x12\x19\n\x15M5STACK_CARDPUTER_ADV\x10p\x12\x1e\n\x1aHELTEC_WIRELESS_TRACKER_V2\x10q\x12\x11\n\rT_WATCH_ULTRA\x10r\x12\x10\n\x0cTHINKNODE_M3\x10s\x12\x12\n\x0eWISMESH_TAP_V2\x10t\x12\x0b\n\x07RAK3401\x10u\x12\x0f\n\nPRIVATE_HW\x10\xff\x01*,\n\tConstants\x12\x08\n\x04ZERO\x10\x00\x12\x15\n\x10\x44\x41TA_PAYLOAD_LEN\x10\xe9\x01*\xb4\x02\n\x11\x43riticalErrorCode\x12\x08\n\x04NONE\x10\x00\x12\x0f\n\x0bTX_WATCHDOG\x10\x01\x12\x14\n\x10SLEEP_ENTER_WAIT\x10\x02\x12\x0c\n\x08NO_RADIO\x10\x03\x12\x0f\n\x0bUNSPECIFIED\x10\x04\x12\x15\n\x11UBLOX_UNIT_FAILED\x10\x05\x12\r\n\tNO_AXP192\x10\x06\x12\x19\n\x15INVALID_RADIO_SETTING\x10\x07\x12\x13\n\x0fTRANSMIT_FAILED\x10\x08\x12\x0c\n\x08\x42ROWNOUT\x10\t\x12\x12\n\x0eSX1262_FAILURE\x10\n\x12\x11\n\rRADIO_SPI_BUG\x10\x0b\x12 \n\x1c\x46LASH_CORRUPTION_RECOVERABLE\x10\x0c\x12\"\n\x1e\x46LASH_CORRUPTION_UNRECOVERABLE\x10\r*\x7f\n\x0f\x46irmwareEdition\x12\x0b\n\x07VANILLA\x10\x00\x12\x11\n\rSMART_CITIZEN\x10\x01\x12\x0e\n\nOPEN_SAUCE\x10\x10\x12\n\n\x06\x44\x45\x46\x43ON\x10\x11\x12\x0f\n\x0b\x42URNING_MAN\x10\x12\x12\x0e\n\nHAMVENTION\x10\x13\x12\x0f\n\x0b\x44IY_EDITION\x10\x7f*\x80\x03\n\x0f\x45xcludedModules\x12\x11\n\rEXCLUDED_NONE\x10\x00\x12\x0f\n\x0bMQTT_CONFIG\x10\x01\x12\x11\n\rSERIAL_CONFIG\x10\x02\x12\x13\n\x0f\x45XTNOTIF_CONFIG\x10\x04\x12\x17\n\x13STOREFORWARD_CONFIG\x10\x08\x12\x14\n\x10RANGETEST_CONFIG\x10\x10\x12\x14\n\x10TELEMETRY_CONFIG\x10 \x12\x14\n\x10\x43\x41NNEDMSG_CONFIG\x10@\x12\x11\n\x0c\x41UDIO_CONFIG\x10\x80\x01\x12\x1a\n\x15REMOTEHARDWARE_CONFIG\x10\x80\x02\x12\x18\n\x13NEIGHBORINFO_CONFIG\x10\x80\x04\x12\x1b\n\x16\x41MBIENTLIGHTING_CONFIG\x10\x80\x08\x12\x1b\n\x16\x44\x45TECTIONSENSOR_CONFIG\x10\x80\x10\x12\x16\n\x11PAXCOUNTER_CONFIG\x10\x80 \x12\x15\n\x10\x42LUETOOTH_CONFIG\x10\x80@\x12\x14\n\x0eNETWORK_CONFIG\x10\x80\x80\x01\x42`\n\x14org.meshtastic.protoB\nMeshProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.mesh_pb2', _globals) +if _descriptor._USE_C_DESCRIPTORS == False: + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\nMeshProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' + _USER.fields_by_name['macaddr']._options = None + _USER.fields_by_name['macaddr']._serialized_options = b'\030\001' + _MESHPACKET.fields_by_name['delayed']._options = None + _MESHPACKET.fields_by_name['delayed']._serialized_options = b'\030\001' + _globals['_HARDWAREMODEL']._serialized_start=7838 + _globals['_HARDWAREMODEL']._serialized_end=10112 + _globals['_CONSTANTS']._serialized_start=10114 + _globals['_CONSTANTS']._serialized_end=10158 + _globals['_CRITICALERRORCODE']._serialized_start=10161 + _globals['_CRITICALERRORCODE']._serialized_end=10469 + _globals['_FIRMWAREEDITION']._serialized_start=10471 + _globals['_FIRMWAREEDITION']._serialized_end=10598 + _globals['_EXCLUDEDMODULES']._serialized_start=10601 + _globals['_EXCLUDEDMODULES']._serialized_end=10985 + _globals['_POSITION']._serialized_start=310 + _globals['_POSITION']._serialized_end=1231 + _globals['_POSITION_LOCSOURCE']._serialized_start=926 + _globals['_POSITION_LOCSOURCE']._serialized_end=1004 + _globals['_POSITION_ALTSOURCE']._serialized_start=1006 + _globals['_POSITION_ALTSOURCE']._serialized_end=1104 + _globals['_USER']._serialized_start=1234 + _globals['_USER']._serialized_end=1518 + _globals['_ROUTEDISCOVERY']._serialized_start=1520 + _globals['_ROUTEDISCOVERY']._serialized_end=1610 + _globals['_ROUTING']._serialized_start=1613 + _globals['_ROUTING']._serialized_end=2147 + _globals['_ROUTING_ERROR']._serialized_start=1807 + _globals['_ROUTING_ERROR']._serialized_end=2136 + _globals['_DATA']._serialized_start=2150 + _globals['_DATA']._serialized_end=2362 + _globals['_KEYVERIFICATION']._serialized_start=2364 + _globals['_KEYVERIFICATION']._serialized_end=2426 + _globals['_WAYPOINT']._serialized_start=2429 + _globals['_WAYPOINT']._serialized_end=2617 + _globals['_MQTTCLIENTPROXYMESSAGE']._serialized_start=2619 + _globals['_MQTTCLIENTPROXYMESSAGE']._serialized_end=2727 + _globals['_MESHPACKET']._serialized_start=2730 + _globals['_MESHPACKET']._serialized_end=3715 + _globals['_MESHPACKET_PRIORITY']._serialized_start=3292 + _globals['_MESHPACKET_PRIORITY']._serialized_end=3418 + _globals['_MESHPACKET_DELAYED']._serialized_start=3420 + _globals['_MESHPACKET_DELAYED']._serialized_end=3486 + _globals['_MESHPACKET_TRANSPORTMECHANISM']._serialized_start=3489 + _globals['_MESHPACKET_TRANSPORTMECHANISM']._serialized_end=3696 + _globals['_NODEINFO']._serialized_start=3718 + _globals['_NODEINFO']._serialized_end=4072 + _globals['_MYNODEINFO']._serialized_start=4075 + _globals['_MYNODEINFO']._serialized_end=4277 + _globals['_LOGRECORD']._serialized_start=4280 + _globals['_LOGRECORD']._serialized_end=4481 + _globals['_LOGRECORD_LEVEL']._serialized_start=4393 + _globals['_LOGRECORD_LEVEL']._serialized_end=4481 + _globals['_QUEUESTATUS']._serialized_start=4483 + _globals['_QUEUESTATUS']._serialized_end=4563 + _globals['_FROMRADIO']._serialized_start=4566 + _globals['_FROMRADIO']._serialized_end=5453 + _globals['_CLIENTNOTIFICATION']._serialized_start=5456 + _globals['_CLIENTNOTIFICATION']._serialized_end=6016 + _globals['_KEYVERIFICATIONNUMBERINFORM']._serialized_start=6018 + _globals['_KEYVERIFICATIONNUMBERINFORM']._serialized_end=6112 + _globals['_KEYVERIFICATIONNUMBERREQUEST']._serialized_start=6114 + _globals['_KEYVERIFICATIONNUMBERREQUEST']._serialized_end=6184 + _globals['_KEYVERIFICATIONFINAL']._serialized_start=6186 + _globals['_KEYVERIFICATIONFINAL']._serialized_end=6299 + _globals['_DUPLICATEDPUBLICKEY']._serialized_start=6301 + _globals['_DUPLICATEDPUBLICKEY']._serialized_end=6322 + _globals['_LOWENTROPYKEY']._serialized_start=6324 + _globals['_LOWENTROPYKEY']._serialized_end=6339 + _globals['_FILEINFO']._serialized_start=6341 + _globals['_FILEINFO']._serialized_end=6390 + _globals['_TORADIO']._serialized_start=6393 + _globals['_TORADIO']._serialized_end=6705 + _globals['_COMPRESSED']._serialized_start=6707 + _globals['_COMPRESSED']._serialized_end=6780 + _globals['_NEIGHBORINFO']._serialized_start=6783 + _globals['_NEIGHBORINFO']._serialized_end=6927 + _globals['_NEIGHBOR']._serialized_start=6929 + _globals['_NEIGHBOR']._serialized_end=7029 + _globals['_DEVICEMETADATA']._serialized_start=7032 + _globals['_DEVICEMETADATA']._serialized_end=7393 + _globals['_HEARTBEAT']._serialized_start=7395 + _globals['_HEARTBEAT']._serialized_end=7421 + _globals['_NODEREMOTEHARDWAREPIN']._serialized_start=7423 + _globals['_NODEREMOTEHARDWAREPIN']._serialized_end=7517 + _globals['_CHUNKEDPAYLOAD']._serialized_start=7519 + _globals['_CHUNKEDPAYLOAD']._serialized_end=7620 + _globals['_RESEND_CHUNKS']._serialized_start=7622 + _globals['_RESEND_CHUNKS']._serialized_end=7653 + _globals['_CHUNKEDPAYLOADRESPONSE']._serialized_start=7656 + _globals['_CHUNKEDPAYLOADRESPONSE']._serialized_end=7835 +# @@protoc_insertion_point(module_scope) diff --git a/meshtastic/protobuf/mesh_pb2.pyi b/meshtastic/protobuf/mesh_pb2.pyi new file mode 100644 index 00000000..6578763f --- /dev/null +++ b/meshtastic/protobuf/mesh_pb2.pyi @@ -0,0 +1,3796 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" + +import builtins +import collections.abc +import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.internal.enum_type_wrapper +import google.protobuf.message +import meshtastic.protobuf.channel_pb2 +import meshtastic.protobuf.config_pb2 +import meshtastic.protobuf.device_ui_pb2 +import meshtastic.protobuf.module_config_pb2 +import meshtastic.protobuf.portnums_pb2 +import meshtastic.protobuf.telemetry_pb2 +import meshtastic.protobuf.xmodem_pb2 +import sys +import typing + +if sys.version_info >= (3, 10): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +class _HardwareModel: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _HardwareModelEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_HardwareModel.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + UNSET: _HardwareModel.ValueType # 0 + """ + TODO: REPLACE + """ + TLORA_V2: _HardwareModel.ValueType # 1 + """ + TODO: REPLACE + """ + TLORA_V1: _HardwareModel.ValueType # 2 + """ + TODO: REPLACE + """ + TLORA_V2_1_1P6: _HardwareModel.ValueType # 3 + """ + TODO: REPLACE + """ + TBEAM: _HardwareModel.ValueType # 4 + """ + TODO: REPLACE + """ + HELTEC_V2_0: _HardwareModel.ValueType # 5 + """ + The original heltec WiFi_Lora_32_V2, which had battery voltage sensing hooked to GPIO 13 + (see HELTEC_V2 for the new version). + """ + TBEAM_V0P7: _HardwareModel.ValueType # 6 + """ + TODO: REPLACE + """ + T_ECHO: _HardwareModel.ValueType # 7 + """ + TODO: REPLACE + """ + TLORA_V1_1P3: _HardwareModel.ValueType # 8 + """ + TODO: REPLACE + """ + RAK4631: _HardwareModel.ValueType # 9 + """ + TODO: REPLACE + """ + HELTEC_V2_1: _HardwareModel.ValueType # 10 + """ + The new version of the heltec WiFi_Lora_32_V2 board that has battery sensing hooked to GPIO 37. + Sadly they did not update anything on the silkscreen to identify this board + """ + HELTEC_V1: _HardwareModel.ValueType # 11 + """ + Ancient heltec WiFi_Lora_32 board + """ + LILYGO_TBEAM_S3_CORE: _HardwareModel.ValueType # 12 + """ + New T-BEAM with ESP32-S3 CPU + """ + RAK11200: _HardwareModel.ValueType # 13 + """ + RAK WisBlock ESP32 core: https://docs.rakwireless.com/Product-Categories/WisBlock/RAK11200/Overview/ + """ + NANO_G1: _HardwareModel.ValueType # 14 + """ + B&Q Consulting Nano Edition G1: https://uniteng.com/wiki/doku.php?id=meshtastic:nano + """ + TLORA_V2_1_1P8: _HardwareModel.ValueType # 15 + """ + TODO: REPLACE + """ + TLORA_T3_S3: _HardwareModel.ValueType # 16 + """ + TODO: REPLACE + """ + NANO_G1_EXPLORER: _HardwareModel.ValueType # 17 + """ + B&Q Consulting Nano G1 Explorer: https://wiki.uniteng.com/en/meshtastic/nano-g1-explorer + """ + NANO_G2_ULTRA: _HardwareModel.ValueType # 18 + """ + B&Q Consulting Nano G2 Ultra: https://wiki.uniteng.com/en/meshtastic/nano-g2-ultra + """ + LORA_TYPE: _HardwareModel.ValueType # 19 + """ + LoRAType device: https://loratype.org/ + """ + WIPHONE: _HardwareModel.ValueType # 20 + """ + wiphone https://www.wiphone.io/ + """ + WIO_WM1110: _HardwareModel.ValueType # 21 + """ + WIO Tracker WM1110 family from Seeed Studio. Includes wio-1110-tracker and wio-1110-sdk + """ + RAK2560: _HardwareModel.ValueType # 22 + """ + RAK2560 Solar base station based on RAK4630 + """ + HELTEC_HRU_3601: _HardwareModel.ValueType # 23 + """ + Heltec HRU-3601: https://heltec.org/project/hru-3601/ + """ + HELTEC_WIRELESS_BRIDGE: _HardwareModel.ValueType # 24 + """ + Heltec Wireless Bridge + """ + STATION_G1: _HardwareModel.ValueType # 25 + """ + B&Q Consulting Station Edition G1: https://uniteng.com/wiki/doku.php?id=meshtastic:station + """ + RAK11310: _HardwareModel.ValueType # 26 + """ + RAK11310 (RP2040 + SX1262) + """ + SENSELORA_RP2040: _HardwareModel.ValueType # 27 + """ + Makerfabs SenseLoRA Receiver (RP2040 + RFM96) + """ + SENSELORA_S3: _HardwareModel.ValueType # 28 + """ + Makerfabs SenseLoRA Industrial Monitor (ESP32-S3 + RFM96) + """ + CANARYONE: _HardwareModel.ValueType # 29 + """ + Canary Radio Company - CanaryOne: https://canaryradio.io/products/canaryone + """ + RP2040_LORA: _HardwareModel.ValueType # 30 + """ + Waveshare RP2040 LoRa - https://www.waveshare.com/rp2040-lora.htm + """ + STATION_G2: _HardwareModel.ValueType # 31 + """ + B&Q Consulting Station G2: https://wiki.uniteng.com/en/meshtastic/station-g2 + """ + LORA_RELAY_V1: _HardwareModel.ValueType # 32 + """ + --------------------------------------------------------------------------- + Less common/prototype boards listed here (needs one more byte over the air) + --------------------------------------------------------------------------- + """ + NRF52840DK: _HardwareModel.ValueType # 33 + """ + TODO: REPLACE + """ + PPR: _HardwareModel.ValueType # 34 + """ + TODO: REPLACE + """ + GENIEBLOCKS: _HardwareModel.ValueType # 35 + """ + TODO: REPLACE + """ + NRF52_UNKNOWN: _HardwareModel.ValueType # 36 + """ + TODO: REPLACE + """ + PORTDUINO: _HardwareModel.ValueType # 37 + """ + TODO: REPLACE + """ + ANDROID_SIM: _HardwareModel.ValueType # 38 + """ + The simulator built into the android app + """ + DIY_V1: _HardwareModel.ValueType # 39 + """ + Custom DIY device based on @NanoVHF schematics: https://github.com/NanoVHF/Meshtastic-DIY/tree/main/Schematics + """ + NRF52840_PCA10059: _HardwareModel.ValueType # 40 + """ + nRF52840 Dongle : https://www.nordicsemi.com/Products/Development-hardware/nrf52840-dongle/ + """ + DR_DEV: _HardwareModel.ValueType # 41 + """ + Custom Disaster Radio esp32 v3 device https://github.com/sudomesh/disaster-radio/tree/master/hardware/board_esp32_v3 + """ + M5STACK: _HardwareModel.ValueType # 42 + """ + M5 esp32 based MCU modules with enclosure, TFT and LORA Shields. All Variants (Basic, Core, Fire, Core2, CoreS3, Paper) https://m5stack.com/ + """ + HELTEC_V3: _HardwareModel.ValueType # 43 + """ + New Heltec LoRA32 with ESP32-S3 CPU + """ + HELTEC_WSL_V3: _HardwareModel.ValueType # 44 + """ + New Heltec Wireless Stick Lite with ESP32-S3 CPU + """ + BETAFPV_2400_TX: _HardwareModel.ValueType # 45 + """ + New BETAFPV ELRS Micro TX Module 2.4G with ESP32 CPU + """ + BETAFPV_900_NANO_TX: _HardwareModel.ValueType # 46 + """ + BetaFPV ExpressLRS "Nano" TX Module 900MHz with ESP32 CPU + """ + RPI_PICO: _HardwareModel.ValueType # 47 + """ + Raspberry Pi Pico (W) with Waveshare SX1262 LoRa Node Module + """ + HELTEC_WIRELESS_TRACKER: _HardwareModel.ValueType # 48 + """ + Heltec Wireless Tracker with ESP32-S3 CPU, built-in GPS, and TFT + Newer V1.1, version is written on the PCB near the display. + """ + HELTEC_WIRELESS_PAPER: _HardwareModel.ValueType # 49 + """ + Heltec Wireless Paper with ESP32-S3 CPU and E-Ink display + """ + T_DECK: _HardwareModel.ValueType # 50 + """ + LilyGo T-Deck with ESP32-S3 CPU, Keyboard and IPS display + """ + T_WATCH_S3: _HardwareModel.ValueType # 51 + """ + LilyGo T-Watch S3 with ESP32-S3 CPU and IPS display + """ + PICOMPUTER_S3: _HardwareModel.ValueType # 52 + """ + Bobricius Picomputer with ESP32-S3 CPU, Keyboard and IPS display + """ + HELTEC_HT62: _HardwareModel.ValueType # 53 + """ + Heltec HT-CT62 with ESP32-C3 CPU and SX1262 LoRa + """ + EBYTE_ESP32_S3: _HardwareModel.ValueType # 54 + """ + EBYTE SPI LoRa module and ESP32-S3 + """ + ESP32_S3_PICO: _HardwareModel.ValueType # 55 + """ + Waveshare ESP32-S3-PICO with PICO LoRa HAT and 2.9inch e-Ink + """ + CHATTER_2: _HardwareModel.ValueType # 56 + """ + CircuitMess Chatter 2 LLCC68 Lora Module and ESP32 Wroom + Lora module can be swapped out for a Heltec RA-62 which is "almost" pin compatible + with one cut and one jumper Meshtastic works + """ + HELTEC_WIRELESS_PAPER_V1_0: _HardwareModel.ValueType # 57 + """ + Heltec Wireless Paper, With ESP32-S3 CPU and E-Ink display + Older "V1.0" Variant, has no "version sticker" + E-Ink model is DEPG0213BNS800 + Tab on the screen protector is RED + Flex connector marking is FPC-7528B + """ + HELTEC_WIRELESS_TRACKER_V1_0: _HardwareModel.ValueType # 58 + """ + Heltec Wireless Tracker with ESP32-S3 CPU, built-in GPS, and TFT + Older "V1.0" Variant + """ + UNPHONE: _HardwareModel.ValueType # 59 + """ + unPhone with ESP32-S3, TFT touchscreen, LSM6DS3TR-C accelerometer and gyroscope + """ + TD_LORAC: _HardwareModel.ValueType # 60 + """ + Teledatics TD-LORAC NRF52840 based M.2 LoRA module + Compatible with the TD-WRLS development board + """ + CDEBYTE_EORA_S3: _HardwareModel.ValueType # 61 + """ + CDEBYTE EoRa-S3 board using their own MM modules, clone of LILYGO T3S3 + """ + TWC_MESH_V4: _HardwareModel.ValueType # 62 + """ + TWC_MESH_V4 + Adafruit NRF52840 feather express with SX1262, SSD1306 OLED and NEO6M GPS + """ + NRF52_PROMICRO_DIY: _HardwareModel.ValueType # 63 + """ + NRF52_PROMICRO_DIY + Promicro NRF52840 with SX1262/LLCC68, SSD1306 OLED and NEO6M GPS + """ + RADIOMASTER_900_BANDIT_NANO: _HardwareModel.ValueType # 64 + """ + RadioMaster 900 Bandit Nano, https://www.radiomasterrc.com/products/bandit-nano-expresslrs-rf-module + ESP32-D0WDQ6 With SX1276/SKY66122, SSD1306 OLED and No GPS + """ + HELTEC_CAPSULE_SENSOR_V3: _HardwareModel.ValueType # 65 + """ + Heltec Capsule Sensor V3 with ESP32-S3 CPU, Portable LoRa device that can replace GNSS modules or sensors + """ + HELTEC_VISION_MASTER_T190: _HardwareModel.ValueType # 66 + """ + Heltec Vision Master T190 with ESP32-S3 CPU, and a 1.90 inch TFT display + """ + HELTEC_VISION_MASTER_E213: _HardwareModel.ValueType # 67 + """ + Heltec Vision Master E213 with ESP32-S3 CPU, and a 2.13 inch E-Ink display + """ + HELTEC_VISION_MASTER_E290: _HardwareModel.ValueType # 68 + """ + Heltec Vision Master E290 with ESP32-S3 CPU, and a 2.9 inch E-Ink display + """ + HELTEC_MESH_NODE_T114: _HardwareModel.ValueType # 69 + """ + Heltec Mesh Node T114 board with nRF52840 CPU, and a 1.14 inch TFT display, Ultimate low-power design, + specifically adapted for the Meshtatic project + """ + SENSECAP_INDICATOR: _HardwareModel.ValueType # 70 + """ + Sensecap Indicator from Seeed Studio. ESP32-S3 device with TFT and RP2040 coprocessor + """ + TRACKER_T1000_E: _HardwareModel.ValueType # 71 + """ + Seeed studio T1000-E tracker card. NRF52840 w/ LR1110 radio, GPS, button, buzzer, and sensors. + """ + RAK3172: _HardwareModel.ValueType # 72 + """ + RAK3172 STM32WLE5 Module (https://store.rakwireless.com/products/wisduo-lpwan-module-rak3172) + """ + WIO_E5: _HardwareModel.ValueType # 73 + """ + Seeed Studio Wio-E5 (either mini or Dev kit) using STM32WL chip. + """ + RADIOMASTER_900_BANDIT: _HardwareModel.ValueType # 74 + """ + RadioMaster 900 Bandit, https://www.radiomasterrc.com/products/bandit-expresslrs-rf-module + SSD1306 OLED and No GPS + """ + ME25LS01_4Y10TD: _HardwareModel.ValueType # 75 + """ + Minewsemi ME25LS01 (ME25LE01_V1.0). NRF52840 w/ LR1110 radio, buttons and leds and pins. + """ + RP2040_FEATHER_RFM95: _HardwareModel.ValueType # 76 + """ + RP2040_FEATHER_RFM95 + Adafruit Feather RP2040 with RFM95 LoRa Radio RFM95 with SX1272, SSD1306 OLED + https://www.adafruit.com/product/5714 + https://www.adafruit.com/product/326 + https://www.adafruit.com/product/938 + ^^^ short A0 to switch to I2C address 0x3C + """ + M5STACK_COREBASIC: _HardwareModel.ValueType # 77 + """M5 esp32 based MCU modules with enclosure, TFT and LORA Shields. All Variants (Basic, Core, Fire, Core2, CoreS3, Paper) https://m5stack.com/""" + M5STACK_CORE2: _HardwareModel.ValueType # 78 + RPI_PICO2: _HardwareModel.ValueType # 79 + """Pico2 with Waveshare Hat, same as Pico""" + M5STACK_CORES3: _HardwareModel.ValueType # 80 + """M5 esp32 based MCU modules with enclosure, TFT and LORA Shields. All Variants (Basic, Core, Fire, Core2, CoreS3, Paper) https://m5stack.com/""" + SEEED_XIAO_S3: _HardwareModel.ValueType # 81 + """Seeed XIAO S3 DK""" + MS24SF1: _HardwareModel.ValueType # 82 + """ + Nordic nRF52840+Semtech SX1262 LoRa BLE Combo Module. nRF52840+SX1262 MS24SF1 + """ + TLORA_C6: _HardwareModel.ValueType # 83 + """ + Lilygo TLora-C6 with the new ESP32-C6 MCU + """ + WISMESH_TAP: _HardwareModel.ValueType # 84 + """ + WisMesh Tap + RAK-4631 w/ TFT in injection modled case + """ + ROUTASTIC: _HardwareModel.ValueType # 85 + """ + Similar to PORTDUINO but used by Routastic devices, this is not any + particular device and does not run Meshtastic's code but supports + the same frame format. + Runs on linux, see https://github.com/Jorropo/routastic + """ + MESH_TAB: _HardwareModel.ValueType # 86 + """ + Mesh-Tab, esp32 based + https://github.com/valzzu/Mesh-Tab + """ + MESHLINK: _HardwareModel.ValueType # 87 + """ + MeshLink board developed by LoraItalia. NRF52840, eByte E22900M22S (Will also come with other frequencies), 25w MPPT solar charger (5v,12v,18v selectable), support for gps, buzzer, oled or e-ink display, 10 gpios, hardware watchdog + https://www.loraitalia.it + """ + XIAO_NRF52_KIT: _HardwareModel.ValueType # 88 + """ + Seeed XIAO nRF52840 + Wio SX1262 kit + """ + THINKNODE_M1: _HardwareModel.ValueType # 89 + """ + Elecrow ThinkNode M1 & M2 + https://www.elecrow.com/wiki/ThinkNode-M1_Transceiver_Device(Meshtastic)_Power_By_nRF52840.html + https://www.elecrow.com/wiki/ThinkNode-M2_Transceiver_Device(Meshtastic)_Power_By_NRF52840.html (this actually uses ESP32-S3) + """ + THINKNODE_M2: _HardwareModel.ValueType # 90 + T_ETH_ELITE: _HardwareModel.ValueType # 91 + """ + Lilygo T-ETH-Elite + """ + HELTEC_SENSOR_HUB: _HardwareModel.ValueType # 92 + """ + Heltec HRI-3621 industrial probe + """ + RESERVED_FRIED_CHICKEN: _HardwareModel.ValueType # 93 + """ + Reserved Fried Chicken ID for future use + """ + HELTEC_MESH_POCKET: _HardwareModel.ValueType # 94 + """ + Heltec Magnetic Power Bank with Meshtastic compatible + """ + SEEED_SOLAR_NODE: _HardwareModel.ValueType # 95 + """ + Seeed Solar Node + """ + NOMADSTAR_METEOR_PRO: _HardwareModel.ValueType # 96 + """ + NomadStar Meteor Pro https://nomadstar.ch/ + """ + CROWPANEL: _HardwareModel.ValueType # 97 + """ + Elecrow CrowPanel Advance models, ESP32-S3 and TFT with SX1262 radio plugin + """ + LINK_32: _HardwareModel.ValueType # 98 + """ + Lilygo LINK32 board with sensors + """ + SEEED_WIO_TRACKER_L1: _HardwareModel.ValueType # 99 + """ + Seeed Tracker L1 + """ + SEEED_WIO_TRACKER_L1_EINK: _HardwareModel.ValueType # 100 + """ + Seeed Tracker L1 EINK driver + """ + MUZI_R1_NEO: _HardwareModel.ValueType # 101 + """ + Muzi Works R1 Neo + """ + T_DECK_PRO: _HardwareModel.ValueType # 102 + """ + Lilygo T-Deck Pro + """ + T_LORA_PAGER: _HardwareModel.ValueType # 103 + """ + Lilygo TLora Pager + """ + M5STACK_RESERVED: _HardwareModel.ValueType # 104 + """ + M5Stack Reserved + 0x68 + """ + WISMESH_TAG: _HardwareModel.ValueType # 105 + """ + RAKwireless WisMesh Tag + """ + RAK3312: _HardwareModel.ValueType # 106 + """ + RAKwireless WisBlock Core RAK3312 https://docs.rakwireless.com/product-categories/wisduo/rak3112-module/overview/ + """ + THINKNODE_M5: _HardwareModel.ValueType # 107 + """ + Elecrow ThinkNode M5 https://www.elecrow.com/wiki/ThinkNode_M5_Meshtastic_LoRa_Signal_Transceiver_ESP32-S3.html + """ + HELTEC_MESH_SOLAR: _HardwareModel.ValueType # 108 + """ + MeshSolar is an integrated power management and communication solution designed for outdoor low-power devices. + https://heltec.org/project/meshsolar/ + """ + T_ECHO_LITE: _HardwareModel.ValueType # 109 + """ + Lilygo T-Echo Lite + """ + HELTEC_V4: _HardwareModel.ValueType # 110 + """ + New Heltec LoRA32 with ESP32-S3 CPU + """ + M5STACK_C6L: _HardwareModel.ValueType # 111 + """ + M5Stack C6L + """ + M5STACK_CARDPUTER_ADV: _HardwareModel.ValueType # 112 + """ + M5Stack Cardputer Adv + """ + HELTEC_WIRELESS_TRACKER_V2: _HardwareModel.ValueType # 113 + """ + ESP32S3 main controller with GPS and TFT screen. + """ + T_WATCH_ULTRA: _HardwareModel.ValueType # 114 + """ + LilyGo T-Watch Ultra + """ + THINKNODE_M3: _HardwareModel.ValueType # 115 + """ + Elecrow ThinkNode M3 + """ + WISMESH_TAP_V2: _HardwareModel.ValueType # 116 + """ + RAK WISMESH_TAP_V2 with ESP32-S3 CPU + """ + RAK3401: _HardwareModel.ValueType # 117 + """ + RAK3401 + """ + PRIVATE_HW: _HardwareModel.ValueType # 255 + """ + ------------------------------------------------------------------------------------------------------------------------------------------ + Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits. + ------------------------------------------------------------------------------------------------------------------------------------------ + """ + +class HardwareModel(_HardwareModel, metaclass=_HardwareModelEnumTypeWrapper): + """ + Note: these enum names must EXACTLY match the string used in the device + bin/build-all.sh script. + Because they will be used to find firmware filenames in the android app for OTA updates. + To match the old style filenames, _ is converted to -, p is converted to . + """ + +UNSET: HardwareModel.ValueType # 0 +""" +TODO: REPLACE +""" +TLORA_V2: HardwareModel.ValueType # 1 +""" +TODO: REPLACE +""" +TLORA_V1: HardwareModel.ValueType # 2 +""" +TODO: REPLACE +""" +TLORA_V2_1_1P6: HardwareModel.ValueType # 3 +""" +TODO: REPLACE +""" +TBEAM: HardwareModel.ValueType # 4 +""" +TODO: REPLACE +""" +HELTEC_V2_0: HardwareModel.ValueType # 5 +""" +The original heltec WiFi_Lora_32_V2, which had battery voltage sensing hooked to GPIO 13 +(see HELTEC_V2 for the new version). +""" +TBEAM_V0P7: HardwareModel.ValueType # 6 +""" +TODO: REPLACE +""" +T_ECHO: HardwareModel.ValueType # 7 +""" +TODO: REPLACE +""" +TLORA_V1_1P3: HardwareModel.ValueType # 8 +""" +TODO: REPLACE +""" +RAK4631: HardwareModel.ValueType # 9 +""" +TODO: REPLACE +""" +HELTEC_V2_1: HardwareModel.ValueType # 10 +""" +The new version of the heltec WiFi_Lora_32_V2 board that has battery sensing hooked to GPIO 37. +Sadly they did not update anything on the silkscreen to identify this board +""" +HELTEC_V1: HardwareModel.ValueType # 11 +""" +Ancient heltec WiFi_Lora_32 board +""" +LILYGO_TBEAM_S3_CORE: HardwareModel.ValueType # 12 +""" +New T-BEAM with ESP32-S3 CPU +""" +RAK11200: HardwareModel.ValueType # 13 +""" +RAK WisBlock ESP32 core: https://docs.rakwireless.com/Product-Categories/WisBlock/RAK11200/Overview/ +""" +NANO_G1: HardwareModel.ValueType # 14 +""" +B&Q Consulting Nano Edition G1: https://uniteng.com/wiki/doku.php?id=meshtastic:nano +""" +TLORA_V2_1_1P8: HardwareModel.ValueType # 15 +""" +TODO: REPLACE +""" +TLORA_T3_S3: HardwareModel.ValueType # 16 +""" +TODO: REPLACE +""" +NANO_G1_EXPLORER: HardwareModel.ValueType # 17 +""" +B&Q Consulting Nano G1 Explorer: https://wiki.uniteng.com/en/meshtastic/nano-g1-explorer +""" +NANO_G2_ULTRA: HardwareModel.ValueType # 18 +""" +B&Q Consulting Nano G2 Ultra: https://wiki.uniteng.com/en/meshtastic/nano-g2-ultra +""" +LORA_TYPE: HardwareModel.ValueType # 19 +""" +LoRAType device: https://loratype.org/ +""" +WIPHONE: HardwareModel.ValueType # 20 +""" +wiphone https://www.wiphone.io/ +""" +WIO_WM1110: HardwareModel.ValueType # 21 +""" +WIO Tracker WM1110 family from Seeed Studio. Includes wio-1110-tracker and wio-1110-sdk +""" +RAK2560: HardwareModel.ValueType # 22 +""" +RAK2560 Solar base station based on RAK4630 +""" +HELTEC_HRU_3601: HardwareModel.ValueType # 23 +""" +Heltec HRU-3601: https://heltec.org/project/hru-3601/ +""" +HELTEC_WIRELESS_BRIDGE: HardwareModel.ValueType # 24 +""" +Heltec Wireless Bridge +""" +STATION_G1: HardwareModel.ValueType # 25 +""" +B&Q Consulting Station Edition G1: https://uniteng.com/wiki/doku.php?id=meshtastic:station +""" +RAK11310: HardwareModel.ValueType # 26 +""" +RAK11310 (RP2040 + SX1262) +""" +SENSELORA_RP2040: HardwareModel.ValueType # 27 +""" +Makerfabs SenseLoRA Receiver (RP2040 + RFM96) +""" +SENSELORA_S3: HardwareModel.ValueType # 28 +""" +Makerfabs SenseLoRA Industrial Monitor (ESP32-S3 + RFM96) +""" +CANARYONE: HardwareModel.ValueType # 29 +""" +Canary Radio Company - CanaryOne: https://canaryradio.io/products/canaryone +""" +RP2040_LORA: HardwareModel.ValueType # 30 +""" +Waveshare RP2040 LoRa - https://www.waveshare.com/rp2040-lora.htm +""" +STATION_G2: HardwareModel.ValueType # 31 +""" +B&Q Consulting Station G2: https://wiki.uniteng.com/en/meshtastic/station-g2 +""" +LORA_RELAY_V1: HardwareModel.ValueType # 32 +""" +--------------------------------------------------------------------------- +Less common/prototype boards listed here (needs one more byte over the air) +--------------------------------------------------------------------------- +""" +NRF52840DK: HardwareModel.ValueType # 33 +""" +TODO: REPLACE +""" +PPR: HardwareModel.ValueType # 34 +""" +TODO: REPLACE +""" +GENIEBLOCKS: HardwareModel.ValueType # 35 +""" +TODO: REPLACE +""" +NRF52_UNKNOWN: HardwareModel.ValueType # 36 +""" +TODO: REPLACE +""" +PORTDUINO: HardwareModel.ValueType # 37 +""" +TODO: REPLACE +""" +ANDROID_SIM: HardwareModel.ValueType # 38 +""" +The simulator built into the android app +""" +DIY_V1: HardwareModel.ValueType # 39 +""" +Custom DIY device based on @NanoVHF schematics: https://github.com/NanoVHF/Meshtastic-DIY/tree/main/Schematics +""" +NRF52840_PCA10059: HardwareModel.ValueType # 40 +""" +nRF52840 Dongle : https://www.nordicsemi.com/Products/Development-hardware/nrf52840-dongle/ +""" +DR_DEV: HardwareModel.ValueType # 41 +""" +Custom Disaster Radio esp32 v3 device https://github.com/sudomesh/disaster-radio/tree/master/hardware/board_esp32_v3 +""" +M5STACK: HardwareModel.ValueType # 42 +""" +M5 esp32 based MCU modules with enclosure, TFT and LORA Shields. All Variants (Basic, Core, Fire, Core2, CoreS3, Paper) https://m5stack.com/ +""" +HELTEC_V3: HardwareModel.ValueType # 43 +""" +New Heltec LoRA32 with ESP32-S3 CPU +""" +HELTEC_WSL_V3: HardwareModel.ValueType # 44 +""" +New Heltec Wireless Stick Lite with ESP32-S3 CPU +""" +BETAFPV_2400_TX: HardwareModel.ValueType # 45 +""" +New BETAFPV ELRS Micro TX Module 2.4G with ESP32 CPU +""" +BETAFPV_900_NANO_TX: HardwareModel.ValueType # 46 +""" +BetaFPV ExpressLRS "Nano" TX Module 900MHz with ESP32 CPU +""" +RPI_PICO: HardwareModel.ValueType # 47 +""" +Raspberry Pi Pico (W) with Waveshare SX1262 LoRa Node Module +""" +HELTEC_WIRELESS_TRACKER: HardwareModel.ValueType # 48 +""" +Heltec Wireless Tracker with ESP32-S3 CPU, built-in GPS, and TFT +Newer V1.1, version is written on the PCB near the display. +""" +HELTEC_WIRELESS_PAPER: HardwareModel.ValueType # 49 +""" +Heltec Wireless Paper with ESP32-S3 CPU and E-Ink display +""" +T_DECK: HardwareModel.ValueType # 50 +""" +LilyGo T-Deck with ESP32-S3 CPU, Keyboard and IPS display +""" +T_WATCH_S3: HardwareModel.ValueType # 51 +""" +LilyGo T-Watch S3 with ESP32-S3 CPU and IPS display +""" +PICOMPUTER_S3: HardwareModel.ValueType # 52 +""" +Bobricius Picomputer with ESP32-S3 CPU, Keyboard and IPS display +""" +HELTEC_HT62: HardwareModel.ValueType # 53 +""" +Heltec HT-CT62 with ESP32-C3 CPU and SX1262 LoRa +""" +EBYTE_ESP32_S3: HardwareModel.ValueType # 54 +""" +EBYTE SPI LoRa module and ESP32-S3 +""" +ESP32_S3_PICO: HardwareModel.ValueType # 55 +""" +Waveshare ESP32-S3-PICO with PICO LoRa HAT and 2.9inch e-Ink +""" +CHATTER_2: HardwareModel.ValueType # 56 +""" +CircuitMess Chatter 2 LLCC68 Lora Module and ESP32 Wroom +Lora module can be swapped out for a Heltec RA-62 which is "almost" pin compatible +with one cut and one jumper Meshtastic works +""" +HELTEC_WIRELESS_PAPER_V1_0: HardwareModel.ValueType # 57 +""" +Heltec Wireless Paper, With ESP32-S3 CPU and E-Ink display +Older "V1.0" Variant, has no "version sticker" +E-Ink model is DEPG0213BNS800 +Tab on the screen protector is RED +Flex connector marking is FPC-7528B +""" +HELTEC_WIRELESS_TRACKER_V1_0: HardwareModel.ValueType # 58 +""" +Heltec Wireless Tracker with ESP32-S3 CPU, built-in GPS, and TFT +Older "V1.0" Variant +""" +UNPHONE: HardwareModel.ValueType # 59 +""" +unPhone with ESP32-S3, TFT touchscreen, LSM6DS3TR-C accelerometer and gyroscope +""" +TD_LORAC: HardwareModel.ValueType # 60 +""" +Teledatics TD-LORAC NRF52840 based M.2 LoRA module +Compatible with the TD-WRLS development board +""" +CDEBYTE_EORA_S3: HardwareModel.ValueType # 61 +""" +CDEBYTE EoRa-S3 board using their own MM modules, clone of LILYGO T3S3 +""" +TWC_MESH_V4: HardwareModel.ValueType # 62 +""" +TWC_MESH_V4 +Adafruit NRF52840 feather express with SX1262, SSD1306 OLED and NEO6M GPS +""" +NRF52_PROMICRO_DIY: HardwareModel.ValueType # 63 +""" +NRF52_PROMICRO_DIY +Promicro NRF52840 with SX1262/LLCC68, SSD1306 OLED and NEO6M GPS +""" +RADIOMASTER_900_BANDIT_NANO: HardwareModel.ValueType # 64 +""" +RadioMaster 900 Bandit Nano, https://www.radiomasterrc.com/products/bandit-nano-expresslrs-rf-module +ESP32-D0WDQ6 With SX1276/SKY66122, SSD1306 OLED and No GPS +""" +HELTEC_CAPSULE_SENSOR_V3: HardwareModel.ValueType # 65 +""" +Heltec Capsule Sensor V3 with ESP32-S3 CPU, Portable LoRa device that can replace GNSS modules or sensors +""" +HELTEC_VISION_MASTER_T190: HardwareModel.ValueType # 66 +""" +Heltec Vision Master T190 with ESP32-S3 CPU, and a 1.90 inch TFT display +""" +HELTEC_VISION_MASTER_E213: HardwareModel.ValueType # 67 +""" +Heltec Vision Master E213 with ESP32-S3 CPU, and a 2.13 inch E-Ink display +""" +HELTEC_VISION_MASTER_E290: HardwareModel.ValueType # 68 +""" +Heltec Vision Master E290 with ESP32-S3 CPU, and a 2.9 inch E-Ink display +""" +HELTEC_MESH_NODE_T114: HardwareModel.ValueType # 69 +""" +Heltec Mesh Node T114 board with nRF52840 CPU, and a 1.14 inch TFT display, Ultimate low-power design, +specifically adapted for the Meshtatic project +""" +SENSECAP_INDICATOR: HardwareModel.ValueType # 70 +""" +Sensecap Indicator from Seeed Studio. ESP32-S3 device with TFT and RP2040 coprocessor +""" +TRACKER_T1000_E: HardwareModel.ValueType # 71 +""" +Seeed studio T1000-E tracker card. NRF52840 w/ LR1110 radio, GPS, button, buzzer, and sensors. +""" +RAK3172: HardwareModel.ValueType # 72 +""" +RAK3172 STM32WLE5 Module (https://store.rakwireless.com/products/wisduo-lpwan-module-rak3172) +""" +WIO_E5: HardwareModel.ValueType # 73 +""" +Seeed Studio Wio-E5 (either mini or Dev kit) using STM32WL chip. +""" +RADIOMASTER_900_BANDIT: HardwareModel.ValueType # 74 +""" +RadioMaster 900 Bandit, https://www.radiomasterrc.com/products/bandit-expresslrs-rf-module +SSD1306 OLED and No GPS +""" +ME25LS01_4Y10TD: HardwareModel.ValueType # 75 +""" +Minewsemi ME25LS01 (ME25LE01_V1.0). NRF52840 w/ LR1110 radio, buttons and leds and pins. +""" +RP2040_FEATHER_RFM95: HardwareModel.ValueType # 76 +""" +RP2040_FEATHER_RFM95 +Adafruit Feather RP2040 with RFM95 LoRa Radio RFM95 with SX1272, SSD1306 OLED +https://www.adafruit.com/product/5714 +https://www.adafruit.com/product/326 +https://www.adafruit.com/product/938 + ^^^ short A0 to switch to I2C address 0x3C +""" +M5STACK_COREBASIC: HardwareModel.ValueType # 77 +"""M5 esp32 based MCU modules with enclosure, TFT and LORA Shields. All Variants (Basic, Core, Fire, Core2, CoreS3, Paper) https://m5stack.com/""" +M5STACK_CORE2: HardwareModel.ValueType # 78 +RPI_PICO2: HardwareModel.ValueType # 79 +"""Pico2 with Waveshare Hat, same as Pico""" +M5STACK_CORES3: HardwareModel.ValueType # 80 +"""M5 esp32 based MCU modules with enclosure, TFT and LORA Shields. All Variants (Basic, Core, Fire, Core2, CoreS3, Paper) https://m5stack.com/""" +SEEED_XIAO_S3: HardwareModel.ValueType # 81 +"""Seeed XIAO S3 DK""" +MS24SF1: HardwareModel.ValueType # 82 +""" +Nordic nRF52840+Semtech SX1262 LoRa BLE Combo Module. nRF52840+SX1262 MS24SF1 +""" +TLORA_C6: HardwareModel.ValueType # 83 +""" +Lilygo TLora-C6 with the new ESP32-C6 MCU +""" +WISMESH_TAP: HardwareModel.ValueType # 84 +""" +WisMesh Tap +RAK-4631 w/ TFT in injection modled case +""" +ROUTASTIC: HardwareModel.ValueType # 85 +""" +Similar to PORTDUINO but used by Routastic devices, this is not any +particular device and does not run Meshtastic's code but supports +the same frame format. +Runs on linux, see https://github.com/Jorropo/routastic +""" +MESH_TAB: HardwareModel.ValueType # 86 +""" +Mesh-Tab, esp32 based +https://github.com/valzzu/Mesh-Tab +""" +MESHLINK: HardwareModel.ValueType # 87 +""" +MeshLink board developed by LoraItalia. NRF52840, eByte E22900M22S (Will also come with other frequencies), 25w MPPT solar charger (5v,12v,18v selectable), support for gps, buzzer, oled or e-ink display, 10 gpios, hardware watchdog +https://www.loraitalia.it +""" +XIAO_NRF52_KIT: HardwareModel.ValueType # 88 +""" +Seeed XIAO nRF52840 + Wio SX1262 kit +""" +THINKNODE_M1: HardwareModel.ValueType # 89 +""" +Elecrow ThinkNode M1 & M2 +https://www.elecrow.com/wiki/ThinkNode-M1_Transceiver_Device(Meshtastic)_Power_By_nRF52840.html +https://www.elecrow.com/wiki/ThinkNode-M2_Transceiver_Device(Meshtastic)_Power_By_NRF52840.html (this actually uses ESP32-S3) +""" +THINKNODE_M2: HardwareModel.ValueType # 90 +T_ETH_ELITE: HardwareModel.ValueType # 91 +""" +Lilygo T-ETH-Elite +""" +HELTEC_SENSOR_HUB: HardwareModel.ValueType # 92 +""" +Heltec HRI-3621 industrial probe +""" +RESERVED_FRIED_CHICKEN: HardwareModel.ValueType # 93 +""" +Reserved Fried Chicken ID for future use +""" +HELTEC_MESH_POCKET: HardwareModel.ValueType # 94 +""" +Heltec Magnetic Power Bank with Meshtastic compatible +""" +SEEED_SOLAR_NODE: HardwareModel.ValueType # 95 +""" +Seeed Solar Node +""" +NOMADSTAR_METEOR_PRO: HardwareModel.ValueType # 96 +""" +NomadStar Meteor Pro https://nomadstar.ch/ +""" +CROWPANEL: HardwareModel.ValueType # 97 +""" +Elecrow CrowPanel Advance models, ESP32-S3 and TFT with SX1262 radio plugin +""" +LINK_32: HardwareModel.ValueType # 98 +""" +Lilygo LINK32 board with sensors +""" +SEEED_WIO_TRACKER_L1: HardwareModel.ValueType # 99 +""" +Seeed Tracker L1 +""" +SEEED_WIO_TRACKER_L1_EINK: HardwareModel.ValueType # 100 +""" +Seeed Tracker L1 EINK driver +""" +MUZI_R1_NEO: HardwareModel.ValueType # 101 +""" +Muzi Works R1 Neo +""" +T_DECK_PRO: HardwareModel.ValueType # 102 +""" +Lilygo T-Deck Pro +""" +T_LORA_PAGER: HardwareModel.ValueType # 103 +""" +Lilygo TLora Pager +""" +M5STACK_RESERVED: HardwareModel.ValueType # 104 +""" +M5Stack Reserved +0x68 +""" +WISMESH_TAG: HardwareModel.ValueType # 105 +""" +RAKwireless WisMesh Tag +""" +RAK3312: HardwareModel.ValueType # 106 +""" +RAKwireless WisBlock Core RAK3312 https://docs.rakwireless.com/product-categories/wisduo/rak3112-module/overview/ +""" +THINKNODE_M5: HardwareModel.ValueType # 107 +""" +Elecrow ThinkNode M5 https://www.elecrow.com/wiki/ThinkNode_M5_Meshtastic_LoRa_Signal_Transceiver_ESP32-S3.html +""" +HELTEC_MESH_SOLAR: HardwareModel.ValueType # 108 +""" +MeshSolar is an integrated power management and communication solution designed for outdoor low-power devices. +https://heltec.org/project/meshsolar/ +""" +T_ECHO_LITE: HardwareModel.ValueType # 109 +""" +Lilygo T-Echo Lite +""" +HELTEC_V4: HardwareModel.ValueType # 110 +""" +New Heltec LoRA32 with ESP32-S3 CPU +""" +M5STACK_C6L: HardwareModel.ValueType # 111 +""" +M5Stack C6L +""" +M5STACK_CARDPUTER_ADV: HardwareModel.ValueType # 112 +""" +M5Stack Cardputer Adv +""" +HELTEC_WIRELESS_TRACKER_V2: HardwareModel.ValueType # 113 +""" +ESP32S3 main controller with GPS and TFT screen. +""" +T_WATCH_ULTRA: HardwareModel.ValueType # 114 +""" +LilyGo T-Watch Ultra +""" +THINKNODE_M3: HardwareModel.ValueType # 115 +""" +Elecrow ThinkNode M3 +""" +WISMESH_TAP_V2: HardwareModel.ValueType # 116 +""" +RAK WISMESH_TAP_V2 with ESP32-S3 CPU +""" +RAK3401: HardwareModel.ValueType # 117 +""" +RAK3401 +""" +PRIVATE_HW: HardwareModel.ValueType # 255 +""" +------------------------------------------------------------------------------------------------------------------------------------------ +Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits. +------------------------------------------------------------------------------------------------------------------------------------------ +""" +global___HardwareModel = HardwareModel + +class _Constants: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _ConstantsEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_Constants.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + ZERO: _Constants.ValueType # 0 + """ + First enum must be zero, and we are just using this enum to + pass int constants between two very different environments + """ + DATA_PAYLOAD_LEN: _Constants.ValueType # 233 + """ + From mesh.options + note: this payload length is ONLY the bytes that are sent inside of the Data protobuf (excluding protobuf overhead). The 16 byte header is + outside of this envelope + """ + +class Constants(_Constants, metaclass=_ConstantsEnumTypeWrapper): + """ + Shared constants between device and phone + """ + +ZERO: Constants.ValueType # 0 +""" +First enum must be zero, and we are just using this enum to +pass int constants between two very different environments +""" +DATA_PAYLOAD_LEN: Constants.ValueType # 233 +""" +From mesh.options +note: this payload length is ONLY the bytes that are sent inside of the Data protobuf (excluding protobuf overhead). The 16 byte header is +outside of this envelope +""" +global___Constants = Constants + +class _CriticalErrorCode: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _CriticalErrorCodeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_CriticalErrorCode.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + NONE: _CriticalErrorCode.ValueType # 0 + """ + TODO: REPLACE + """ + TX_WATCHDOG: _CriticalErrorCode.ValueType # 1 + """ + A software bug was detected while trying to send lora + """ + SLEEP_ENTER_WAIT: _CriticalErrorCode.ValueType # 2 + """ + A software bug was detected on entry to sleep + """ + NO_RADIO: _CriticalErrorCode.ValueType # 3 + """ + No Lora radio hardware could be found + """ + UNSPECIFIED: _CriticalErrorCode.ValueType # 4 + """ + Not normally used + """ + UBLOX_UNIT_FAILED: _CriticalErrorCode.ValueType # 5 + """ + We failed while configuring a UBlox GPS + """ + NO_AXP192: _CriticalErrorCode.ValueType # 6 + """ + This board was expected to have a power management chip and it is missing or broken + """ + INVALID_RADIO_SETTING: _CriticalErrorCode.ValueType # 7 + """ + The channel tried to set a radio setting which is not supported by this chipset, + radio comms settings are now undefined. + """ + TRANSMIT_FAILED: _CriticalErrorCode.ValueType # 8 + """ + Radio transmit hardware failure. We sent data to the radio chip, but it didn't + reply with an interrupt. + """ + BROWNOUT: _CriticalErrorCode.ValueType # 9 + """ + We detected that the main CPU voltage dropped below the minimum acceptable value + """ + SX1262_FAILURE: _CriticalErrorCode.ValueType # 10 + """Selftest of SX1262 radio chip failed""" + RADIO_SPI_BUG: _CriticalErrorCode.ValueType # 11 + """ + A (likely software but possibly hardware) failure was detected while trying to send packets. + If this occurs on your board, please post in the forum so that we can ask you to collect some information to allow fixing this bug + """ + FLASH_CORRUPTION_RECOVERABLE: _CriticalErrorCode.ValueType # 12 + """ + Corruption was detected on the flash filesystem but we were able to repair things. + If you see this failure in the field please post in the forum because we are interested in seeing if this is occurring in the field. + """ + FLASH_CORRUPTION_UNRECOVERABLE: _CriticalErrorCode.ValueType # 13 + """ + Corruption was detected on the flash filesystem but we were unable to repair things. + NOTE: Your node will probably need to be reconfigured the next time it reboots (it will lose the region code etc...) + If you see this failure in the field please post in the forum because we are interested in seeing if this is occurring in the field. + """ + +class CriticalErrorCode(_CriticalErrorCode, metaclass=_CriticalErrorCodeEnumTypeWrapper): + """ + Error codes for critical errors + The device might report these fault codes on the screen. + If you encounter a fault code, please post on the meshtastic.discourse.group + and we'll try to help. + """ + +NONE: CriticalErrorCode.ValueType # 0 +""" +TODO: REPLACE +""" +TX_WATCHDOG: CriticalErrorCode.ValueType # 1 +""" +A software bug was detected while trying to send lora +""" +SLEEP_ENTER_WAIT: CriticalErrorCode.ValueType # 2 +""" +A software bug was detected on entry to sleep +""" +NO_RADIO: CriticalErrorCode.ValueType # 3 +""" +No Lora radio hardware could be found +""" +UNSPECIFIED: CriticalErrorCode.ValueType # 4 +""" +Not normally used +""" +UBLOX_UNIT_FAILED: CriticalErrorCode.ValueType # 5 +""" +We failed while configuring a UBlox GPS +""" +NO_AXP192: CriticalErrorCode.ValueType # 6 +""" +This board was expected to have a power management chip and it is missing or broken +""" +INVALID_RADIO_SETTING: CriticalErrorCode.ValueType # 7 +""" +The channel tried to set a radio setting which is not supported by this chipset, +radio comms settings are now undefined. +""" +TRANSMIT_FAILED: CriticalErrorCode.ValueType # 8 +""" +Radio transmit hardware failure. We sent data to the radio chip, but it didn't +reply with an interrupt. +""" +BROWNOUT: CriticalErrorCode.ValueType # 9 +""" +We detected that the main CPU voltage dropped below the minimum acceptable value +""" +SX1262_FAILURE: CriticalErrorCode.ValueType # 10 +"""Selftest of SX1262 radio chip failed""" +RADIO_SPI_BUG: CriticalErrorCode.ValueType # 11 +""" +A (likely software but possibly hardware) failure was detected while trying to send packets. +If this occurs on your board, please post in the forum so that we can ask you to collect some information to allow fixing this bug +""" +FLASH_CORRUPTION_RECOVERABLE: CriticalErrorCode.ValueType # 12 +""" +Corruption was detected on the flash filesystem but we were able to repair things. +If you see this failure in the field please post in the forum because we are interested in seeing if this is occurring in the field. +""" +FLASH_CORRUPTION_UNRECOVERABLE: CriticalErrorCode.ValueType # 13 +""" +Corruption was detected on the flash filesystem but we were unable to repair things. +NOTE: Your node will probably need to be reconfigured the next time it reboots (it will lose the region code etc...) +If you see this failure in the field please post in the forum because we are interested in seeing if this is occurring in the field. +""" +global___CriticalErrorCode = CriticalErrorCode + +class _FirmwareEdition: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _FirmwareEditionEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_FirmwareEdition.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + VANILLA: _FirmwareEdition.ValueType # 0 + """ + Vanilla firmware + """ + SMART_CITIZEN: _FirmwareEdition.ValueType # 1 + """ + Firmware for use in the Smart Citizen environmental monitoring network + """ + OPEN_SAUCE: _FirmwareEdition.ValueType # 16 + """ + Open Sauce, the maker conference held yearly in CA + """ + DEFCON: _FirmwareEdition.ValueType # 17 + """ + DEFCON, the yearly hacker conference + """ + BURNING_MAN: _FirmwareEdition.ValueType # 18 + """ + Burning Man, the yearly hippie gathering in the desert + """ + HAMVENTION: _FirmwareEdition.ValueType # 19 + """ + Hamvention, the Dayton amateur radio convention + """ + DIY_EDITION: _FirmwareEdition.ValueType # 127 + """ + Placeholder for DIY and unofficial events + """ + +class FirmwareEdition(_FirmwareEdition, metaclass=_FirmwareEditionEnumTypeWrapper): + """ + Enum to indicate to clients whether this firmware is a special firmware build, like an event. + The first 16 values are reserved for non-event special firmwares, like the Smart Citizen use case. + """ + +VANILLA: FirmwareEdition.ValueType # 0 +""" +Vanilla firmware +""" +SMART_CITIZEN: FirmwareEdition.ValueType # 1 +""" +Firmware for use in the Smart Citizen environmental monitoring network +""" +OPEN_SAUCE: FirmwareEdition.ValueType # 16 +""" +Open Sauce, the maker conference held yearly in CA +""" +DEFCON: FirmwareEdition.ValueType # 17 +""" +DEFCON, the yearly hacker conference +""" +BURNING_MAN: FirmwareEdition.ValueType # 18 +""" +Burning Man, the yearly hippie gathering in the desert +""" +HAMVENTION: FirmwareEdition.ValueType # 19 +""" +Hamvention, the Dayton amateur radio convention +""" +DIY_EDITION: FirmwareEdition.ValueType # 127 +""" +Placeholder for DIY and unofficial events +""" +global___FirmwareEdition = FirmwareEdition + +class _ExcludedModules: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _ExcludedModulesEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_ExcludedModules.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + EXCLUDED_NONE: _ExcludedModules.ValueType # 0 + """ + Default value of 0 indicates no modules are excluded. + """ + MQTT_CONFIG: _ExcludedModules.ValueType # 1 + """ + MQTT module + """ + SERIAL_CONFIG: _ExcludedModules.ValueType # 2 + """ + Serial module + """ + EXTNOTIF_CONFIG: _ExcludedModules.ValueType # 4 + """ + External Notification module + """ + STOREFORWARD_CONFIG: _ExcludedModules.ValueType # 8 + """ + Store and Forward module + """ + RANGETEST_CONFIG: _ExcludedModules.ValueType # 16 + """ + Range Test module + """ + TELEMETRY_CONFIG: _ExcludedModules.ValueType # 32 + """ + Telemetry module + """ + CANNEDMSG_CONFIG: _ExcludedModules.ValueType # 64 + """ + Canned Message module + """ + AUDIO_CONFIG: _ExcludedModules.ValueType # 128 + """ + Audio module + """ + REMOTEHARDWARE_CONFIG: _ExcludedModules.ValueType # 256 + """ + Remote Hardware module + """ + NEIGHBORINFO_CONFIG: _ExcludedModules.ValueType # 512 + """ + Neighbor Info module + """ + AMBIENTLIGHTING_CONFIG: _ExcludedModules.ValueType # 1024 + """ + Ambient Lighting module + """ + DETECTIONSENSOR_CONFIG: _ExcludedModules.ValueType # 2048 + """ + Detection Sensor module + """ + PAXCOUNTER_CONFIG: _ExcludedModules.ValueType # 4096 + """ + Paxcounter module + """ + BLUETOOTH_CONFIG: _ExcludedModules.ValueType # 8192 + """ + Bluetooth config (not technically a module, but used to indicate bluetooth capabilities) + """ + NETWORK_CONFIG: _ExcludedModules.ValueType # 16384 + """ + Network config (not technically a module, but used to indicate network capabilities) + """ + +class ExcludedModules(_ExcludedModules, metaclass=_ExcludedModulesEnumTypeWrapper): + """ + Enum for modules excluded from a device's configuration. + Each value represents a ModuleConfigType that can be toggled as excluded + by setting its corresponding bit in the `excluded_modules` bitmask field. + """ + +EXCLUDED_NONE: ExcludedModules.ValueType # 0 +""" +Default value of 0 indicates no modules are excluded. +""" +MQTT_CONFIG: ExcludedModules.ValueType # 1 +""" +MQTT module +""" +SERIAL_CONFIG: ExcludedModules.ValueType # 2 +""" +Serial module +""" +EXTNOTIF_CONFIG: ExcludedModules.ValueType # 4 +""" +External Notification module +""" +STOREFORWARD_CONFIG: ExcludedModules.ValueType # 8 +""" +Store and Forward module +""" +RANGETEST_CONFIG: ExcludedModules.ValueType # 16 +""" +Range Test module +""" +TELEMETRY_CONFIG: ExcludedModules.ValueType # 32 +""" +Telemetry module +""" +CANNEDMSG_CONFIG: ExcludedModules.ValueType # 64 +""" +Canned Message module +""" +AUDIO_CONFIG: ExcludedModules.ValueType # 128 +""" +Audio module +""" +REMOTEHARDWARE_CONFIG: ExcludedModules.ValueType # 256 +""" +Remote Hardware module +""" +NEIGHBORINFO_CONFIG: ExcludedModules.ValueType # 512 +""" +Neighbor Info module +""" +AMBIENTLIGHTING_CONFIG: ExcludedModules.ValueType # 1024 +""" +Ambient Lighting module +""" +DETECTIONSENSOR_CONFIG: ExcludedModules.ValueType # 2048 +""" +Detection Sensor module +""" +PAXCOUNTER_CONFIG: ExcludedModules.ValueType # 4096 +""" +Paxcounter module +""" +BLUETOOTH_CONFIG: ExcludedModules.ValueType # 8192 +""" +Bluetooth config (not technically a module, but used to indicate bluetooth capabilities) +""" +NETWORK_CONFIG: ExcludedModules.ValueType # 16384 +""" +Network config (not technically a module, but used to indicate network capabilities) +""" +global___ExcludedModules = ExcludedModules + +@typing.final +class Position(google.protobuf.message.Message): + """ + A GPS Position + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + class _LocSource: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + + class _LocSourceEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[Position._LocSource.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + LOC_UNSET: Position._LocSource.ValueType # 0 + """ + TODO: REPLACE + """ + LOC_MANUAL: Position._LocSource.ValueType # 1 + """ + TODO: REPLACE + """ + LOC_INTERNAL: Position._LocSource.ValueType # 2 + """ + TODO: REPLACE + """ + LOC_EXTERNAL: Position._LocSource.ValueType # 3 + """ + TODO: REPLACE + """ + + class LocSource(_LocSource, metaclass=_LocSourceEnumTypeWrapper): + """ + How the location was acquired: manual, onboard GPS, external (EUD) GPS + """ + + LOC_UNSET: Position.LocSource.ValueType # 0 + """ + TODO: REPLACE + """ + LOC_MANUAL: Position.LocSource.ValueType # 1 + """ + TODO: REPLACE + """ + LOC_INTERNAL: Position.LocSource.ValueType # 2 + """ + TODO: REPLACE + """ + LOC_EXTERNAL: Position.LocSource.ValueType # 3 + """ + TODO: REPLACE + """ + + class _AltSource: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + + class _AltSourceEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[Position._AltSource.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + ALT_UNSET: Position._AltSource.ValueType # 0 + """ + TODO: REPLACE + """ + ALT_MANUAL: Position._AltSource.ValueType # 1 + """ + TODO: REPLACE + """ + ALT_INTERNAL: Position._AltSource.ValueType # 2 + """ + TODO: REPLACE + """ + ALT_EXTERNAL: Position._AltSource.ValueType # 3 + """ + TODO: REPLACE + """ + ALT_BAROMETRIC: Position._AltSource.ValueType # 4 + """ + TODO: REPLACE + """ + + class AltSource(_AltSource, metaclass=_AltSourceEnumTypeWrapper): + """ + How the altitude was acquired: manual, GPS int/ext, etc + Default: same as location_source if present + """ + + ALT_UNSET: Position.AltSource.ValueType # 0 + """ + TODO: REPLACE + """ + ALT_MANUAL: Position.AltSource.ValueType # 1 + """ + TODO: REPLACE + """ + ALT_INTERNAL: Position.AltSource.ValueType # 2 + """ + TODO: REPLACE + """ + ALT_EXTERNAL: Position.AltSource.ValueType # 3 + """ + TODO: REPLACE + """ + ALT_BAROMETRIC: Position.AltSource.ValueType # 4 + """ + TODO: REPLACE + """ + + LATITUDE_I_FIELD_NUMBER: builtins.int + LONGITUDE_I_FIELD_NUMBER: builtins.int + ALTITUDE_FIELD_NUMBER: builtins.int + TIME_FIELD_NUMBER: builtins.int + LOCATION_SOURCE_FIELD_NUMBER: builtins.int + ALTITUDE_SOURCE_FIELD_NUMBER: builtins.int + TIMESTAMP_FIELD_NUMBER: builtins.int + TIMESTAMP_MILLIS_ADJUST_FIELD_NUMBER: builtins.int + ALTITUDE_HAE_FIELD_NUMBER: builtins.int + ALTITUDE_GEOIDAL_SEPARATION_FIELD_NUMBER: builtins.int + PDOP_FIELD_NUMBER: builtins.int + HDOP_FIELD_NUMBER: builtins.int + VDOP_FIELD_NUMBER: builtins.int + GPS_ACCURACY_FIELD_NUMBER: builtins.int + GROUND_SPEED_FIELD_NUMBER: builtins.int + GROUND_TRACK_FIELD_NUMBER: builtins.int + FIX_QUALITY_FIELD_NUMBER: builtins.int + FIX_TYPE_FIELD_NUMBER: builtins.int + SATS_IN_VIEW_FIELD_NUMBER: builtins.int + SENSOR_ID_FIELD_NUMBER: builtins.int + NEXT_UPDATE_FIELD_NUMBER: builtins.int + SEQ_NUMBER_FIELD_NUMBER: builtins.int + PRECISION_BITS_FIELD_NUMBER: builtins.int + latitude_i: builtins.int + """ + The new preferred location encoding, multiply by 1e-7 to get degrees + in floating point + """ + longitude_i: builtins.int + """ + TODO: REPLACE + """ + altitude: builtins.int + """ + In meters above MSL (but see issue #359) + """ + time: builtins.int + """ + This is usually not sent over the mesh (to save space), but it is sent + from the phone so that the local device can set its time if it is sent over + the mesh (because there are devices on the mesh without GPS or RTC). + seconds since 1970 + """ + location_source: global___Position.LocSource.ValueType + """ + TODO: REPLACE + """ + altitude_source: global___Position.AltSource.ValueType + """ + TODO: REPLACE + """ + timestamp: builtins.int + """ + Positional timestamp (actual timestamp of GPS solution) in integer epoch seconds + """ + timestamp_millis_adjust: builtins.int + """ + Pos. timestamp milliseconds adjustment (rarely available or required) + """ + altitude_hae: builtins.int + """ + HAE altitude in meters - can be used instead of MSL altitude + """ + altitude_geoidal_separation: builtins.int + """ + Geoidal separation in meters + """ + PDOP: builtins.int + """ + Horizontal, Vertical and Position Dilution of Precision, in 1/100 units + - PDOP is sufficient for most cases + - for higher precision scenarios, HDOP and VDOP can be used instead, + in which case PDOP becomes redundant (PDOP=sqrt(HDOP^2 + VDOP^2)) + TODO: REMOVE/INTEGRATE + """ + HDOP: builtins.int + """ + TODO: REPLACE + """ + VDOP: builtins.int + """ + TODO: REPLACE + """ + gps_accuracy: builtins.int + """ + GPS accuracy (a hardware specific constant) in mm + multiplied with DOP to calculate positional accuracy + Default: "'bout three meters-ish" :) + """ + ground_speed: builtins.int + """ + Ground speed in m/s and True North TRACK in 1/100 degrees + Clarification of terms: + - "track" is the direction of motion (measured in horizontal plane) + - "heading" is where the fuselage points (measured in horizontal plane) + - "yaw" indicates a relative rotation about the vertical axis + TODO: REMOVE/INTEGRATE + """ + ground_track: builtins.int + """ + TODO: REPLACE + """ + fix_quality: builtins.int + """ + GPS fix quality (from NMEA GxGGA statement or similar) + """ + fix_type: builtins.int + """ + GPS fix type 2D/3D (from NMEA GxGSA statement) + """ + sats_in_view: builtins.int + """ + GPS "Satellites in View" number + """ + sensor_id: builtins.int + """ + Sensor ID - in case multiple positioning sensors are being used + """ + next_update: builtins.int + """ + Estimated/expected time (in seconds) until next update: + - if we update at fixed intervals of X seconds, use X + - if we update at dynamic intervals (based on relative movement etc), + but "AT LEAST every Y seconds", use Y + """ + seq_number: builtins.int + """ + A sequence number, incremented with each Position message to help + detect lost updates if needed + """ + precision_bits: builtins.int + """ + Indicates the bits of precision set by the sending node + """ + def __init__( + self, + *, + latitude_i: builtins.int | None = ..., + longitude_i: builtins.int | None = ..., + altitude: builtins.int | None = ..., + time: builtins.int = ..., + location_source: global___Position.LocSource.ValueType = ..., + altitude_source: global___Position.AltSource.ValueType = ..., + timestamp: builtins.int = ..., + timestamp_millis_adjust: builtins.int = ..., + altitude_hae: builtins.int | None = ..., + altitude_geoidal_separation: builtins.int | None = ..., + PDOP: builtins.int = ..., + HDOP: builtins.int = ..., + VDOP: builtins.int = ..., + gps_accuracy: builtins.int = ..., + ground_speed: builtins.int | None = ..., + ground_track: builtins.int | None = ..., + fix_quality: builtins.int = ..., + fix_type: builtins.int = ..., + sats_in_view: builtins.int = ..., + sensor_id: builtins.int = ..., + next_update: builtins.int = ..., + seq_number: builtins.int = ..., + precision_bits: builtins.int = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["_altitude", b"_altitude", "_altitude_geoidal_separation", b"_altitude_geoidal_separation", "_altitude_hae", b"_altitude_hae", "_ground_speed", b"_ground_speed", "_ground_track", b"_ground_track", "_latitude_i", b"_latitude_i", "_longitude_i", b"_longitude_i", "altitude", b"altitude", "altitude_geoidal_separation", b"altitude_geoidal_separation", "altitude_hae", b"altitude_hae", "ground_speed", b"ground_speed", "ground_track", b"ground_track", "latitude_i", b"latitude_i", "longitude_i", b"longitude_i"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["HDOP", b"HDOP", "PDOP", b"PDOP", "VDOP", b"VDOP", "_altitude", b"_altitude", "_altitude_geoidal_separation", b"_altitude_geoidal_separation", "_altitude_hae", b"_altitude_hae", "_ground_speed", b"_ground_speed", "_ground_track", b"_ground_track", "_latitude_i", b"_latitude_i", "_longitude_i", b"_longitude_i", "altitude", b"altitude", "altitude_geoidal_separation", b"altitude_geoidal_separation", "altitude_hae", b"altitude_hae", "altitude_source", b"altitude_source", "fix_quality", b"fix_quality", "fix_type", b"fix_type", "gps_accuracy", b"gps_accuracy", "ground_speed", b"ground_speed", "ground_track", b"ground_track", "latitude_i", b"latitude_i", "location_source", b"location_source", "longitude_i", b"longitude_i", "next_update", b"next_update", "precision_bits", b"precision_bits", "sats_in_view", b"sats_in_view", "sensor_id", b"sensor_id", "seq_number", b"seq_number", "time", b"time", "timestamp", b"timestamp", "timestamp_millis_adjust", b"timestamp_millis_adjust"]) -> None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_altitude", b"_altitude"]) -> typing.Literal["altitude"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_altitude_geoidal_separation", b"_altitude_geoidal_separation"]) -> typing.Literal["altitude_geoidal_separation"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_altitude_hae", b"_altitude_hae"]) -> typing.Literal["altitude_hae"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_ground_speed", b"_ground_speed"]) -> typing.Literal["ground_speed"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_ground_track", b"_ground_track"]) -> typing.Literal["ground_track"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_latitude_i", b"_latitude_i"]) -> typing.Literal["latitude_i"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_longitude_i", b"_longitude_i"]) -> typing.Literal["longitude_i"] | None: ... + +global___Position = Position + +@typing.final +class User(google.protobuf.message.Message): + """ + Broadcast when a newly powered mesh node wants to find a node num it can use + Sent from the phone over bluetooth to set the user id for the owner of this node. + Also sent from nodes to each other when a new node signs on (so all clients can have this info) + The algorithm is as follows: + when a node starts up, it broadcasts their user and the normal flow is for all + other nodes to reply with their User as well (so the new node can build its nodedb) + If a node ever receives a User (not just the first broadcast) message where + the sender node number equals our node number, that indicates a collision has + occurred and the following steps should happen: + If the receiving node (that was already in the mesh)'s macaddr is LOWER than the + new User who just tried to sign in: it gets to keep its nodenum. + We send a broadcast message of OUR User (we use a broadcast so that the other node can + receive our message, considering we have the same id - it also serves to let + observers correct their nodedb) - this case is rare so it should be okay. + If any node receives a User where the macaddr is GTE than their local macaddr, + they have been vetoed and should pick a new random nodenum (filtering against + whatever it knows about the nodedb) and rebroadcast their User. + A few nodenums are reserved and will never be requested: + 0xff - broadcast + 0 through 3 - for future use + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ID_FIELD_NUMBER: builtins.int + LONG_NAME_FIELD_NUMBER: builtins.int + SHORT_NAME_FIELD_NUMBER: builtins.int + MACADDR_FIELD_NUMBER: builtins.int + HW_MODEL_FIELD_NUMBER: builtins.int + IS_LICENSED_FIELD_NUMBER: builtins.int + ROLE_FIELD_NUMBER: builtins.int + PUBLIC_KEY_FIELD_NUMBER: builtins.int + IS_UNMESSAGABLE_FIELD_NUMBER: builtins.int + id: builtins.str + """ + A globally unique ID string for this user. + In the case of Signal that would mean +16504442323, for the default macaddr derived id it would be !<8 hexidecimal bytes>. + Note: app developers are encouraged to also use the following standard + node IDs "^all" (for broadcast), "^local" (for the locally connected node) + """ + long_name: builtins.str + """ + A full name for this user, i.e. "Kevin Hester" + """ + short_name: builtins.str + """ + A VERY short name, ideally two characters. + Suitable for a tiny OLED screen + """ + macaddr: builtins.bytes + """ + Deprecated in Meshtastic 2.1.x + This is the addr of the radio. + Not populated by the phone, but added by the esp32 when broadcasting + """ + hw_model: global___HardwareModel.ValueType + """ + TBEAM, HELTEC, etc... + Starting in 1.2.11 moved to hw_model enum in the NodeInfo object. + Apps will still need the string here for older builds + (so OTA update can find the right image), but if the enum is available it will be used instead. + """ + is_licensed: builtins.bool + """ + In some regions Ham radio operators have different bandwidth limitations than others. + If this user is a licensed operator, set this flag. + Also, "long_name" should be their licence number. + """ + role: meshtastic.protobuf.config_pb2.Config.DeviceConfig.Role.ValueType + """ + Indicates that the user's role in the mesh + """ + public_key: builtins.bytes + """ + The public key of the user's device. + This is sent out to other nodes on the mesh to allow them to compute a shared secret key. + """ + is_unmessagable: builtins.bool + """ + Whether or not the node can be messaged + """ + def __init__( + self, + *, + id: builtins.str = ..., + long_name: builtins.str = ..., + short_name: builtins.str = ..., + macaddr: builtins.bytes = ..., + hw_model: global___HardwareModel.ValueType = ..., + is_licensed: builtins.bool = ..., + role: meshtastic.protobuf.config_pb2.Config.DeviceConfig.Role.ValueType = ..., + public_key: builtins.bytes = ..., + is_unmessagable: builtins.bool | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["_is_unmessagable", b"_is_unmessagable", "is_unmessagable", b"is_unmessagable"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["_is_unmessagable", b"_is_unmessagable", "hw_model", b"hw_model", "id", b"id", "is_licensed", b"is_licensed", "is_unmessagable", b"is_unmessagable", "long_name", b"long_name", "macaddr", b"macaddr", "public_key", b"public_key", "role", b"role", "short_name", b"short_name"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["_is_unmessagable", b"_is_unmessagable"]) -> typing.Literal["is_unmessagable"] | None: ... + +global___User = User + +@typing.final +class RouteDiscovery(google.protobuf.message.Message): + """ + A message used in a traceroute + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ROUTE_FIELD_NUMBER: builtins.int + SNR_TOWARDS_FIELD_NUMBER: builtins.int + ROUTE_BACK_FIELD_NUMBER: builtins.int + SNR_BACK_FIELD_NUMBER: builtins.int + @property + def route(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.int]: + """ + The list of nodenums this packet has visited so far to the destination. + """ + + @property + def snr_towards(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.int]: + """ + The list of SNRs (in dB, scaled by 4) in the route towards the destination. + """ + + @property + def route_back(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.int]: + """ + The list of nodenums the packet has visited on the way back from the destination. + """ + + @property + def snr_back(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.int]: + """ + The list of SNRs (in dB, scaled by 4) in the route back from the destination. + """ + + def __init__( + self, + *, + route: collections.abc.Iterable[builtins.int] | None = ..., + snr_towards: collections.abc.Iterable[builtins.int] | None = ..., + route_back: collections.abc.Iterable[builtins.int] | None = ..., + snr_back: collections.abc.Iterable[builtins.int] | None = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["route", b"route", "route_back", b"route_back", "snr_back", b"snr_back", "snr_towards", b"snr_towards"]) -> None: ... + +global___RouteDiscovery = RouteDiscovery + +@typing.final +class Routing(google.protobuf.message.Message): + """ + A Routing control Data packet handled by the routing module + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + class _Error: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + + class _ErrorEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[Routing._Error.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + NONE: Routing._Error.ValueType # 0 + """ + This message is not a failure + """ + NO_ROUTE: Routing._Error.ValueType # 1 + """ + Our node doesn't have a route to the requested destination anymore. + """ + GOT_NAK: Routing._Error.ValueType # 2 + """ + We received a nak while trying to forward on your behalf + """ + TIMEOUT: Routing._Error.ValueType # 3 + """ + TODO: REPLACE + """ + NO_INTERFACE: Routing._Error.ValueType # 4 + """ + No suitable interface could be found for delivering this packet + """ + MAX_RETRANSMIT: Routing._Error.ValueType # 5 + """ + We reached the max retransmission count (typically for naive flood routing) + """ + NO_CHANNEL: Routing._Error.ValueType # 6 + """ + No suitable channel was found for sending this packet (i.e. was requested channel index disabled?) + """ + TOO_LARGE: Routing._Error.ValueType # 7 + """ + The packet was too big for sending (exceeds interface MTU after encoding) + """ + NO_RESPONSE: Routing._Error.ValueType # 8 + """ + The request had want_response set, the request reached the destination node, but no service on that node wants to send a response + (possibly due to bad channel permissions) + """ + DUTY_CYCLE_LIMIT: Routing._Error.ValueType # 9 + """ + Cannot send currently because duty cycle regulations will be violated. + """ + BAD_REQUEST: Routing._Error.ValueType # 32 + """ + The application layer service on the remote node received your request, but considered your request somehow invalid + """ + NOT_AUTHORIZED: Routing._Error.ValueType # 33 + """ + The application layer service on the remote node received your request, but considered your request not authorized + (i.e you did not send the request on the required bound channel) + """ + PKI_FAILED: Routing._Error.ValueType # 34 + """ + The client specified a PKI transport, but the node was unable to send the packet using PKI (and did not send the message at all) + """ + PKI_UNKNOWN_PUBKEY: Routing._Error.ValueType # 35 + """ + The receiving node does not have a Public Key to decode with + """ + ADMIN_BAD_SESSION_KEY: Routing._Error.ValueType # 36 + """ + Admin packet otherwise checks out, but uses a bogus or expired session key + """ + ADMIN_PUBLIC_KEY_UNAUTHORIZED: Routing._Error.ValueType # 37 + """ + Admin packet sent using PKC, but not from a public key on the admin key list + """ + RATE_LIMIT_EXCEEDED: Routing._Error.ValueType # 38 + """ + Airtime fairness rate limit exceeded for a packet + This typically enforced per portnum and is used to prevent a single node from monopolizing airtime + """ + + class Error(_Error, metaclass=_ErrorEnumTypeWrapper): + """ + A failure in delivering a message (usually used for routing control messages, but might be provided in addition to ack.fail_id to provide + details on the type of failure). + """ + + NONE: Routing.Error.ValueType # 0 + """ + This message is not a failure + """ + NO_ROUTE: Routing.Error.ValueType # 1 + """ + Our node doesn't have a route to the requested destination anymore. + """ + GOT_NAK: Routing.Error.ValueType # 2 + """ + We received a nak while trying to forward on your behalf + """ + TIMEOUT: Routing.Error.ValueType # 3 + """ + TODO: REPLACE + """ + NO_INTERFACE: Routing.Error.ValueType # 4 + """ + No suitable interface could be found for delivering this packet + """ + MAX_RETRANSMIT: Routing.Error.ValueType # 5 + """ + We reached the max retransmission count (typically for naive flood routing) + """ + NO_CHANNEL: Routing.Error.ValueType # 6 + """ + No suitable channel was found for sending this packet (i.e. was requested channel index disabled?) + """ + TOO_LARGE: Routing.Error.ValueType # 7 + """ + The packet was too big for sending (exceeds interface MTU after encoding) + """ + NO_RESPONSE: Routing.Error.ValueType # 8 + """ + The request had want_response set, the request reached the destination node, but no service on that node wants to send a response + (possibly due to bad channel permissions) + """ + DUTY_CYCLE_LIMIT: Routing.Error.ValueType # 9 + """ + Cannot send currently because duty cycle regulations will be violated. + """ + BAD_REQUEST: Routing.Error.ValueType # 32 + """ + The application layer service on the remote node received your request, but considered your request somehow invalid + """ + NOT_AUTHORIZED: Routing.Error.ValueType # 33 + """ + The application layer service on the remote node received your request, but considered your request not authorized + (i.e you did not send the request on the required bound channel) + """ + PKI_FAILED: Routing.Error.ValueType # 34 + """ + The client specified a PKI transport, but the node was unable to send the packet using PKI (and did not send the message at all) + """ + PKI_UNKNOWN_PUBKEY: Routing.Error.ValueType # 35 + """ + The receiving node does not have a Public Key to decode with + """ + ADMIN_BAD_SESSION_KEY: Routing.Error.ValueType # 36 + """ + Admin packet otherwise checks out, but uses a bogus or expired session key + """ + ADMIN_PUBLIC_KEY_UNAUTHORIZED: Routing.Error.ValueType # 37 + """ + Admin packet sent using PKC, but not from a public key on the admin key list + """ + RATE_LIMIT_EXCEEDED: Routing.Error.ValueType # 38 + """ + Airtime fairness rate limit exceeded for a packet + This typically enforced per portnum and is used to prevent a single node from monopolizing airtime + """ + + ROUTE_REQUEST_FIELD_NUMBER: builtins.int + ROUTE_REPLY_FIELD_NUMBER: builtins.int + ERROR_REASON_FIELD_NUMBER: builtins.int + error_reason: global___Routing.Error.ValueType + """ + A failure in delivering a message (usually used for routing control messages, but might be provided + in addition to ack.fail_id to provide details on the type of failure). + """ + @property + def route_request(self) -> global___RouteDiscovery: + """ + A route request going from the requester + """ + + @property + def route_reply(self) -> global___RouteDiscovery: + """ + A route reply + """ + + def __init__( + self, + *, + route_request: global___RouteDiscovery | None = ..., + route_reply: global___RouteDiscovery | None = ..., + error_reason: global___Routing.Error.ValueType = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["error_reason", b"error_reason", "route_reply", b"route_reply", "route_request", b"route_request", "variant", b"variant"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["error_reason", b"error_reason", "route_reply", b"route_reply", "route_request", b"route_request", "variant", b"variant"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["variant", b"variant"]) -> typing.Literal["route_request", "route_reply", "error_reason"] | None: ... + +global___Routing = Routing + +@typing.final +class Data(google.protobuf.message.Message): + """ + (Formerly called SubPacket) + The payload portion fo a packet, this is the actual bytes that are sent + inside a radio packet (because from/to are broken out by the comms library) + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + PORTNUM_FIELD_NUMBER: builtins.int + PAYLOAD_FIELD_NUMBER: builtins.int + WANT_RESPONSE_FIELD_NUMBER: builtins.int + DEST_FIELD_NUMBER: builtins.int + SOURCE_FIELD_NUMBER: builtins.int + REQUEST_ID_FIELD_NUMBER: builtins.int + REPLY_ID_FIELD_NUMBER: builtins.int + EMOJI_FIELD_NUMBER: builtins.int + BITFIELD_FIELD_NUMBER: builtins.int + portnum: meshtastic.protobuf.portnums_pb2.PortNum.ValueType + """ + Formerly named typ and of type Type + """ + payload: builtins.bytes + """ + TODO: REPLACE + """ + want_response: builtins.bool + """ + Not normally used, but for testing a sender can request that recipient + responds in kind (i.e. if it received a position, it should unicast back it's position). + Note: that if you set this on a broadcast you will receive many replies. + """ + dest: builtins.int + """ + The address of the destination node. + This field is is filled in by the mesh radio device software, application + layer software should never need it. + RouteDiscovery messages _must_ populate this. + Other message types might need to if they are doing multihop routing. + """ + source: builtins.int + """ + The address of the original sender for this message. + This field should _only_ be populated for reliable multihop packets (to keep + packets small). + """ + request_id: builtins.int + """ + Only used in routing or response messages. + Indicates the original message ID that this message is reporting failure on. (formerly called original_id) + """ + reply_id: builtins.int + """ + If set, this message is intened to be a reply to a previously sent message with the defined id. + """ + emoji: builtins.int + """ + Defaults to false. If true, then what is in the payload should be treated as an emoji like giving + a message a heart or poop emoji. + """ + bitfield: builtins.int + """ + Bitfield for extra flags. First use is to indicate that user approves the packet being uploaded to MQTT. + """ + def __init__( + self, + *, + portnum: meshtastic.protobuf.portnums_pb2.PortNum.ValueType = ..., + payload: builtins.bytes = ..., + want_response: builtins.bool = ..., + dest: builtins.int = ..., + source: builtins.int = ..., + request_id: builtins.int = ..., + reply_id: builtins.int = ..., + emoji: builtins.int = ..., + bitfield: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["_bitfield", b"_bitfield", "bitfield", b"bitfield"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["_bitfield", b"_bitfield", "bitfield", b"bitfield", "dest", b"dest", "emoji", b"emoji", "payload", b"payload", "portnum", b"portnum", "reply_id", b"reply_id", "request_id", b"request_id", "source", b"source", "want_response", b"want_response"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["_bitfield", b"_bitfield"]) -> typing.Literal["bitfield"] | None: ... + +global___Data = Data + +@typing.final +class KeyVerification(google.protobuf.message.Message): + """ + The actual over-the-mesh message doing KeyVerification + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + NONCE_FIELD_NUMBER: builtins.int + HASH1_FIELD_NUMBER: builtins.int + HASH2_FIELD_NUMBER: builtins.int + nonce: builtins.int + """ + random value Selected by the requesting node + """ + hash1: builtins.bytes + """ + The final authoritative hash, only to be sent by NodeA at the end of the handshake + """ + hash2: builtins.bytes + """ + The intermediary hash (actually derived from hash1), + sent from NodeB to NodeA in response to the initial message. + """ + def __init__( + self, + *, + nonce: builtins.int = ..., + hash1: builtins.bytes = ..., + hash2: builtins.bytes = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["hash1", b"hash1", "hash2", b"hash2", "nonce", b"nonce"]) -> None: ... + +global___KeyVerification = KeyVerification + +@typing.final +class Waypoint(google.protobuf.message.Message): + """ + Waypoint message, used to share arbitrary locations across the mesh + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ID_FIELD_NUMBER: builtins.int + LATITUDE_I_FIELD_NUMBER: builtins.int + LONGITUDE_I_FIELD_NUMBER: builtins.int + EXPIRE_FIELD_NUMBER: builtins.int + LOCKED_TO_FIELD_NUMBER: builtins.int + NAME_FIELD_NUMBER: builtins.int + DESCRIPTION_FIELD_NUMBER: builtins.int + ICON_FIELD_NUMBER: builtins.int + id: builtins.int + """ + Id of the waypoint + """ + latitude_i: builtins.int + """ + latitude_i + """ + longitude_i: builtins.int + """ + longitude_i + """ + expire: builtins.int + """ + Time the waypoint is to expire (epoch) + """ + locked_to: builtins.int + """ + If greater than zero, treat the value as a nodenum only allowing them to update the waypoint. + If zero, the waypoint is open to be edited by any member of the mesh. + """ + name: builtins.str + """ + Name of the waypoint - max 30 chars + """ + description: builtins.str + """ + Description of the waypoint - max 100 chars + """ + icon: builtins.int + """ + Designator icon for the waypoint in the form of a unicode emoji + """ + def __init__( + self, + *, + id: builtins.int = ..., + latitude_i: builtins.int | None = ..., + longitude_i: builtins.int | None = ..., + expire: builtins.int = ..., + locked_to: builtins.int = ..., + name: builtins.str = ..., + description: builtins.str = ..., + icon: builtins.int = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["_latitude_i", b"_latitude_i", "_longitude_i", b"_longitude_i", "latitude_i", b"latitude_i", "longitude_i", b"longitude_i"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["_latitude_i", b"_latitude_i", "_longitude_i", b"_longitude_i", "description", b"description", "expire", b"expire", "icon", b"icon", "id", b"id", "latitude_i", b"latitude_i", "locked_to", b"locked_to", "longitude_i", b"longitude_i", "name", b"name"]) -> None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_latitude_i", b"_latitude_i"]) -> typing.Literal["latitude_i"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_longitude_i", b"_longitude_i"]) -> typing.Literal["longitude_i"] | None: ... + +global___Waypoint = Waypoint + +@typing.final +class MqttClientProxyMessage(google.protobuf.message.Message): + """ + This message will be proxied over the PhoneAPI for the client to deliver to the MQTT server + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + TOPIC_FIELD_NUMBER: builtins.int + DATA_FIELD_NUMBER: builtins.int + TEXT_FIELD_NUMBER: builtins.int + RETAINED_FIELD_NUMBER: builtins.int + topic: builtins.str + """ + The MQTT topic this message will be sent /received on + """ + data: builtins.bytes + """ + Bytes + """ + text: builtins.str + """ + Text + """ + retained: builtins.bool + """ + Whether the message should be retained (or not) + """ + def __init__( + self, + *, + topic: builtins.str = ..., + data: builtins.bytes = ..., + text: builtins.str = ..., + retained: builtins.bool = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["data", b"data", "payload_variant", b"payload_variant", "text", b"text"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["data", b"data", "payload_variant", b"payload_variant", "retained", b"retained", "text", b"text", "topic", b"topic"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["payload_variant", b"payload_variant"]) -> typing.Literal["data", "text"] | None: ... + +global___MqttClientProxyMessage = MqttClientProxyMessage + +@typing.final +class MeshPacket(google.protobuf.message.Message): + """ + A packet envelope sent/received over the mesh + only payload_variant is sent in the payload portion of the LORA packet. + The other fields are either not sent at all, or sent in the special 16 byte LORA header. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + class _Priority: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + + class _PriorityEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[MeshPacket._Priority.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + UNSET: MeshPacket._Priority.ValueType # 0 + """ + Treated as Priority.DEFAULT + """ + MIN: MeshPacket._Priority.ValueType # 1 + """ + TODO: REPLACE + """ + BACKGROUND: MeshPacket._Priority.ValueType # 10 + """ + Background position updates are sent with very low priority - + if the link is super congested they might not go out at all + """ + DEFAULT: MeshPacket._Priority.ValueType # 64 + """ + This priority is used for most messages that don't have a priority set + """ + RELIABLE: MeshPacket._Priority.ValueType # 70 + """ + If priority is unset but the message is marked as want_ack, + assume it is important and use a slightly higher priority + """ + RESPONSE: MeshPacket._Priority.ValueType # 80 + """ + If priority is unset but the packet is a response to a request, we want it to get there relatively quickly. + Furthermore, responses stop relaying packets directed to a node early. + """ + HIGH: MeshPacket._Priority.ValueType # 100 + """ + Higher priority for specific message types (portnums) to distinguish between other reliable packets. + """ + ALERT: MeshPacket._Priority.ValueType # 110 + """ + Higher priority alert message used for critical alerts which take priority over other reliable packets. + """ + ACK: MeshPacket._Priority.ValueType # 120 + """ + Ack/naks are sent with very high priority to ensure that retransmission + stops as soon as possible + """ + MAX: MeshPacket._Priority.ValueType # 127 + """ + TODO: REPLACE + """ + + class Priority(_Priority, metaclass=_PriorityEnumTypeWrapper): + """ + The priority of this message for sending. + Higher priorities are sent first (when managing the transmit queue). + This field is never sent over the air, it is only used internally inside of a local device node. + API clients (either on the local node or connected directly to the node) + can set this parameter if necessary. + (values must be <= 127 to keep protobuf field to one byte in size. + Detailed background on this field: + I noticed a funny side effect of lora being so slow: Usually when making + a protocol there isn’t much need to use message priority to change the order + of transmission (because interfaces are fairly fast). + But for lora where packets can take a few seconds each, it is very important + to make sure that critical packets are sent ASAP. + In the case of meshtastic that means we want to send protocol acks as soon as possible + (to prevent unneeded retransmissions), we want routing messages to be sent next, + then messages marked as reliable and finally 'background' packets like periodic position updates. + So I bit the bullet and implemented a new (internal - not sent over the air) + field in MeshPacket called 'priority'. + And the transmission queue in the router object is now a priority queue. + """ + + UNSET: MeshPacket.Priority.ValueType # 0 + """ + Treated as Priority.DEFAULT + """ + MIN: MeshPacket.Priority.ValueType # 1 + """ + TODO: REPLACE + """ + BACKGROUND: MeshPacket.Priority.ValueType # 10 + """ + Background position updates are sent with very low priority - + if the link is super congested they might not go out at all + """ + DEFAULT: MeshPacket.Priority.ValueType # 64 + """ + This priority is used for most messages that don't have a priority set + """ + RELIABLE: MeshPacket.Priority.ValueType # 70 + """ + If priority is unset but the message is marked as want_ack, + assume it is important and use a slightly higher priority + """ + RESPONSE: MeshPacket.Priority.ValueType # 80 + """ + If priority is unset but the packet is a response to a request, we want it to get there relatively quickly. + Furthermore, responses stop relaying packets directed to a node early. + """ + HIGH: MeshPacket.Priority.ValueType # 100 + """ + Higher priority for specific message types (portnums) to distinguish between other reliable packets. + """ + ALERT: MeshPacket.Priority.ValueType # 110 + """ + Higher priority alert message used for critical alerts which take priority over other reliable packets. + """ + ACK: MeshPacket.Priority.ValueType # 120 + """ + Ack/naks are sent with very high priority to ensure that retransmission + stops as soon as possible + """ + MAX: MeshPacket.Priority.ValueType # 127 + """ + TODO: REPLACE + """ + + class _Delayed: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + + class _DelayedEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[MeshPacket._Delayed.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + NO_DELAY: MeshPacket._Delayed.ValueType # 0 + """ + If unset, the message is being sent in real time. + """ + DELAYED_BROADCAST: MeshPacket._Delayed.ValueType # 1 + """ + The message is delayed and was originally a broadcast + """ + DELAYED_DIRECT: MeshPacket._Delayed.ValueType # 2 + """ + The message is delayed and was originally a direct message + """ + + class Delayed(_Delayed, metaclass=_DelayedEnumTypeWrapper): + """ + Identify if this is a delayed packet + """ + + NO_DELAY: MeshPacket.Delayed.ValueType # 0 + """ + If unset, the message is being sent in real time. + """ + DELAYED_BROADCAST: MeshPacket.Delayed.ValueType # 1 + """ + The message is delayed and was originally a broadcast + """ + DELAYED_DIRECT: MeshPacket.Delayed.ValueType # 2 + """ + The message is delayed and was originally a direct message + """ + + class _TransportMechanism: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + + class _TransportMechanismEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[MeshPacket._TransportMechanism.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + TRANSPORT_INTERNAL: MeshPacket._TransportMechanism.ValueType # 0 + """ + The default case is that the node generated a packet itself + """ + TRANSPORT_LORA: MeshPacket._TransportMechanism.ValueType # 1 + """ + Arrived via the primary LoRa radio + """ + TRANSPORT_LORA_ALT1: MeshPacket._TransportMechanism.ValueType # 2 + """ + Arrived via a secondary LoRa radio + """ + TRANSPORT_LORA_ALT2: MeshPacket._TransportMechanism.ValueType # 3 + """ + Arrived via a tertiary LoRa radio + """ + TRANSPORT_LORA_ALT3: MeshPacket._TransportMechanism.ValueType # 4 + """ + Arrived via a quaternary LoRa radio + """ + TRANSPORT_MQTT: MeshPacket._TransportMechanism.ValueType # 5 + """ + Arrived via an MQTT connection + """ + TRANSPORT_MULTICAST_UDP: MeshPacket._TransportMechanism.ValueType # 6 + """ + Arrived via Multicast UDP + """ + TRANSPORT_API: MeshPacket._TransportMechanism.ValueType # 7 + """ + Arrived via API connection + """ + + class TransportMechanism(_TransportMechanism, metaclass=_TransportMechanismEnumTypeWrapper): + """ + Enum to identify which transport mechanism this packet arrived over + """ + + TRANSPORT_INTERNAL: MeshPacket.TransportMechanism.ValueType # 0 + """ + The default case is that the node generated a packet itself + """ + TRANSPORT_LORA: MeshPacket.TransportMechanism.ValueType # 1 + """ + Arrived via the primary LoRa radio + """ + TRANSPORT_LORA_ALT1: MeshPacket.TransportMechanism.ValueType # 2 + """ + Arrived via a secondary LoRa radio + """ + TRANSPORT_LORA_ALT2: MeshPacket.TransportMechanism.ValueType # 3 + """ + Arrived via a tertiary LoRa radio + """ + TRANSPORT_LORA_ALT3: MeshPacket.TransportMechanism.ValueType # 4 + """ + Arrived via a quaternary LoRa radio + """ + TRANSPORT_MQTT: MeshPacket.TransportMechanism.ValueType # 5 + """ + Arrived via an MQTT connection + """ + TRANSPORT_MULTICAST_UDP: MeshPacket.TransportMechanism.ValueType # 6 + """ + Arrived via Multicast UDP + """ + TRANSPORT_API: MeshPacket.TransportMechanism.ValueType # 7 + """ + Arrived via API connection + """ + + FROM_FIELD_NUMBER: builtins.int + TO_FIELD_NUMBER: builtins.int + CHANNEL_FIELD_NUMBER: builtins.int + DECODED_FIELD_NUMBER: builtins.int + ENCRYPTED_FIELD_NUMBER: builtins.int + ID_FIELD_NUMBER: builtins.int + RX_TIME_FIELD_NUMBER: builtins.int + RX_SNR_FIELD_NUMBER: builtins.int + HOP_LIMIT_FIELD_NUMBER: builtins.int + WANT_ACK_FIELD_NUMBER: builtins.int + PRIORITY_FIELD_NUMBER: builtins.int + RX_RSSI_FIELD_NUMBER: builtins.int + DELAYED_FIELD_NUMBER: builtins.int + VIA_MQTT_FIELD_NUMBER: builtins.int + HOP_START_FIELD_NUMBER: builtins.int + PUBLIC_KEY_FIELD_NUMBER: builtins.int + PKI_ENCRYPTED_FIELD_NUMBER: builtins.int + NEXT_HOP_FIELD_NUMBER: builtins.int + RELAY_NODE_FIELD_NUMBER: builtins.int + TX_AFTER_FIELD_NUMBER: builtins.int + TRANSPORT_MECHANISM_FIELD_NUMBER: builtins.int + to: builtins.int + """ + The (immediate) destination for this packet + """ + channel: builtins.int + """ + (Usually) If set, this indicates the index in the secondary_channels table that this packet was sent/received on. + If unset, packet was on the primary channel. + A particular node might know only a subset of channels in use on the mesh. + Therefore channel_index is inherently a local concept and meaningless to send between nodes. + Very briefly, while sending and receiving deep inside the device Router code, this field instead + contains the 'channel hash' instead of the index. + This 'trick' is only used while the payload_variant is an 'encrypted'. + """ + encrypted: builtins.bytes + """ + TODO: REPLACE + """ + id: builtins.int + """ + A unique ID for this packet. + Always 0 for no-ack packets or non broadcast packets (and therefore take zero bytes of space). + Otherwise a unique ID for this packet, useful for flooding algorithms. + ID only needs to be unique on a _per sender_ basis, and it only + needs to be unique for a few minutes (long enough to last for the length of + any ACK or the completion of a mesh broadcast flood). + Note: Our crypto implementation uses this id as well. + See [crypto](/docs/overview/encryption) for details. + """ + rx_time: builtins.int + """ + The time this message was received by the esp32 (secs since 1970). + Note: this field is _never_ sent on the radio link itself (to save space) Times + are typically not sent over the mesh, but they will be added to any Packet + (chain of SubPacket) sent to the phone (so the phone can know exact time of reception) + """ + rx_snr: builtins.float + """ + *Never* sent over the radio links. + Set during reception to indicate the SNR of this packet. + Used to collect statistics on current link quality. + """ + hop_limit: builtins.int + """ + If unset treated as zero (no forwarding, send to direct neighbor nodes only) + if 1, allow hopping through one node, etc... + For our usecase real world topologies probably have a max of about 3. + This field is normally placed into a few of bits in the header. + """ + want_ack: builtins.bool + """ + This packet is being sent as a reliable message, we would prefer it to arrive at the destination. + We would like to receive a ack packet in response. + Broadcasts messages treat this flag specially: Since acks for broadcasts would + rapidly flood the channel, the normal ack behavior is suppressed. + Instead, the original sender listens to see if at least one node is rebroadcasting this packet (because naive flooding algorithm). + If it hears that the odds (given typical LoRa topologies) the odds are very high that every node should eventually receive the message. + So FloodingRouter.cpp generates an implicit ack which is delivered to the original sender. + If after some time we don't hear anyone rebroadcast our packet, we will timeout and retransmit, using the regular resend logic. + Note: This flag is normally sent in a flag bit in the header when sent over the wire + """ + priority: global___MeshPacket.Priority.ValueType + """ + The priority of this message for sending. + See MeshPacket.Priority description for more details. + """ + rx_rssi: builtins.int + """ + rssi of received packet. Only sent to phone for dispay purposes. + """ + delayed: global___MeshPacket.Delayed.ValueType + """ + Describe if this message is delayed + """ + via_mqtt: builtins.bool + """ + Describes whether this packet passed via MQTT somewhere along the path it currently took. + """ + hop_start: builtins.int + """ + Hop limit with which the original packet started. Sent via LoRa using three bits in the unencrypted header. + When receiving a packet, the difference between hop_start and hop_limit gives how many hops it traveled. + """ + public_key: builtins.bytes + """ + Records the public key the packet was encrypted with, if applicable. + """ + pki_encrypted: builtins.bool + """ + Indicates whether the packet was en/decrypted using PKI + """ + next_hop: builtins.int + """ + Last byte of the node number of the node that should be used as the next hop in routing. + Set by the firmware internally, clients are not supposed to set this. + """ + relay_node: builtins.int + """ + Last byte of the node number of the node that will relay/relayed this packet. + Set by the firmware internally, clients are not supposed to set this. + """ + tx_after: builtins.int + """ + *Never* sent over the radio links. + Timestamp after which this packet may be sent. + Set by the firmware internally, clients are not supposed to set this. + """ + transport_mechanism: global___MeshPacket.TransportMechanism.ValueType + """ + Indicates which transport mechanism this packet arrived over + """ + @property + def decoded(self) -> global___Data: + """ + TODO: REPLACE + """ + + def __init__( + self, + *, + to: builtins.int = ..., + channel: builtins.int = ..., + decoded: global___Data | None = ..., + encrypted: builtins.bytes = ..., + id: builtins.int = ..., + rx_time: builtins.int = ..., + rx_snr: builtins.float = ..., + hop_limit: builtins.int = ..., + want_ack: builtins.bool = ..., + priority: global___MeshPacket.Priority.ValueType = ..., + rx_rssi: builtins.int = ..., + delayed: global___MeshPacket.Delayed.ValueType = ..., + via_mqtt: builtins.bool = ..., + hop_start: builtins.int = ..., + public_key: builtins.bytes = ..., + pki_encrypted: builtins.bool = ..., + next_hop: builtins.int = ..., + relay_node: builtins.int = ..., + tx_after: builtins.int = ..., + transport_mechanism: global___MeshPacket.TransportMechanism.ValueType = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["decoded", b"decoded", "encrypted", b"encrypted", "payload_variant", b"payload_variant"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["channel", b"channel", "decoded", b"decoded", "delayed", b"delayed", "encrypted", b"encrypted", "from", b"from", "hop_limit", b"hop_limit", "hop_start", b"hop_start", "id", b"id", "next_hop", b"next_hop", "payload_variant", b"payload_variant", "pki_encrypted", b"pki_encrypted", "priority", b"priority", "public_key", b"public_key", "relay_node", b"relay_node", "rx_rssi", b"rx_rssi", "rx_snr", b"rx_snr", "rx_time", b"rx_time", "to", b"to", "transport_mechanism", b"transport_mechanism", "tx_after", b"tx_after", "via_mqtt", b"via_mqtt", "want_ack", b"want_ack"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["payload_variant", b"payload_variant"]) -> typing.Literal["decoded", "encrypted"] | None: ... + +global___MeshPacket = MeshPacket + +@typing.final +class NodeInfo(google.protobuf.message.Message): + """ + The bluetooth to device link: + Old BTLE protocol docs from TODO, merge in above and make real docs... + use protocol buffers, and NanoPB + messages from device to phone: + POSITION_UPDATE (..., time) + TEXT_RECEIVED(from, text, time) + OPAQUE_RECEIVED(from, payload, time) (for signal messages or other applications) + messages from phone to device: + SET_MYID(id, human readable long, human readable short) (send down the unique ID + string used for this node, a human readable string shown for that id, and a very + short human readable string suitable for oled screen) SEND_OPAQUE(dest, payload) + (for signal messages or other applications) SEND_TEXT(dest, text) Get all + nodes() (returns list of nodes, with full info, last time seen, loc, battery + level etc) SET_CONFIG (switches device to a new set of radio params and + preshared key, drops all existing nodes, force our node to rejoin this new group) + Full information about a node on the mesh + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + NUM_FIELD_NUMBER: builtins.int + USER_FIELD_NUMBER: builtins.int + POSITION_FIELD_NUMBER: builtins.int + SNR_FIELD_NUMBER: builtins.int + LAST_HEARD_FIELD_NUMBER: builtins.int + DEVICE_METRICS_FIELD_NUMBER: builtins.int + CHANNEL_FIELD_NUMBER: builtins.int + VIA_MQTT_FIELD_NUMBER: builtins.int + HOPS_AWAY_FIELD_NUMBER: builtins.int + IS_FAVORITE_FIELD_NUMBER: builtins.int + IS_IGNORED_FIELD_NUMBER: builtins.int + IS_KEY_MANUALLY_VERIFIED_FIELD_NUMBER: builtins.int + num: builtins.int + """ + The node number + """ + snr: builtins.float + """ + Returns the Signal-to-noise ratio (SNR) of the last received message, + as measured by the receiver. Return SNR of the last received message in dB + """ + last_heard: builtins.int + """ + TODO: REMOVE/INTEGRATE + Not currently used (till full DSR deployment?) Our current preferred node node for routing - might be the same as num if + we are direct neighbor or zero if we don't yet know a route to this node. + fixed32 next_hop = 5; + + + Set to indicate the last time we received a packet from this node + """ + channel: builtins.int + """ + local channel index we heard that node on. Only populated if its not the default channel. + """ + via_mqtt: builtins.bool + """ + True if we witnessed the node over MQTT instead of LoRA transport + """ + hops_away: builtins.int + """ + Number of hops away from us this node is (0 if direct neighbor) + """ + is_favorite: builtins.bool + """ + True if node is in our favorites list + Persists between NodeDB internal clean ups + """ + is_ignored: builtins.bool + """ + True if node is in our ignored list + Persists between NodeDB internal clean ups + """ + is_key_manually_verified: builtins.bool + """ + True if node public key has been verified. + Persists between NodeDB internal clean ups + LSB 0 of the bitfield + """ + @property + def user(self) -> global___User: + """ + The user info for this node + """ + + @property + def position(self) -> global___Position: + """ + This position data. Note: before 1.2.14 we would also store the last time we've heard from this node in position.time, that is no longer true. + Position.time now indicates the last time we received a POSITION from that node. + """ + + @property + def device_metrics(self) -> meshtastic.protobuf.telemetry_pb2.DeviceMetrics: + """ + The latest device metrics for the node. + """ + + def __init__( + self, + *, + num: builtins.int = ..., + user: global___User | None = ..., + position: global___Position | None = ..., + snr: builtins.float = ..., + last_heard: builtins.int = ..., + device_metrics: meshtastic.protobuf.telemetry_pb2.DeviceMetrics | None = ..., + channel: builtins.int = ..., + via_mqtt: builtins.bool = ..., + hops_away: builtins.int | None = ..., + is_favorite: builtins.bool = ..., + is_ignored: builtins.bool = ..., + is_key_manually_verified: builtins.bool = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["_hops_away", b"_hops_away", "device_metrics", b"device_metrics", "hops_away", b"hops_away", "position", b"position", "user", b"user"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["_hops_away", b"_hops_away", "channel", b"channel", "device_metrics", b"device_metrics", "hops_away", b"hops_away", "is_favorite", b"is_favorite", "is_ignored", b"is_ignored", "is_key_manually_verified", b"is_key_manually_verified", "last_heard", b"last_heard", "num", b"num", "position", b"position", "snr", b"snr", "user", b"user", "via_mqtt", b"via_mqtt"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["_hops_away", b"_hops_away"]) -> typing.Literal["hops_away"] | None: ... + +global___NodeInfo = NodeInfo + +@typing.final +class MyNodeInfo(google.protobuf.message.Message): + """ + Unique local debugging info for this node + Note: we don't include position or the user info, because that will come in the + Sent to the phone in response to WantNodes. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + MY_NODE_NUM_FIELD_NUMBER: builtins.int + REBOOT_COUNT_FIELD_NUMBER: builtins.int + MIN_APP_VERSION_FIELD_NUMBER: builtins.int + DEVICE_ID_FIELD_NUMBER: builtins.int + PIO_ENV_FIELD_NUMBER: builtins.int + FIRMWARE_EDITION_FIELD_NUMBER: builtins.int + NODEDB_COUNT_FIELD_NUMBER: builtins.int + my_node_num: builtins.int + """ + Tells the phone what our node number is, default starting value is + lowbyte of macaddr, but it will be fixed if that is already in use + """ + reboot_count: builtins.int + """ + The total number of reboots this node has ever encountered + (well - since the last time we discarded preferences) + """ + min_app_version: builtins.int + """ + The minimum app version that can talk to this device. + Phone/PC apps should compare this to their build number and if too low tell the user they must update their app + """ + device_id: builtins.bytes + """ + Unique hardware identifier for this device + """ + pio_env: builtins.str + """ + The PlatformIO environment used to build this firmware + """ + firmware_edition: global___FirmwareEdition.ValueType + """ + The indicator for whether this device is running event firmware and which + """ + nodedb_count: builtins.int + """ + The number of nodes in the nodedb. + This is used by the phone to know how many NodeInfo packets to expect on want_config + """ + def __init__( + self, + *, + my_node_num: builtins.int = ..., + reboot_count: builtins.int = ..., + min_app_version: builtins.int = ..., + device_id: builtins.bytes = ..., + pio_env: builtins.str = ..., + firmware_edition: global___FirmwareEdition.ValueType = ..., + nodedb_count: builtins.int = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["device_id", b"device_id", "firmware_edition", b"firmware_edition", "min_app_version", b"min_app_version", "my_node_num", b"my_node_num", "nodedb_count", b"nodedb_count", "pio_env", b"pio_env", "reboot_count", b"reboot_count"]) -> None: ... + +global___MyNodeInfo = MyNodeInfo + +@typing.final +class LogRecord(google.protobuf.message.Message): + """ + Debug output from the device. + To minimize the size of records inside the device code, if a time/source/level is not set + on the message it is assumed to be a continuation of the previously sent message. + This allows the device code to use fixed maxlen 64 byte strings for messages, + and then extend as needed by emitting multiple records. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + class _Level: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + + class _LevelEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[LogRecord._Level.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + UNSET: LogRecord._Level.ValueType # 0 + """ + Log levels, chosen to match python logging conventions. + """ + CRITICAL: LogRecord._Level.ValueType # 50 + """ + Log levels, chosen to match python logging conventions. + """ + ERROR: LogRecord._Level.ValueType # 40 + """ + Log levels, chosen to match python logging conventions. + """ + WARNING: LogRecord._Level.ValueType # 30 + """ + Log levels, chosen to match python logging conventions. + """ + INFO: LogRecord._Level.ValueType # 20 + """ + Log levels, chosen to match python logging conventions. + """ + DEBUG: LogRecord._Level.ValueType # 10 + """ + Log levels, chosen to match python logging conventions. + """ + TRACE: LogRecord._Level.ValueType # 5 + """ + Log levels, chosen to match python logging conventions. + """ + + class Level(_Level, metaclass=_LevelEnumTypeWrapper): + """ + Log levels, chosen to match python logging conventions. + """ + + UNSET: LogRecord.Level.ValueType # 0 + """ + Log levels, chosen to match python logging conventions. + """ + CRITICAL: LogRecord.Level.ValueType # 50 + """ + Log levels, chosen to match python logging conventions. + """ + ERROR: LogRecord.Level.ValueType # 40 + """ + Log levels, chosen to match python logging conventions. + """ + WARNING: LogRecord.Level.ValueType # 30 + """ + Log levels, chosen to match python logging conventions. + """ + INFO: LogRecord.Level.ValueType # 20 + """ + Log levels, chosen to match python logging conventions. + """ + DEBUG: LogRecord.Level.ValueType # 10 + """ + Log levels, chosen to match python logging conventions. + """ + TRACE: LogRecord.Level.ValueType # 5 + """ + Log levels, chosen to match python logging conventions. + """ + + MESSAGE_FIELD_NUMBER: builtins.int + TIME_FIELD_NUMBER: builtins.int + SOURCE_FIELD_NUMBER: builtins.int + LEVEL_FIELD_NUMBER: builtins.int + message: builtins.str + """ + Log levels, chosen to match python logging conventions. + """ + time: builtins.int + """ + Seconds since 1970 - or 0 for unknown/unset + """ + source: builtins.str + """ + Usually based on thread name - if known + """ + level: global___LogRecord.Level.ValueType + """ + Not yet set + """ + def __init__( + self, + *, + message: builtins.str = ..., + time: builtins.int = ..., + source: builtins.str = ..., + level: global___LogRecord.Level.ValueType = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["level", b"level", "message", b"message", "source", b"source", "time", b"time"]) -> None: ... + +global___LogRecord = LogRecord + +@typing.final +class QueueStatus(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + RES_FIELD_NUMBER: builtins.int + FREE_FIELD_NUMBER: builtins.int + MAXLEN_FIELD_NUMBER: builtins.int + MESH_PACKET_ID_FIELD_NUMBER: builtins.int + res: builtins.int + """Last attempt to queue status, ErrorCode""" + free: builtins.int + """Free entries in the outgoing queue""" + maxlen: builtins.int + """Maximum entries in the outgoing queue""" + mesh_packet_id: builtins.int + """What was mesh packet id that generated this response?""" + def __init__( + self, + *, + res: builtins.int = ..., + free: builtins.int = ..., + maxlen: builtins.int = ..., + mesh_packet_id: builtins.int = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["free", b"free", "maxlen", b"maxlen", "mesh_packet_id", b"mesh_packet_id", "res", b"res"]) -> None: ... + +global___QueueStatus = QueueStatus + +@typing.final +class FromRadio(google.protobuf.message.Message): + """ + Packets from the radio to the phone will appear on the fromRadio characteristic. + It will support READ and NOTIFY. When a new packet arrives the device will BLE notify? + It will sit in that descriptor until consumed by the phone, + at which point the next item in the FIFO will be populated. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ID_FIELD_NUMBER: builtins.int + PACKET_FIELD_NUMBER: builtins.int + MY_INFO_FIELD_NUMBER: builtins.int + NODE_INFO_FIELD_NUMBER: builtins.int + CONFIG_FIELD_NUMBER: builtins.int + LOG_RECORD_FIELD_NUMBER: builtins.int + CONFIG_COMPLETE_ID_FIELD_NUMBER: builtins.int + REBOOTED_FIELD_NUMBER: builtins.int + MODULECONFIG_FIELD_NUMBER: builtins.int + CHANNEL_FIELD_NUMBER: builtins.int + QUEUESTATUS_FIELD_NUMBER: builtins.int + XMODEMPACKET_FIELD_NUMBER: builtins.int + METADATA_FIELD_NUMBER: builtins.int + MQTTCLIENTPROXYMESSAGE_FIELD_NUMBER: builtins.int + FILEINFO_FIELD_NUMBER: builtins.int + CLIENTNOTIFICATION_FIELD_NUMBER: builtins.int + DEVICEUICONFIG_FIELD_NUMBER: builtins.int + id: builtins.int + """ + The packet id, used to allow the phone to request missing read packets from the FIFO, + see our bluetooth docs + """ + config_complete_id: builtins.int + """ + Sent as true once the device has finished sending all of the responses to want_config + recipient should check if this ID matches our original request nonce, if + not, it means your config responses haven't started yet. + NOTE: This ID must not change - to keep (minimal) compatibility with <1.2 version of android apps. + """ + rebooted: builtins.bool + """ + Sent to tell clients the radio has just rebooted. + Set to true if present. + Not used on all transports, currently just used for the serial console. + NOTE: This ID must not change - to keep (minimal) compatibility with <1.2 version of android apps. + """ + @property + def packet(self) -> global___MeshPacket: + """ + Log levels, chosen to match python logging conventions. + """ + + @property + def my_info(self) -> global___MyNodeInfo: + """ + Tells the phone what our node number is, can be -1 if we've not yet joined a mesh. + NOTE: This ID must not change - to keep (minimal) compatibility with <1.2 version of android apps. + """ + + @property + def node_info(self) -> global___NodeInfo: + """ + One packet is sent for each node in the on radio DB + starts over with the first node in our DB + """ + + @property + def config(self) -> meshtastic.protobuf.config_pb2.Config: + """ + Include a part of the config (was: RadioConfig radio) + """ + + @property + def log_record(self) -> global___LogRecord: + """ + Set to send debug console output over our protobuf stream + """ + + @property + def moduleConfig(self) -> meshtastic.protobuf.module_config_pb2.ModuleConfig: + """ + Include module config + """ + + @property + def channel(self) -> meshtastic.protobuf.channel_pb2.Channel: + """ + One packet is sent for each channel + """ + + @property + def queueStatus(self) -> global___QueueStatus: + """ + Queue status info + """ + + @property + def xmodemPacket(self) -> meshtastic.protobuf.xmodem_pb2.XModem: + """ + File Transfer Chunk + """ + + @property + def metadata(self) -> global___DeviceMetadata: + """ + Device metadata message + """ + + @property + def mqttClientProxyMessage(self) -> global___MqttClientProxyMessage: + """ + MQTT Client Proxy Message (device sending to client / phone for publishing to MQTT) + """ + + @property + def fileInfo(self) -> global___FileInfo: + """ + File system manifest messages + """ + + @property + def clientNotification(self) -> global___ClientNotification: + """ + Notification message to the client + """ + + @property + def deviceuiConfig(self) -> meshtastic.protobuf.device_ui_pb2.DeviceUIConfig: + """ + Persistent data for device-ui + """ + + def __init__( + self, + *, + id: builtins.int = ..., + packet: global___MeshPacket | None = ..., + my_info: global___MyNodeInfo | None = ..., + node_info: global___NodeInfo | None = ..., + config: meshtastic.protobuf.config_pb2.Config | None = ..., + log_record: global___LogRecord | None = ..., + config_complete_id: builtins.int = ..., + rebooted: builtins.bool = ..., + moduleConfig: meshtastic.protobuf.module_config_pb2.ModuleConfig | None = ..., + channel: meshtastic.protobuf.channel_pb2.Channel | None = ..., + queueStatus: global___QueueStatus | None = ..., + xmodemPacket: meshtastic.protobuf.xmodem_pb2.XModem | None = ..., + metadata: global___DeviceMetadata | None = ..., + mqttClientProxyMessage: global___MqttClientProxyMessage | None = ..., + fileInfo: global___FileInfo | None = ..., + clientNotification: global___ClientNotification | None = ..., + deviceuiConfig: meshtastic.protobuf.device_ui_pb2.DeviceUIConfig | 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: ... + +global___FromRadio = FromRadio + +@typing.final +class ClientNotification(google.protobuf.message.Message): + """ + A notification message from the device to the client + To be used for important messages that should to be displayed to the user + in the form of push notifications or validation messages when saving + invalid configuration. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + REPLY_ID_FIELD_NUMBER: builtins.int + TIME_FIELD_NUMBER: builtins.int + LEVEL_FIELD_NUMBER: builtins.int + MESSAGE_FIELD_NUMBER: builtins.int + KEY_VERIFICATION_NUMBER_INFORM_FIELD_NUMBER: builtins.int + KEY_VERIFICATION_NUMBER_REQUEST_FIELD_NUMBER: builtins.int + KEY_VERIFICATION_FINAL_FIELD_NUMBER: builtins.int + DUPLICATED_PUBLIC_KEY_FIELD_NUMBER: builtins.int + LOW_ENTROPY_KEY_FIELD_NUMBER: builtins.int + reply_id: builtins.int + """ + The id of the packet we're notifying in response to + """ + time: builtins.int + """ + Seconds since 1970 - or 0 for unknown/unset + """ + level: global___LogRecord.Level.ValueType + """ + The level type of notification + """ + message: builtins.str + """ + The message body of the notification + """ + @property + def key_verification_number_inform(self) -> global___KeyVerificationNumberInform: ... + @property + def key_verification_number_request(self) -> global___KeyVerificationNumberRequest: ... + @property + def key_verification_final(self) -> global___KeyVerificationFinal: ... + @property + def duplicated_public_key(self) -> global___DuplicatedPublicKey: ... + @property + def low_entropy_key(self) -> global___LowEntropyKey: ... + def __init__( + self, + *, + reply_id: builtins.int | None = ..., + time: builtins.int = ..., + level: global___LogRecord.Level.ValueType = ..., + message: builtins.str = ..., + key_verification_number_inform: global___KeyVerificationNumberInform | None = ..., + key_verification_number_request: global___KeyVerificationNumberRequest | None = ..., + key_verification_final: global___KeyVerificationFinal | None = ..., + duplicated_public_key: global___DuplicatedPublicKey | None = ..., + low_entropy_key: global___LowEntropyKey | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["_reply_id", b"_reply_id", "duplicated_public_key", b"duplicated_public_key", "key_verification_final", b"key_verification_final", "key_verification_number_inform", b"key_verification_number_inform", "key_verification_number_request", b"key_verification_number_request", "low_entropy_key", b"low_entropy_key", "payload_variant", b"payload_variant", "reply_id", b"reply_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["_reply_id", b"_reply_id", "duplicated_public_key", b"duplicated_public_key", "key_verification_final", b"key_verification_final", "key_verification_number_inform", b"key_verification_number_inform", "key_verification_number_request", b"key_verification_number_request", "level", b"level", "low_entropy_key", b"low_entropy_key", "message", b"message", "payload_variant", b"payload_variant", "reply_id", b"reply_id", "time", b"time"]) -> None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_reply_id", b"_reply_id"]) -> typing.Literal["reply_id"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["payload_variant", b"payload_variant"]) -> typing.Literal["key_verification_number_inform", "key_verification_number_request", "key_verification_final", "duplicated_public_key", "low_entropy_key"] | None: ... + +global___ClientNotification = ClientNotification + +@typing.final +class KeyVerificationNumberInform(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + NONCE_FIELD_NUMBER: builtins.int + REMOTE_LONGNAME_FIELD_NUMBER: builtins.int + SECURITY_NUMBER_FIELD_NUMBER: builtins.int + nonce: builtins.int + remote_longname: builtins.str + security_number: builtins.int + def __init__( + self, + *, + nonce: builtins.int = ..., + remote_longname: builtins.str = ..., + security_number: builtins.int = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["nonce", b"nonce", "remote_longname", b"remote_longname", "security_number", b"security_number"]) -> None: ... + +global___KeyVerificationNumberInform = KeyVerificationNumberInform + +@typing.final +class KeyVerificationNumberRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + NONCE_FIELD_NUMBER: builtins.int + REMOTE_LONGNAME_FIELD_NUMBER: builtins.int + nonce: builtins.int + remote_longname: builtins.str + def __init__( + self, + *, + nonce: builtins.int = ..., + remote_longname: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["nonce", b"nonce", "remote_longname", b"remote_longname"]) -> None: ... + +global___KeyVerificationNumberRequest = KeyVerificationNumberRequest + +@typing.final +class KeyVerificationFinal(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + NONCE_FIELD_NUMBER: builtins.int + REMOTE_LONGNAME_FIELD_NUMBER: builtins.int + ISSENDER_FIELD_NUMBER: builtins.int + VERIFICATION_CHARACTERS_FIELD_NUMBER: builtins.int + nonce: builtins.int + remote_longname: builtins.str + isSender: builtins.bool + verification_characters: builtins.str + def __init__( + self, + *, + nonce: builtins.int = ..., + remote_longname: builtins.str = ..., + isSender: builtins.bool = ..., + verification_characters: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["isSender", b"isSender", "nonce", b"nonce", "remote_longname", b"remote_longname", "verification_characters", b"verification_characters"]) -> None: ... + +global___KeyVerificationFinal = KeyVerificationFinal + +@typing.final +class DuplicatedPublicKey(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__( + self, + ) -> None: ... + +global___DuplicatedPublicKey = DuplicatedPublicKey + +@typing.final +class LowEntropyKey(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__( + self, + ) -> None: ... + +global___LowEntropyKey = LowEntropyKey + +@typing.final +class FileInfo(google.protobuf.message.Message): + """ + Individual File info for the device + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + FILE_NAME_FIELD_NUMBER: builtins.int + SIZE_BYTES_FIELD_NUMBER: builtins.int + file_name: builtins.str + """ + The fully qualified path of the file + """ + size_bytes: builtins.int + """ + The size of the file in bytes + """ + def __init__( + self, + *, + file_name: builtins.str = ..., + size_bytes: builtins.int = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["file_name", b"file_name", "size_bytes", b"size_bytes"]) -> None: ... + +global___FileInfo = FileInfo + +@typing.final +class ToRadio(google.protobuf.message.Message): + """ + Packets/commands to the radio will be written (reliably) to the toRadio characteristic. + Once the write completes the phone can assume it is handled. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + PACKET_FIELD_NUMBER: builtins.int + WANT_CONFIG_ID_FIELD_NUMBER: builtins.int + DISCONNECT_FIELD_NUMBER: builtins.int + XMODEMPACKET_FIELD_NUMBER: builtins.int + MQTTCLIENTPROXYMESSAGE_FIELD_NUMBER: builtins.int + HEARTBEAT_FIELD_NUMBER: builtins.int + want_config_id: builtins.int + """ + Phone wants radio to send full node db to the phone, This is + typically the first packet sent to the radio when the phone gets a + bluetooth connection. The radio will respond by sending back a + MyNodeInfo, a owner, a radio config and a series of + FromRadio.node_infos, and config_complete + the integer you write into this field will be reported back in the + config_complete_id response this allows clients to never be confused by + a stale old partially sent config. + """ + disconnect: builtins.bool + """ + Tell API server we are disconnecting now. + This is useful for serial links where there is no hardware/protocol based notification that the client has dropped the link. + (Sending this message is optional for clients) + """ + @property + def packet(self) -> global___MeshPacket: + """ + Send this packet on the mesh + """ + + @property + def xmodemPacket(self) -> meshtastic.protobuf.xmodem_pb2.XModem: + """ + File Transfer Chunk + """ + + @property + def mqttClientProxyMessage(self) -> global___MqttClientProxyMessage: + """ + MQTT Client Proxy Message (for client / phone subscribed to MQTT sending to device) + """ + + @property + def heartbeat(self) -> global___Heartbeat: + """ + Heartbeat message (used to keep the device connection awake on serial) + """ + + def __init__( + self, + *, + packet: global___MeshPacket | None = ..., + want_config_id: builtins.int = ..., + disconnect: builtins.bool = ..., + xmodemPacket: meshtastic.protobuf.xmodem_pb2.XModem | None = ..., + mqttClientProxyMessage: global___MqttClientProxyMessage | None = ..., + heartbeat: global___Heartbeat | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["disconnect", b"disconnect", "heartbeat", b"heartbeat", "mqttClientProxyMessage", b"mqttClientProxyMessage", "packet", b"packet", "payload_variant", b"payload_variant", "want_config_id", b"want_config_id", "xmodemPacket", b"xmodemPacket"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["disconnect", b"disconnect", "heartbeat", b"heartbeat", "mqttClientProxyMessage", b"mqttClientProxyMessage", "packet", b"packet", "payload_variant", b"payload_variant", "want_config_id", b"want_config_id", "xmodemPacket", b"xmodemPacket"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["payload_variant", b"payload_variant"]) -> typing.Literal["packet", "want_config_id", "disconnect", "xmodemPacket", "mqttClientProxyMessage", "heartbeat"] | None: ... + +global___ToRadio = ToRadio + +@typing.final +class Compressed(google.protobuf.message.Message): + """ + Compressed message payload + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + PORTNUM_FIELD_NUMBER: builtins.int + DATA_FIELD_NUMBER: builtins.int + portnum: meshtastic.protobuf.portnums_pb2.PortNum.ValueType + """ + PortNum to determine the how to handle the compressed payload. + """ + data: builtins.bytes + """ + Compressed data. + """ + def __init__( + self, + *, + portnum: meshtastic.protobuf.portnums_pb2.PortNum.ValueType = ..., + data: builtins.bytes = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["data", b"data", "portnum", b"portnum"]) -> None: ... + +global___Compressed = Compressed + +@typing.final +class NeighborInfo(google.protobuf.message.Message): + """ + Full info on edges for a single node + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + NODE_ID_FIELD_NUMBER: builtins.int + LAST_SENT_BY_ID_FIELD_NUMBER: builtins.int + NODE_BROADCAST_INTERVAL_SECS_FIELD_NUMBER: builtins.int + NEIGHBORS_FIELD_NUMBER: builtins.int + node_id: builtins.int + """ + The node ID of the node sending info on its neighbors + """ + last_sent_by_id: builtins.int + """ + Field to pass neighbor info for the next sending cycle + """ + node_broadcast_interval_secs: builtins.int + """ + Broadcast interval of the represented node (in seconds) + """ + @property + def neighbors(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Neighbor]: + """ + The list of out edges from this node + """ + + def __init__( + self, + *, + node_id: builtins.int = ..., + last_sent_by_id: builtins.int = ..., + node_broadcast_interval_secs: builtins.int = ..., + neighbors: collections.abc.Iterable[global___Neighbor] | None = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["last_sent_by_id", b"last_sent_by_id", "neighbors", b"neighbors", "node_broadcast_interval_secs", b"node_broadcast_interval_secs", "node_id", b"node_id"]) -> None: ... + +global___NeighborInfo = NeighborInfo + +@typing.final +class Neighbor(google.protobuf.message.Message): + """ + A single edge in the mesh + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + NODE_ID_FIELD_NUMBER: builtins.int + SNR_FIELD_NUMBER: builtins.int + LAST_RX_TIME_FIELD_NUMBER: builtins.int + NODE_BROADCAST_INTERVAL_SECS_FIELD_NUMBER: builtins.int + node_id: builtins.int + """ + Node ID of neighbor + """ + snr: builtins.float + """ + SNR of last heard message + """ + last_rx_time: builtins.int + """ + Reception time (in secs since 1970) of last message that was last sent by this ID. + Note: this is for local storage only and will not be sent out over the mesh. + """ + node_broadcast_interval_secs: builtins.int + """ + Broadcast interval of this neighbor (in seconds). + Note: this is for local storage only and will not be sent out over the mesh. + """ + def __init__( + self, + *, + node_id: builtins.int = ..., + snr: builtins.float = ..., + last_rx_time: builtins.int = ..., + node_broadcast_interval_secs: builtins.int = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["last_rx_time", b"last_rx_time", "node_broadcast_interval_secs", b"node_broadcast_interval_secs", "node_id", b"node_id", "snr", b"snr"]) -> None: ... + +global___Neighbor = Neighbor + +@typing.final +class DeviceMetadata(google.protobuf.message.Message): + """ + Device metadata response + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + FIRMWARE_VERSION_FIELD_NUMBER: builtins.int + DEVICE_STATE_VERSION_FIELD_NUMBER: builtins.int + CANSHUTDOWN_FIELD_NUMBER: builtins.int + HASWIFI_FIELD_NUMBER: builtins.int + HASBLUETOOTH_FIELD_NUMBER: builtins.int + HASETHERNET_FIELD_NUMBER: builtins.int + ROLE_FIELD_NUMBER: builtins.int + POSITION_FLAGS_FIELD_NUMBER: builtins.int + HW_MODEL_FIELD_NUMBER: builtins.int + HASREMOTEHARDWARE_FIELD_NUMBER: builtins.int + HASPKC_FIELD_NUMBER: builtins.int + EXCLUDED_MODULES_FIELD_NUMBER: builtins.int + firmware_version: builtins.str + """ + Device firmware version string + """ + device_state_version: builtins.int + """ + Device state version + """ + canShutdown: builtins.bool + """ + Indicates whether the device can shutdown CPU natively or via power management chip + """ + hasWifi: builtins.bool + """ + Indicates that the device has native wifi capability + """ + hasBluetooth: builtins.bool + """ + Indicates that the device has native bluetooth capability + """ + hasEthernet: builtins.bool + """ + Indicates that the device has an ethernet peripheral + """ + role: meshtastic.protobuf.config_pb2.Config.DeviceConfig.Role.ValueType + """ + Indicates that the device's role in the mesh + """ + position_flags: builtins.int + """ + Indicates the device's current enabled position flags + """ + hw_model: global___HardwareModel.ValueType + """ + Device hardware model + """ + hasRemoteHardware: builtins.bool + """ + Has Remote Hardware enabled + """ + hasPKC: builtins.bool + """ + Has PKC capabilities + """ + excluded_modules: builtins.int + """ + Bit field of boolean for excluded modules + (bitwise OR of ExcludedModules) + """ + def __init__( + self, + *, + firmware_version: builtins.str = ..., + device_state_version: builtins.int = ..., + canShutdown: builtins.bool = ..., + hasWifi: builtins.bool = ..., + hasBluetooth: builtins.bool = ..., + hasEthernet: builtins.bool = ..., + role: meshtastic.protobuf.config_pb2.Config.DeviceConfig.Role.ValueType = ..., + position_flags: builtins.int = ..., + hw_model: global___HardwareModel.ValueType = ..., + hasRemoteHardware: builtins.bool = ..., + hasPKC: builtins.bool = ..., + excluded_modules: builtins.int = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["canShutdown", b"canShutdown", "device_state_version", b"device_state_version", "excluded_modules", b"excluded_modules", "firmware_version", b"firmware_version", "hasBluetooth", b"hasBluetooth", "hasEthernet", b"hasEthernet", "hasPKC", b"hasPKC", "hasRemoteHardware", b"hasRemoteHardware", "hasWifi", b"hasWifi", "hw_model", b"hw_model", "position_flags", b"position_flags", "role", b"role"]) -> None: ... + +global___DeviceMetadata = DeviceMetadata + +@typing.final +class Heartbeat(google.protobuf.message.Message): + """ + A heartbeat message is sent to the node from the client to keep the connection alive. + This is currently only needed to keep serial connections alive, but can be used by any PhoneAPI. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + NONCE_FIELD_NUMBER: builtins.int + nonce: builtins.int + """ + The nonce of the heartbeat message + """ + def __init__( + self, + *, + nonce: builtins.int = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["nonce", b"nonce"]) -> None: ... + +global___Heartbeat = Heartbeat + +@typing.final +class NodeRemoteHardwarePin(google.protobuf.message.Message): + """ + RemoteHardwarePins associated with a node + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + NODE_NUM_FIELD_NUMBER: builtins.int + PIN_FIELD_NUMBER: builtins.int + node_num: builtins.int + """ + The node_num exposing the available gpio pin + """ + @property + def pin(self) -> meshtastic.protobuf.module_config_pb2.RemoteHardwarePin: + """ + The the available gpio pin for usage with RemoteHardware module + """ + + def __init__( + self, + *, + node_num: builtins.int = ..., + pin: meshtastic.protobuf.module_config_pb2.RemoteHardwarePin | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["pin", b"pin"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["node_num", b"node_num", "pin", b"pin"]) -> None: ... + +global___NodeRemoteHardwarePin = NodeRemoteHardwarePin + +@typing.final +class ChunkedPayload(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + PAYLOAD_ID_FIELD_NUMBER: builtins.int + CHUNK_COUNT_FIELD_NUMBER: builtins.int + CHUNK_INDEX_FIELD_NUMBER: builtins.int + PAYLOAD_CHUNK_FIELD_NUMBER: builtins.int + payload_id: builtins.int + """ + The ID of the entire payload + """ + chunk_count: builtins.int + """ + The total number of chunks in the payload + """ + chunk_index: builtins.int + """ + The current chunk index in the total + """ + payload_chunk: builtins.bytes + """ + The binary data of the current chunk + """ + def __init__( + self, + *, + payload_id: builtins.int = ..., + chunk_count: builtins.int = ..., + chunk_index: builtins.int = ..., + payload_chunk: builtins.bytes = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["chunk_count", b"chunk_count", "chunk_index", b"chunk_index", "payload_chunk", b"payload_chunk", "payload_id", b"payload_id"]) -> None: ... + +global___ChunkedPayload = ChunkedPayload + +@typing.final +class resend_chunks(google.protobuf.message.Message): + """ + Wrapper message for broken repeated oneof support + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + CHUNKS_FIELD_NUMBER: builtins.int + @property + def chunks(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.int]: ... + def __init__( + self, + *, + chunks: collections.abc.Iterable[builtins.int] | None = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["chunks", b"chunks"]) -> None: ... + +global___resend_chunks = resend_chunks + +@typing.final +class ChunkedPayloadResponse(google.protobuf.message.Message): + """ + Responses to a ChunkedPayload request + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + PAYLOAD_ID_FIELD_NUMBER: builtins.int + REQUEST_TRANSFER_FIELD_NUMBER: builtins.int + ACCEPT_TRANSFER_FIELD_NUMBER: builtins.int + RESEND_CHUNKS_FIELD_NUMBER: builtins.int + payload_id: builtins.int + """ + The ID of the entire payload + """ + request_transfer: builtins.bool + """ + Request to transfer chunked payload + """ + accept_transfer: builtins.bool + """ + Accept the transfer chunked payload + """ + @property + def resend_chunks(self) -> global___resend_chunks: + """ + Request missing indexes in the chunked payload + """ + + def __init__( + self, + *, + payload_id: builtins.int = ..., + request_transfer: builtins.bool = ..., + accept_transfer: builtins.bool = ..., + resend_chunks: global___resend_chunks | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["accept_transfer", b"accept_transfer", "payload_variant", b"payload_variant", "request_transfer", b"request_transfer", "resend_chunks", b"resend_chunks"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["accept_transfer", b"accept_transfer", "payload_id", b"payload_id", "payload_variant", b"payload_variant", "request_transfer", b"request_transfer", "resend_chunks", b"resend_chunks"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["payload_variant", b"payload_variant"]) -> typing.Literal["request_transfer", "accept_transfer", "resend_chunks"] | None: ... + +global___ChunkedPayloadResponse = ChunkedPayloadResponse diff --git a/meshtastic/protobuf/module_config_pb2.py b/meshtastic/protobuf/module_config_pb2.py new file mode 100644 index 00000000..44d0bc55 --- /dev/null +++ b/meshtastic/protobuf/module_config_pb2.py @@ -0,0 +1,72 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: meshtastic/protobuf/module_config.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\'meshtastic/protobuf/module_config.proto\x12\x13meshtastic.protobuf\"\xfb\'\n\x0cModuleConfig\x12<\n\x04mqtt\x18\x01 \x01(\x0b\x32,.meshtastic.protobuf.ModuleConfig.MQTTConfigH\x00\x12@\n\x06serial\x18\x02 \x01(\x0b\x32..meshtastic.protobuf.ModuleConfig.SerialConfigH\x00\x12]\n\x15\x65xternal_notification\x18\x03 \x01(\x0b\x32<.meshtastic.protobuf.ModuleConfig.ExternalNotificationConfigH\x00\x12M\n\rstore_forward\x18\x04 \x01(\x0b\x32\x34.meshtastic.protobuf.ModuleConfig.StoreForwardConfigH\x00\x12G\n\nrange_test\x18\x05 \x01(\x0b\x32\x31.meshtastic.protobuf.ModuleConfig.RangeTestConfigH\x00\x12\x46\n\ttelemetry\x18\x06 \x01(\x0b\x32\x31.meshtastic.protobuf.ModuleConfig.TelemetryConfigH\x00\x12O\n\x0e\x63\x61nned_message\x18\x07 \x01(\x0b\x32\x35.meshtastic.protobuf.ModuleConfig.CannedMessageConfigH\x00\x12>\n\x05\x61udio\x18\x08 \x01(\x0b\x32-.meshtastic.protobuf.ModuleConfig.AudioConfigH\x00\x12Q\n\x0fremote_hardware\x18\t \x01(\x0b\x32\x36.meshtastic.protobuf.ModuleConfig.RemoteHardwareConfigH\x00\x12M\n\rneighbor_info\x18\n \x01(\x0b\x32\x34.meshtastic.protobuf.ModuleConfig.NeighborInfoConfigH\x00\x12S\n\x10\x61mbient_lighting\x18\x0b \x01(\x0b\x32\x37.meshtastic.protobuf.ModuleConfig.AmbientLightingConfigH\x00\x12S\n\x10\x64\x65tection_sensor\x18\x0c \x01(\x0b\x32\x37.meshtastic.protobuf.ModuleConfig.DetectionSensorConfigH\x00\x12H\n\npaxcounter\x18\r \x01(\x0b\x32\x32.meshtastic.protobuf.ModuleConfig.PaxcounterConfigH\x00\x1a\xb9\x02\n\nMQTTConfig\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x0f\n\x07\x61\x64\x64ress\x18\x02 \x01(\t\x12\x10\n\x08username\x18\x03 \x01(\t\x12\x10\n\x08password\x18\x04 \x01(\t\x12\x1a\n\x12\x65ncryption_enabled\x18\x05 \x01(\x08\x12\x14\n\x0cjson_enabled\x18\x06 \x01(\x08\x12\x13\n\x0btls_enabled\x18\x07 \x01(\x08\x12\x0c\n\x04root\x18\x08 \x01(\t\x12\x1f\n\x17proxy_to_client_enabled\x18\t \x01(\x08\x12\x1d\n\x15map_reporting_enabled\x18\n \x01(\x08\x12P\n\x13map_report_settings\x18\x0b \x01(\x0b\x32\x33.meshtastic.protobuf.ModuleConfig.MapReportSettings\x1an\n\x11MapReportSettings\x12\x1d\n\x15publish_interval_secs\x18\x01 \x01(\r\x12\x1a\n\x12position_precision\x18\x02 \x01(\r\x12\x1e\n\x16should_report_location\x18\x03 \x01(\x08\x1a\x8b\x01\n\x14RemoteHardwareConfig\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\"\n\x1a\x61llow_undefined_pin_access\x18\x02 \x01(\x08\x12>\n\x0e\x61vailable_pins\x18\x03 \x03(\x0b\x32&.meshtastic.protobuf.RemoteHardwarePin\x1aZ\n\x12NeighborInfoConfig\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x17\n\x0fupdate_interval\x18\x02 \x01(\r\x12\x1a\n\x12transmit_over_lora\x18\x03 \x01(\x08\x1a\xa0\x03\n\x15\x44\x65tectionSensorConfig\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x1e\n\x16minimum_broadcast_secs\x18\x02 \x01(\r\x12\x1c\n\x14state_broadcast_secs\x18\x03 \x01(\r\x12\x11\n\tsend_bell\x18\x04 \x01(\x08\x12\x0c\n\x04name\x18\x05 \x01(\t\x12\x13\n\x0bmonitor_pin\x18\x06 \x01(\r\x12\x63\n\x16\x64\x65tection_trigger_type\x18\x07 \x01(\x0e\x32\x43.meshtastic.protobuf.ModuleConfig.DetectionSensorConfig.TriggerType\x12\x12\n\nuse_pullup\x18\x08 \x01(\x08\"\x88\x01\n\x0bTriggerType\x12\r\n\tLOGIC_LOW\x10\x00\x12\x0e\n\nLOGIC_HIGH\x10\x01\x12\x10\n\x0c\x46\x41LLING_EDGE\x10\x02\x12\x0f\n\x0bRISING_EDGE\x10\x03\x12\x1a\n\x16\x45ITHER_EDGE_ACTIVE_LOW\x10\x04\x12\x1b\n\x17\x45ITHER_EDGE_ACTIVE_HIGH\x10\x05\x1a\xed\x02\n\x0b\x41udioConfig\x12\x16\n\x0e\x63odec2_enabled\x18\x01 \x01(\x08\x12\x0f\n\x07ptt_pin\x18\x02 \x01(\r\x12I\n\x07\x62itrate\x18\x03 \x01(\x0e\x32\x38.meshtastic.protobuf.ModuleConfig.AudioConfig.Audio_Baud\x12\x0e\n\x06i2s_ws\x18\x04 \x01(\r\x12\x0e\n\x06i2s_sd\x18\x05 \x01(\r\x12\x0f\n\x07i2s_din\x18\x06 \x01(\r\x12\x0f\n\x07i2s_sck\x18\x07 \x01(\r\"\xa7\x01\n\nAudio_Baud\x12\x12\n\x0e\x43ODEC2_DEFAULT\x10\x00\x12\x0f\n\x0b\x43ODEC2_3200\x10\x01\x12\x0f\n\x0b\x43ODEC2_2400\x10\x02\x12\x0f\n\x0b\x43ODEC2_1600\x10\x03\x12\x0f\n\x0b\x43ODEC2_1400\x10\x04\x12\x0f\n\x0b\x43ODEC2_1300\x10\x05\x12\x0f\n\x0b\x43ODEC2_1200\x10\x06\x12\x0e\n\nCODEC2_700\x10\x07\x12\x0f\n\x0b\x43ODEC2_700B\x10\x08\x1av\n\x10PaxcounterConfig\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\"\n\x1apaxcounter_update_interval\x18\x02 \x01(\r\x12\x16\n\x0ewifi_threshold\x18\x03 \x01(\x05\x12\x15\n\rble_threshold\x18\x04 \x01(\x05\x1a\x9e\x05\n\x0cSerialConfig\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x0c\n\x04\x65\x63ho\x18\x02 \x01(\x08\x12\x0b\n\x03rxd\x18\x03 \x01(\r\x12\x0b\n\x03txd\x18\x04 \x01(\r\x12H\n\x04\x62\x61ud\x18\x05 \x01(\x0e\x32:.meshtastic.protobuf.ModuleConfig.SerialConfig.Serial_Baud\x12\x0f\n\x07timeout\x18\x06 \x01(\r\x12H\n\x04mode\x18\x07 \x01(\x0e\x32:.meshtastic.protobuf.ModuleConfig.SerialConfig.Serial_Mode\x12$\n\x1coverride_console_serial_port\x18\x08 \x01(\x08\"\x8a\x02\n\x0bSerial_Baud\x12\x10\n\x0c\x42\x41UD_DEFAULT\x10\x00\x12\x0c\n\x08\x42\x41UD_110\x10\x01\x12\x0c\n\x08\x42\x41UD_300\x10\x02\x12\x0c\n\x08\x42\x41UD_600\x10\x03\x12\r\n\tBAUD_1200\x10\x04\x12\r\n\tBAUD_2400\x10\x05\x12\r\n\tBAUD_4800\x10\x06\x12\r\n\tBAUD_9600\x10\x07\x12\x0e\n\nBAUD_19200\x10\x08\x12\x0e\n\nBAUD_38400\x10\t\x12\x0e\n\nBAUD_57600\x10\n\x12\x0f\n\x0b\x42\x41UD_115200\x10\x0b\x12\x0f\n\x0b\x42\x41UD_230400\x10\x0c\x12\x0f\n\x0b\x42\x41UD_460800\x10\r\x12\x0f\n\x0b\x42\x41UD_576000\x10\x0e\x12\x0f\n\x0b\x42\x41UD_921600\x10\x0f\"}\n\x0bSerial_Mode\x12\x0b\n\x07\x44\x45\x46\x41ULT\x10\x00\x12\n\n\x06SIMPLE\x10\x01\x12\t\n\x05PROTO\x10\x02\x12\x0b\n\x07TEXTMSG\x10\x03\x12\x08\n\x04NMEA\x10\x04\x12\x0b\n\x07\x43\x41LTOPO\x10\x05\x12\x08\n\x04WS85\x10\x06\x12\r\n\tVE_DIRECT\x10\x07\x12\r\n\tMS_CONFIG\x10\x08\x1a\xe9\x02\n\x1a\x45xternalNotificationConfig\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x11\n\toutput_ms\x18\x02 \x01(\r\x12\x0e\n\x06output\x18\x03 \x01(\r\x12\x14\n\x0coutput_vibra\x18\x08 \x01(\r\x12\x15\n\routput_buzzer\x18\t \x01(\r\x12\x0e\n\x06\x61\x63tive\x18\x04 \x01(\x08\x12\x15\n\ralert_message\x18\x05 \x01(\x08\x12\x1b\n\x13\x61lert_message_vibra\x18\n \x01(\x08\x12\x1c\n\x14\x61lert_message_buzzer\x18\x0b \x01(\x08\x12\x12\n\nalert_bell\x18\x06 \x01(\x08\x12\x18\n\x10\x61lert_bell_vibra\x18\x0c \x01(\x08\x12\x19\n\x11\x61lert_bell_buzzer\x18\r \x01(\x08\x12\x0f\n\x07use_pwm\x18\x07 \x01(\x08\x12\x13\n\x0bnag_timeout\x18\x0e \x01(\r\x12\x19\n\x11use_i2s_as_buzzer\x18\x0f \x01(\x08\x1a\x97\x01\n\x12StoreForwardConfig\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x11\n\theartbeat\x18\x02 \x01(\x08\x12\x0f\n\x07records\x18\x03 \x01(\r\x12\x1a\n\x12history_return_max\x18\x04 \x01(\r\x12\x1d\n\x15history_return_window\x18\x05 \x01(\r\x12\x11\n\tis_server\x18\x06 \x01(\x08\x1aY\n\x0fRangeTestConfig\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x0e\n\x06sender\x18\x02 \x01(\r\x12\x0c\n\x04save\x18\x03 \x01(\x08\x12\x17\n\x0f\x63lear_on_reboot\x18\x04 \x01(\x08\x1a\xeb\x03\n\x0fTelemetryConfig\x12\x1e\n\x16\x64\x65vice_update_interval\x18\x01 \x01(\r\x12#\n\x1b\x65nvironment_update_interval\x18\x02 \x01(\r\x12\'\n\x1f\x65nvironment_measurement_enabled\x18\x03 \x01(\x08\x12\"\n\x1a\x65nvironment_screen_enabled\x18\x04 \x01(\x08\x12&\n\x1e\x65nvironment_display_fahrenheit\x18\x05 \x01(\x08\x12\x1b\n\x13\x61ir_quality_enabled\x18\x06 \x01(\x08\x12\x1c\n\x14\x61ir_quality_interval\x18\x07 \x01(\r\x12!\n\x19power_measurement_enabled\x18\x08 \x01(\x08\x12\x1d\n\x15power_update_interval\x18\t \x01(\r\x12\x1c\n\x14power_screen_enabled\x18\n \x01(\x08\x12\"\n\x1ahealth_measurement_enabled\x18\x0b \x01(\x08\x12\x1e\n\x16health_update_interval\x18\x0c \x01(\r\x12\x1d\n\x15health_screen_enabled\x18\r \x01(\x08\x12 \n\x18\x64\x65vice_telemetry_enabled\x18\x0e \x01(\x08\x1a\xf9\x04\n\x13\x43\x61nnedMessageConfig\x12\x17\n\x0frotary1_enabled\x18\x01 \x01(\x08\x12\x19\n\x11inputbroker_pin_a\x18\x02 \x01(\r\x12\x19\n\x11inputbroker_pin_b\x18\x03 \x01(\r\x12\x1d\n\x15inputbroker_pin_press\x18\x04 \x01(\r\x12\x62\n\x14inputbroker_event_cw\x18\x05 \x01(\x0e\x32\x44.meshtastic.protobuf.ModuleConfig.CannedMessageConfig.InputEventChar\x12\x63\n\x15inputbroker_event_ccw\x18\x06 \x01(\x0e\x32\x44.meshtastic.protobuf.ModuleConfig.CannedMessageConfig.InputEventChar\x12\x65\n\x17inputbroker_event_press\x18\x07 \x01(\x0e\x32\x44.meshtastic.protobuf.ModuleConfig.CannedMessageConfig.InputEventChar\x12\x17\n\x0fupdown1_enabled\x18\x08 \x01(\x08\x12\x13\n\x07\x65nabled\x18\t \x01(\x08\x42\x02\x18\x01\x12\x1e\n\x12\x61llow_input_source\x18\n \x01(\tB\x02\x18\x01\x12\x11\n\tsend_bell\x18\x0b \x01(\x08\"c\n\x0eInputEventChar\x12\x08\n\x04NONE\x10\x00\x12\x06\n\x02UP\x10\x11\x12\x08\n\x04\x44OWN\x10\x12\x12\x08\n\x04LEFT\x10\x13\x12\t\n\x05RIGHT\x10\x14\x12\n\n\x06SELECT\x10\n\x12\x08\n\x04\x42\x41\x43K\x10\x1b\x12\n\n\x06\x43\x41NCEL\x10\x18\x1a\x65\n\x15\x41mbientLightingConfig\x12\x11\n\tled_state\x18\x01 \x01(\x08\x12\x0f\n\x07\x63urrent\x18\x02 \x01(\r\x12\x0b\n\x03red\x18\x03 \x01(\r\x12\r\n\x05green\x18\x04 \x01(\r\x12\x0c\n\x04\x62lue\x18\x05 \x01(\rB\x11\n\x0fpayload_variant\"m\n\x11RemoteHardwarePin\x12\x10\n\x08gpio_pin\x18\x01 \x01(\r\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x38\n\x04type\x18\x03 \x01(\x0e\x32*.meshtastic.protobuf.RemoteHardwarePinType*I\n\x15RemoteHardwarePinType\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x10\n\x0c\x44IGITAL_READ\x10\x01\x12\x11\n\rDIGITAL_WRITE\x10\x02\x42h\n\x14org.meshtastic.protoB\x12ModuleConfigProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.module_config_pb2', _globals) +if _descriptor._USE_C_DESCRIPTORS == False: + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\022ModuleConfigProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' + _MODULECONFIG_CANNEDMESSAGECONFIG.fields_by_name['enabled']._options = None + _MODULECONFIG_CANNEDMESSAGECONFIG.fields_by_name['enabled']._serialized_options = b'\030\001' + _MODULECONFIG_CANNEDMESSAGECONFIG.fields_by_name['allow_input_source']._options = None + _MODULECONFIG_CANNEDMESSAGECONFIG.fields_by_name['allow_input_source']._serialized_options = b'\030\001' + _globals['_REMOTEHARDWAREPINTYPE']._serialized_start=5293 + _globals['_REMOTEHARDWAREPINTYPE']._serialized_end=5366 + _globals['_MODULECONFIG']._serialized_start=65 + _globals['_MODULECONFIG']._serialized_end=5180 + _globals['_MODULECONFIG_MQTTCONFIG']._serialized_start=1080 + _globals['_MODULECONFIG_MQTTCONFIG']._serialized_end=1393 + _globals['_MODULECONFIG_MAPREPORTSETTINGS']._serialized_start=1395 + _globals['_MODULECONFIG_MAPREPORTSETTINGS']._serialized_end=1505 + _globals['_MODULECONFIG_REMOTEHARDWARECONFIG']._serialized_start=1508 + _globals['_MODULECONFIG_REMOTEHARDWARECONFIG']._serialized_end=1647 + _globals['_MODULECONFIG_NEIGHBORINFOCONFIG']._serialized_start=1649 + _globals['_MODULECONFIG_NEIGHBORINFOCONFIG']._serialized_end=1739 + _globals['_MODULECONFIG_DETECTIONSENSORCONFIG']._serialized_start=1742 + _globals['_MODULECONFIG_DETECTIONSENSORCONFIG']._serialized_end=2158 + _globals['_MODULECONFIG_DETECTIONSENSORCONFIG_TRIGGERTYPE']._serialized_start=2022 + _globals['_MODULECONFIG_DETECTIONSENSORCONFIG_TRIGGERTYPE']._serialized_end=2158 + _globals['_MODULECONFIG_AUDIOCONFIG']._serialized_start=2161 + _globals['_MODULECONFIG_AUDIOCONFIG']._serialized_end=2526 + _globals['_MODULECONFIG_AUDIOCONFIG_AUDIO_BAUD']._serialized_start=2359 + _globals['_MODULECONFIG_AUDIOCONFIG_AUDIO_BAUD']._serialized_end=2526 + _globals['_MODULECONFIG_PAXCOUNTERCONFIG']._serialized_start=2528 + _globals['_MODULECONFIG_PAXCOUNTERCONFIG']._serialized_end=2646 + _globals['_MODULECONFIG_SERIALCONFIG']._serialized_start=2649 + _globals['_MODULECONFIG_SERIALCONFIG']._serialized_end=3319 + _globals['_MODULECONFIG_SERIALCONFIG_SERIAL_BAUD']._serialized_start=2926 + _globals['_MODULECONFIG_SERIALCONFIG_SERIAL_BAUD']._serialized_end=3192 + _globals['_MODULECONFIG_SERIALCONFIG_SERIAL_MODE']._serialized_start=3194 + _globals['_MODULECONFIG_SERIALCONFIG_SERIAL_MODE']._serialized_end=3319 + _globals['_MODULECONFIG_EXTERNALNOTIFICATIONCONFIG']._serialized_start=3322 + _globals['_MODULECONFIG_EXTERNALNOTIFICATIONCONFIG']._serialized_end=3683 + _globals['_MODULECONFIG_STOREFORWARDCONFIG']._serialized_start=3686 + _globals['_MODULECONFIG_STOREFORWARDCONFIG']._serialized_end=3837 + _globals['_MODULECONFIG_RANGETESTCONFIG']._serialized_start=3839 + _globals['_MODULECONFIG_RANGETESTCONFIG']._serialized_end=3928 + _globals['_MODULECONFIG_TELEMETRYCONFIG']._serialized_start=3931 + _globals['_MODULECONFIG_TELEMETRYCONFIG']._serialized_end=4422 + _globals['_MODULECONFIG_CANNEDMESSAGECONFIG']._serialized_start=4425 + _globals['_MODULECONFIG_CANNEDMESSAGECONFIG']._serialized_end=5058 + _globals['_MODULECONFIG_CANNEDMESSAGECONFIG_INPUTEVENTCHAR']._serialized_start=4959 + _globals['_MODULECONFIG_CANNEDMESSAGECONFIG_INPUTEVENTCHAR']._serialized_end=5058 + _globals['_MODULECONFIG_AMBIENTLIGHTINGCONFIG']._serialized_start=5060 + _globals['_MODULECONFIG_AMBIENTLIGHTINGCONFIG']._serialized_end=5161 + _globals['_REMOTEHARDWAREPIN']._serialized_start=5182 + _globals['_REMOTEHARDWAREPIN']._serialized_end=5291 +# @@protoc_insertion_point(module_scope) diff --git a/meshtastic/protobuf/module_config_pb2.pyi b/meshtastic/protobuf/module_config_pb2.pyi new file mode 100644 index 00000000..500528e7 --- /dev/null +++ b/meshtastic/protobuf/module_config_pb2.pyi @@ -0,0 +1,1313 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" + +import builtins +import collections.abc +import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.internal.enum_type_wrapper +import google.protobuf.message +import sys +import typing + +if sys.version_info >= (3, 10): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +class _RemoteHardwarePinType: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _RemoteHardwarePinTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_RemoteHardwarePinType.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + UNKNOWN: _RemoteHardwarePinType.ValueType # 0 + """ + Unset/unused + """ + DIGITAL_READ: _RemoteHardwarePinType.ValueType # 1 + """ + GPIO pin can be read (if it is high / low) + """ + DIGITAL_WRITE: _RemoteHardwarePinType.ValueType # 2 + """ + GPIO pin can be written to (high / low) + """ + +class RemoteHardwarePinType(_RemoteHardwarePinType, metaclass=_RemoteHardwarePinTypeEnumTypeWrapper): ... + +UNKNOWN: RemoteHardwarePinType.ValueType # 0 +""" +Unset/unused +""" +DIGITAL_READ: RemoteHardwarePinType.ValueType # 1 +""" +GPIO pin can be read (if it is high / low) +""" +DIGITAL_WRITE: RemoteHardwarePinType.ValueType # 2 +""" +GPIO pin can be written to (high / low) +""" +global___RemoteHardwarePinType = RemoteHardwarePinType + +@typing.final +class ModuleConfig(google.protobuf.message.Message): + """ + Module Config + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + @typing.final + class MQTTConfig(google.protobuf.message.Message): + """ + MQTT Client Config + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ENABLED_FIELD_NUMBER: builtins.int + ADDRESS_FIELD_NUMBER: builtins.int + USERNAME_FIELD_NUMBER: builtins.int + PASSWORD_FIELD_NUMBER: builtins.int + ENCRYPTION_ENABLED_FIELD_NUMBER: builtins.int + JSON_ENABLED_FIELD_NUMBER: builtins.int + TLS_ENABLED_FIELD_NUMBER: builtins.int + ROOT_FIELD_NUMBER: builtins.int + PROXY_TO_CLIENT_ENABLED_FIELD_NUMBER: builtins.int + MAP_REPORTING_ENABLED_FIELD_NUMBER: builtins.int + MAP_REPORT_SETTINGS_FIELD_NUMBER: builtins.int + enabled: builtins.bool + """ + If a meshtastic node is able to reach the internet it will normally attempt to gateway any channels that are marked as + is_uplink_enabled or is_downlink_enabled. + """ + address: builtins.str + """ + The server to use for our MQTT global message gateway feature. + If not set, the default server will be used + """ + username: builtins.str + """ + MQTT username to use (most useful for a custom MQTT server). + If using a custom server, this will be honoured even if empty. + If using the default server, this will only be honoured if set, otherwise the device will use the default username + """ + password: builtins.str + """ + MQTT password to use (most useful for a custom MQTT server). + If using a custom server, this will be honoured even if empty. + If using the default server, this will only be honoured if set, otherwise the device will use the default password + """ + encryption_enabled: builtins.bool + """ + Whether to send encrypted or decrypted packets to MQTT. + This parameter is only honoured if you also set server + (the default official mqtt.meshtastic.org server can handle encrypted packets) + Decrypted packets may be useful for external systems that want to consume meshtastic packets + """ + json_enabled: builtins.bool + """ + Whether to send / consume json packets on MQTT + """ + tls_enabled: builtins.bool + """ + If true, we attempt to establish a secure connection using TLS + """ + root: builtins.str + """ + The root topic to use for MQTT messages. Default is "msh". + This is useful if you want to use a single MQTT server for multiple meshtastic networks and separate them via ACLs + """ + proxy_to_client_enabled: builtins.bool + """ + If true, we can use the connected phone / client to proxy messages to MQTT instead of a direct connection + """ + map_reporting_enabled: builtins.bool + """ + If true, we will periodically report unencrypted information about our node to a map via MQTT + """ + @property + def map_report_settings(self) -> global___ModuleConfig.MapReportSettings: + """ + Settings for reporting information about our node to a map via MQTT + """ + + def __init__( + self, + *, + enabled: builtins.bool = ..., + address: builtins.str = ..., + username: builtins.str = ..., + password: builtins.str = ..., + encryption_enabled: builtins.bool = ..., + json_enabled: builtins.bool = ..., + tls_enabled: builtins.bool = ..., + root: builtins.str = ..., + proxy_to_client_enabled: builtins.bool = ..., + map_reporting_enabled: builtins.bool = ..., + map_report_settings: global___ModuleConfig.MapReportSettings | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["map_report_settings", b"map_report_settings"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["address", b"address", "enabled", b"enabled", "encryption_enabled", b"encryption_enabled", "json_enabled", b"json_enabled", "map_report_settings", b"map_report_settings", "map_reporting_enabled", b"map_reporting_enabled", "password", b"password", "proxy_to_client_enabled", b"proxy_to_client_enabled", "root", b"root", "tls_enabled", b"tls_enabled", "username", b"username"]) -> None: ... + + @typing.final + class MapReportSettings(google.protobuf.message.Message): + """ + Settings for reporting unencrypted information about our node to a map via MQTT + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + PUBLISH_INTERVAL_SECS_FIELD_NUMBER: builtins.int + POSITION_PRECISION_FIELD_NUMBER: builtins.int + SHOULD_REPORT_LOCATION_FIELD_NUMBER: builtins.int + publish_interval_secs: builtins.int + """ + How often we should report our info to the map (in seconds) + """ + position_precision: builtins.int + """ + Bits of precision for the location sent (default of 32 is full precision). + """ + should_report_location: builtins.bool + """ + Whether we have opted-in to report our location to the map + """ + def __init__( + self, + *, + publish_interval_secs: builtins.int = ..., + position_precision: builtins.int = ..., + should_report_location: builtins.bool = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["position_precision", b"position_precision", "publish_interval_secs", b"publish_interval_secs", "should_report_location", b"should_report_location"]) -> None: ... + + @typing.final + class RemoteHardwareConfig(google.protobuf.message.Message): + """ + RemoteHardwareModule Config + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ENABLED_FIELD_NUMBER: builtins.int + ALLOW_UNDEFINED_PIN_ACCESS_FIELD_NUMBER: builtins.int + AVAILABLE_PINS_FIELD_NUMBER: builtins.int + enabled: builtins.bool + """ + Whether the Module is enabled + """ + allow_undefined_pin_access: builtins.bool + """ + Whether the Module allows consumers to read / write to pins not defined in available_pins + """ + @property + def available_pins(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___RemoteHardwarePin]: + """ + Exposes the available pins to the mesh for reading and writing + """ + + def __init__( + self, + *, + enabled: builtins.bool = ..., + allow_undefined_pin_access: builtins.bool = ..., + available_pins: collections.abc.Iterable[global___RemoteHardwarePin] | None = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["allow_undefined_pin_access", b"allow_undefined_pin_access", "available_pins", b"available_pins", "enabled", b"enabled"]) -> None: ... + + @typing.final + class NeighborInfoConfig(google.protobuf.message.Message): + """ + NeighborInfoModule Config + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ENABLED_FIELD_NUMBER: builtins.int + UPDATE_INTERVAL_FIELD_NUMBER: builtins.int + TRANSMIT_OVER_LORA_FIELD_NUMBER: builtins.int + enabled: builtins.bool + """ + Whether the Module is enabled + """ + update_interval: builtins.int + """ + Interval in seconds of how often we should try to send our + Neighbor Info (minimum is 14400, i.e., 4 hours) + """ + transmit_over_lora: builtins.bool + """ + Whether in addition to sending it to MQTT and the PhoneAPI, our NeighborInfo should be transmitted over LoRa. + Note that this is not available on a channel with default key and name. + """ + def __init__( + self, + *, + enabled: builtins.bool = ..., + update_interval: builtins.int = ..., + transmit_over_lora: builtins.bool = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["enabled", b"enabled", "transmit_over_lora", b"transmit_over_lora", "update_interval", b"update_interval"]) -> None: ... + + @typing.final + class DetectionSensorConfig(google.protobuf.message.Message): + """ + Detection Sensor Module Config + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + class _TriggerType: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + + class _TriggerTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[ModuleConfig.DetectionSensorConfig._TriggerType.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + LOGIC_LOW: ModuleConfig.DetectionSensorConfig._TriggerType.ValueType # 0 + """Event is triggered if pin is low""" + LOGIC_HIGH: ModuleConfig.DetectionSensorConfig._TriggerType.ValueType # 1 + """Event is triggered if pin is high""" + FALLING_EDGE: ModuleConfig.DetectionSensorConfig._TriggerType.ValueType # 2 + """Event is triggered when pin goes high to low""" + RISING_EDGE: ModuleConfig.DetectionSensorConfig._TriggerType.ValueType # 3 + """Event is triggered when pin goes low to high""" + EITHER_EDGE_ACTIVE_LOW: ModuleConfig.DetectionSensorConfig._TriggerType.ValueType # 4 + """Event is triggered on every pin state change, low is considered to be + "active" + """ + EITHER_EDGE_ACTIVE_HIGH: ModuleConfig.DetectionSensorConfig._TriggerType.ValueType # 5 + """Event is triggered on every pin state change, high is considered to be + "active" + """ + + class TriggerType(_TriggerType, metaclass=_TriggerTypeEnumTypeWrapper): ... + LOGIC_LOW: ModuleConfig.DetectionSensorConfig.TriggerType.ValueType # 0 + """Event is triggered if pin is low""" + LOGIC_HIGH: ModuleConfig.DetectionSensorConfig.TriggerType.ValueType # 1 + """Event is triggered if pin is high""" + FALLING_EDGE: ModuleConfig.DetectionSensorConfig.TriggerType.ValueType # 2 + """Event is triggered when pin goes high to low""" + RISING_EDGE: ModuleConfig.DetectionSensorConfig.TriggerType.ValueType # 3 + """Event is triggered when pin goes low to high""" + EITHER_EDGE_ACTIVE_LOW: ModuleConfig.DetectionSensorConfig.TriggerType.ValueType # 4 + """Event is triggered on every pin state change, low is considered to be + "active" + """ + EITHER_EDGE_ACTIVE_HIGH: ModuleConfig.DetectionSensorConfig.TriggerType.ValueType # 5 + """Event is triggered on every pin state change, high is considered to be + "active" + """ + + ENABLED_FIELD_NUMBER: builtins.int + MINIMUM_BROADCAST_SECS_FIELD_NUMBER: builtins.int + STATE_BROADCAST_SECS_FIELD_NUMBER: builtins.int + SEND_BELL_FIELD_NUMBER: builtins.int + NAME_FIELD_NUMBER: builtins.int + MONITOR_PIN_FIELD_NUMBER: builtins.int + DETECTION_TRIGGER_TYPE_FIELD_NUMBER: builtins.int + USE_PULLUP_FIELD_NUMBER: builtins.int + enabled: builtins.bool + """ + Whether the Module is enabled + """ + minimum_broadcast_secs: builtins.int + """ + Interval in seconds of how often we can send a message to the mesh when a + trigger event is detected + """ + state_broadcast_secs: builtins.int + """ + Interval in seconds of how often we should send a message to the mesh + with the current state regardless of trigger events When set to 0, only + trigger events will be broadcasted Works as a sort of status heartbeat + for peace of mind + """ + send_bell: builtins.bool + """ + Send ASCII bell with alert message + Useful for triggering ext. notification on bell + """ + name: builtins.str + """ + Friendly name used to format message sent to mesh + Example: A name "Motion" would result in a message "Motion detected" + Maximum length of 20 characters + """ + monitor_pin: builtins.int + """ + GPIO pin to monitor for state changes + """ + detection_trigger_type: global___ModuleConfig.DetectionSensorConfig.TriggerType.ValueType + """ + The type of trigger event to be used + """ + use_pullup: builtins.bool + """ + Whether or not use INPUT_PULLUP mode for GPIO pin + Only applicable if the board uses pull-up resistors on the pin + """ + def __init__( + self, + *, + enabled: builtins.bool = ..., + minimum_broadcast_secs: builtins.int = ..., + state_broadcast_secs: builtins.int = ..., + send_bell: builtins.bool = ..., + name: builtins.str = ..., + monitor_pin: builtins.int = ..., + detection_trigger_type: global___ModuleConfig.DetectionSensorConfig.TriggerType.ValueType = ..., + use_pullup: builtins.bool = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["detection_trigger_type", b"detection_trigger_type", "enabled", b"enabled", "minimum_broadcast_secs", b"minimum_broadcast_secs", "monitor_pin", b"monitor_pin", "name", b"name", "send_bell", b"send_bell", "state_broadcast_secs", b"state_broadcast_secs", "use_pullup", b"use_pullup"]) -> None: ... + + @typing.final + class AudioConfig(google.protobuf.message.Message): + """ + Audio Config for codec2 voice + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + class _Audio_Baud: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + + class _Audio_BaudEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[ModuleConfig.AudioConfig._Audio_Baud.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + CODEC2_DEFAULT: ModuleConfig.AudioConfig._Audio_Baud.ValueType # 0 + CODEC2_3200: ModuleConfig.AudioConfig._Audio_Baud.ValueType # 1 + CODEC2_2400: ModuleConfig.AudioConfig._Audio_Baud.ValueType # 2 + CODEC2_1600: ModuleConfig.AudioConfig._Audio_Baud.ValueType # 3 + CODEC2_1400: ModuleConfig.AudioConfig._Audio_Baud.ValueType # 4 + CODEC2_1300: ModuleConfig.AudioConfig._Audio_Baud.ValueType # 5 + CODEC2_1200: ModuleConfig.AudioConfig._Audio_Baud.ValueType # 6 + CODEC2_700: ModuleConfig.AudioConfig._Audio_Baud.ValueType # 7 + CODEC2_700B: ModuleConfig.AudioConfig._Audio_Baud.ValueType # 8 + + class Audio_Baud(_Audio_Baud, metaclass=_Audio_BaudEnumTypeWrapper): + """ + Baudrate for codec2 voice + """ + + CODEC2_DEFAULT: ModuleConfig.AudioConfig.Audio_Baud.ValueType # 0 + CODEC2_3200: ModuleConfig.AudioConfig.Audio_Baud.ValueType # 1 + CODEC2_2400: ModuleConfig.AudioConfig.Audio_Baud.ValueType # 2 + CODEC2_1600: ModuleConfig.AudioConfig.Audio_Baud.ValueType # 3 + CODEC2_1400: ModuleConfig.AudioConfig.Audio_Baud.ValueType # 4 + CODEC2_1300: ModuleConfig.AudioConfig.Audio_Baud.ValueType # 5 + CODEC2_1200: ModuleConfig.AudioConfig.Audio_Baud.ValueType # 6 + CODEC2_700: ModuleConfig.AudioConfig.Audio_Baud.ValueType # 7 + CODEC2_700B: ModuleConfig.AudioConfig.Audio_Baud.ValueType # 8 + + CODEC2_ENABLED_FIELD_NUMBER: builtins.int + PTT_PIN_FIELD_NUMBER: builtins.int + BITRATE_FIELD_NUMBER: builtins.int + I2S_WS_FIELD_NUMBER: builtins.int + I2S_SD_FIELD_NUMBER: builtins.int + I2S_DIN_FIELD_NUMBER: builtins.int + I2S_SCK_FIELD_NUMBER: builtins.int + codec2_enabled: builtins.bool + """ + Whether Audio is enabled + """ + ptt_pin: builtins.int + """ + PTT Pin + """ + bitrate: global___ModuleConfig.AudioConfig.Audio_Baud.ValueType + """ + The audio sample rate to use for codec2 + """ + i2s_ws: builtins.int + """ + I2S Word Select + """ + i2s_sd: builtins.int + """ + I2S Data IN + """ + i2s_din: builtins.int + """ + I2S Data OUT + """ + i2s_sck: builtins.int + """ + I2S Clock + """ + def __init__( + self, + *, + codec2_enabled: builtins.bool = ..., + ptt_pin: builtins.int = ..., + bitrate: global___ModuleConfig.AudioConfig.Audio_Baud.ValueType = ..., + i2s_ws: builtins.int = ..., + i2s_sd: builtins.int = ..., + i2s_din: builtins.int = ..., + i2s_sck: builtins.int = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["bitrate", b"bitrate", "codec2_enabled", b"codec2_enabled", "i2s_din", b"i2s_din", "i2s_sck", b"i2s_sck", "i2s_sd", b"i2s_sd", "i2s_ws", b"i2s_ws", "ptt_pin", b"ptt_pin"]) -> None: ... + + @typing.final + class PaxcounterConfig(google.protobuf.message.Message): + """ + Config for the Paxcounter Module + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ENABLED_FIELD_NUMBER: builtins.int + PAXCOUNTER_UPDATE_INTERVAL_FIELD_NUMBER: builtins.int + WIFI_THRESHOLD_FIELD_NUMBER: builtins.int + BLE_THRESHOLD_FIELD_NUMBER: builtins.int + enabled: builtins.bool + """ + Enable the Paxcounter Module + """ + paxcounter_update_interval: builtins.int + """ + Interval in seconds of how often we should try to send our + metrics to the mesh + """ + wifi_threshold: builtins.int + """ + WiFi RSSI threshold. Defaults to -80 + """ + ble_threshold: builtins.int + """ + BLE RSSI threshold. Defaults to -80 + """ + def __init__( + self, + *, + enabled: builtins.bool = ..., + paxcounter_update_interval: builtins.int = ..., + wifi_threshold: builtins.int = ..., + ble_threshold: builtins.int = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["ble_threshold", b"ble_threshold", "enabled", b"enabled", "paxcounter_update_interval", b"paxcounter_update_interval", "wifi_threshold", b"wifi_threshold"]) -> None: ... + + @typing.final + class SerialConfig(google.protobuf.message.Message): + """ + Serial Config + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + class _Serial_Baud: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + + class _Serial_BaudEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[ModuleConfig.SerialConfig._Serial_Baud.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + BAUD_DEFAULT: ModuleConfig.SerialConfig._Serial_Baud.ValueType # 0 + BAUD_110: ModuleConfig.SerialConfig._Serial_Baud.ValueType # 1 + BAUD_300: ModuleConfig.SerialConfig._Serial_Baud.ValueType # 2 + BAUD_600: ModuleConfig.SerialConfig._Serial_Baud.ValueType # 3 + BAUD_1200: ModuleConfig.SerialConfig._Serial_Baud.ValueType # 4 + BAUD_2400: ModuleConfig.SerialConfig._Serial_Baud.ValueType # 5 + BAUD_4800: ModuleConfig.SerialConfig._Serial_Baud.ValueType # 6 + BAUD_9600: ModuleConfig.SerialConfig._Serial_Baud.ValueType # 7 + BAUD_19200: ModuleConfig.SerialConfig._Serial_Baud.ValueType # 8 + BAUD_38400: ModuleConfig.SerialConfig._Serial_Baud.ValueType # 9 + BAUD_57600: ModuleConfig.SerialConfig._Serial_Baud.ValueType # 10 + BAUD_115200: ModuleConfig.SerialConfig._Serial_Baud.ValueType # 11 + BAUD_230400: ModuleConfig.SerialConfig._Serial_Baud.ValueType # 12 + BAUD_460800: ModuleConfig.SerialConfig._Serial_Baud.ValueType # 13 + BAUD_576000: ModuleConfig.SerialConfig._Serial_Baud.ValueType # 14 + BAUD_921600: ModuleConfig.SerialConfig._Serial_Baud.ValueType # 15 + + class Serial_Baud(_Serial_Baud, metaclass=_Serial_BaudEnumTypeWrapper): + """ + TODO: REPLACE + """ + + BAUD_DEFAULT: ModuleConfig.SerialConfig.Serial_Baud.ValueType # 0 + BAUD_110: ModuleConfig.SerialConfig.Serial_Baud.ValueType # 1 + BAUD_300: ModuleConfig.SerialConfig.Serial_Baud.ValueType # 2 + BAUD_600: ModuleConfig.SerialConfig.Serial_Baud.ValueType # 3 + BAUD_1200: ModuleConfig.SerialConfig.Serial_Baud.ValueType # 4 + BAUD_2400: ModuleConfig.SerialConfig.Serial_Baud.ValueType # 5 + BAUD_4800: ModuleConfig.SerialConfig.Serial_Baud.ValueType # 6 + BAUD_9600: ModuleConfig.SerialConfig.Serial_Baud.ValueType # 7 + BAUD_19200: ModuleConfig.SerialConfig.Serial_Baud.ValueType # 8 + BAUD_38400: ModuleConfig.SerialConfig.Serial_Baud.ValueType # 9 + BAUD_57600: ModuleConfig.SerialConfig.Serial_Baud.ValueType # 10 + BAUD_115200: ModuleConfig.SerialConfig.Serial_Baud.ValueType # 11 + BAUD_230400: ModuleConfig.SerialConfig.Serial_Baud.ValueType # 12 + BAUD_460800: ModuleConfig.SerialConfig.Serial_Baud.ValueType # 13 + BAUD_576000: ModuleConfig.SerialConfig.Serial_Baud.ValueType # 14 + BAUD_921600: ModuleConfig.SerialConfig.Serial_Baud.ValueType # 15 + + class _Serial_Mode: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + + class _Serial_ModeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[ModuleConfig.SerialConfig._Serial_Mode.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + DEFAULT: ModuleConfig.SerialConfig._Serial_Mode.ValueType # 0 + SIMPLE: ModuleConfig.SerialConfig._Serial_Mode.ValueType # 1 + PROTO: ModuleConfig.SerialConfig._Serial_Mode.ValueType # 2 + TEXTMSG: ModuleConfig.SerialConfig._Serial_Mode.ValueType # 3 + NMEA: ModuleConfig.SerialConfig._Serial_Mode.ValueType # 4 + CALTOPO: ModuleConfig.SerialConfig._Serial_Mode.ValueType # 5 + """NMEA messages specifically tailored for CalTopo""" + WS85: ModuleConfig.SerialConfig._Serial_Mode.ValueType # 6 + """Ecowitt WS85 weather station""" + VE_DIRECT: ModuleConfig.SerialConfig._Serial_Mode.ValueType # 7 + """VE.Direct is a serial protocol used by Victron Energy products + https://beta.ivc.no/wiki/index.php/Victron_VE_Direct_DIY_Cable + """ + MS_CONFIG: ModuleConfig.SerialConfig._Serial_Mode.ValueType # 8 + """Used to configure and view some parameters of MeshSolar. + https://heltec.org/project/meshsolar/ + """ + + class Serial_Mode(_Serial_Mode, metaclass=_Serial_ModeEnumTypeWrapper): + """ + TODO: REPLACE + """ + + DEFAULT: ModuleConfig.SerialConfig.Serial_Mode.ValueType # 0 + SIMPLE: ModuleConfig.SerialConfig.Serial_Mode.ValueType # 1 + PROTO: ModuleConfig.SerialConfig.Serial_Mode.ValueType # 2 + TEXTMSG: ModuleConfig.SerialConfig.Serial_Mode.ValueType # 3 + NMEA: ModuleConfig.SerialConfig.Serial_Mode.ValueType # 4 + CALTOPO: ModuleConfig.SerialConfig.Serial_Mode.ValueType # 5 + """NMEA messages specifically tailored for CalTopo""" + WS85: ModuleConfig.SerialConfig.Serial_Mode.ValueType # 6 + """Ecowitt WS85 weather station""" + VE_DIRECT: ModuleConfig.SerialConfig.Serial_Mode.ValueType # 7 + """VE.Direct is a serial protocol used by Victron Energy products + https://beta.ivc.no/wiki/index.php/Victron_VE_Direct_DIY_Cable + """ + MS_CONFIG: ModuleConfig.SerialConfig.Serial_Mode.ValueType # 8 + """Used to configure and view some parameters of MeshSolar. + https://heltec.org/project/meshsolar/ + """ + + ENABLED_FIELD_NUMBER: builtins.int + ECHO_FIELD_NUMBER: builtins.int + RXD_FIELD_NUMBER: builtins.int + TXD_FIELD_NUMBER: builtins.int + BAUD_FIELD_NUMBER: builtins.int + TIMEOUT_FIELD_NUMBER: builtins.int + MODE_FIELD_NUMBER: builtins.int + OVERRIDE_CONSOLE_SERIAL_PORT_FIELD_NUMBER: builtins.int + enabled: builtins.bool + """ + Preferences for the SerialModule + """ + echo: builtins.bool + """ + TODO: REPLACE + """ + rxd: builtins.int + """ + RX pin (should match Arduino gpio pin number) + """ + txd: builtins.int + """ + TX pin (should match Arduino gpio pin number) + """ + baud: global___ModuleConfig.SerialConfig.Serial_Baud.ValueType + """ + Serial baud rate + """ + timeout: builtins.int + """ + TODO: REPLACE + """ + mode: global___ModuleConfig.SerialConfig.Serial_Mode.ValueType + """ + Mode for serial module operation + """ + override_console_serial_port: builtins.bool + """ + Overrides the platform's defacto Serial port instance to use with Serial module config settings + This is currently only usable in output modes like NMEA / CalTopo and may behave strangely or not work at all in other modes + Existing logging over the Serial Console will still be present + """ + def __init__( + self, + *, + enabled: builtins.bool = ..., + echo: builtins.bool = ..., + rxd: builtins.int = ..., + txd: builtins.int = ..., + baud: global___ModuleConfig.SerialConfig.Serial_Baud.ValueType = ..., + timeout: builtins.int = ..., + mode: global___ModuleConfig.SerialConfig.Serial_Mode.ValueType = ..., + override_console_serial_port: builtins.bool = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["baud", b"baud", "echo", b"echo", "enabled", b"enabled", "mode", b"mode", "override_console_serial_port", b"override_console_serial_port", "rxd", b"rxd", "timeout", b"timeout", "txd", b"txd"]) -> None: ... + + @typing.final + class ExternalNotificationConfig(google.protobuf.message.Message): + """ + External Notifications Config + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ENABLED_FIELD_NUMBER: builtins.int + OUTPUT_MS_FIELD_NUMBER: builtins.int + OUTPUT_FIELD_NUMBER: builtins.int + OUTPUT_VIBRA_FIELD_NUMBER: builtins.int + OUTPUT_BUZZER_FIELD_NUMBER: builtins.int + ACTIVE_FIELD_NUMBER: builtins.int + ALERT_MESSAGE_FIELD_NUMBER: builtins.int + ALERT_MESSAGE_VIBRA_FIELD_NUMBER: builtins.int + ALERT_MESSAGE_BUZZER_FIELD_NUMBER: builtins.int + ALERT_BELL_FIELD_NUMBER: builtins.int + ALERT_BELL_VIBRA_FIELD_NUMBER: builtins.int + ALERT_BELL_BUZZER_FIELD_NUMBER: builtins.int + USE_PWM_FIELD_NUMBER: builtins.int + NAG_TIMEOUT_FIELD_NUMBER: builtins.int + USE_I2S_AS_BUZZER_FIELD_NUMBER: builtins.int + enabled: builtins.bool + """ + Enable the ExternalNotificationModule + """ + output_ms: builtins.int + """ + When using in On/Off mode, keep the output on for this many + milliseconds. Default 1000ms (1 second). + """ + output: builtins.int + """ + Define the output pin GPIO setting Defaults to + EXT_NOTIFY_OUT if set for the board. + In standalone devices this pin should drive the LED to match the UI. + """ + output_vibra: builtins.int + """ + Optional: Define a secondary output pin for a vibra motor + This is used in standalone devices to match the UI. + """ + output_buzzer: builtins.int + """ + Optional: Define a tertiary output pin for an active buzzer + This is used in standalone devices to to match the UI. + """ + active: builtins.bool + """ + IF this is true, the 'output' Pin will be pulled active high, false + means active low. + """ + alert_message: builtins.bool + """ + True: Alert when a text message arrives (output) + """ + alert_message_vibra: builtins.bool + """ + True: Alert when a text message arrives (output_vibra) + """ + alert_message_buzzer: builtins.bool + """ + True: Alert when a text message arrives (output_buzzer) + """ + alert_bell: builtins.bool + """ + True: Alert when the bell character is received (output) + """ + alert_bell_vibra: builtins.bool + """ + True: Alert when the bell character is received (output_vibra) + """ + alert_bell_buzzer: builtins.bool + """ + True: Alert when the bell character is received (output_buzzer) + """ + use_pwm: builtins.bool + """ + use a PWM output instead of a simple on/off output. This will ignore + the 'output', 'output_ms' and 'active' settings and use the + device.buzzer_gpio instead. + """ + nag_timeout: builtins.int + """ + The notification will toggle with 'output_ms' for this time of seconds. + Default is 0 which means don't repeat at all. 60 would mean blink + and/or beep for 60 seconds + """ + use_i2s_as_buzzer: builtins.bool + """ + When true, enables devices with native I2S audio output to use the RTTTL over speaker like a buzzer + T-Watch S3 and T-Deck for example have this capability + """ + def __init__( + self, + *, + enabled: builtins.bool = ..., + output_ms: builtins.int = ..., + output: builtins.int = ..., + output_vibra: builtins.int = ..., + output_buzzer: builtins.int = ..., + active: builtins.bool = ..., + alert_message: builtins.bool = ..., + alert_message_vibra: builtins.bool = ..., + alert_message_buzzer: builtins.bool = ..., + alert_bell: builtins.bool = ..., + alert_bell_vibra: builtins.bool = ..., + alert_bell_buzzer: builtins.bool = ..., + use_pwm: builtins.bool = ..., + nag_timeout: builtins.int = ..., + use_i2s_as_buzzer: builtins.bool = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["active", b"active", "alert_bell", b"alert_bell", "alert_bell_buzzer", b"alert_bell_buzzer", "alert_bell_vibra", b"alert_bell_vibra", "alert_message", b"alert_message", "alert_message_buzzer", b"alert_message_buzzer", "alert_message_vibra", b"alert_message_vibra", "enabled", b"enabled", "nag_timeout", b"nag_timeout", "output", b"output", "output_buzzer", b"output_buzzer", "output_ms", b"output_ms", "output_vibra", b"output_vibra", "use_i2s_as_buzzer", b"use_i2s_as_buzzer", "use_pwm", b"use_pwm"]) -> None: ... + + @typing.final + class StoreForwardConfig(google.protobuf.message.Message): + """ + Store and Forward Module Config + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ENABLED_FIELD_NUMBER: builtins.int + HEARTBEAT_FIELD_NUMBER: builtins.int + RECORDS_FIELD_NUMBER: builtins.int + HISTORY_RETURN_MAX_FIELD_NUMBER: builtins.int + HISTORY_RETURN_WINDOW_FIELD_NUMBER: builtins.int + IS_SERVER_FIELD_NUMBER: builtins.int + enabled: builtins.bool + """ + Enable the Store and Forward Module + """ + heartbeat: builtins.bool + """ + TODO: REPLACE + """ + records: builtins.int + """ + TODO: REPLACE + """ + history_return_max: builtins.int + """ + TODO: REPLACE + """ + history_return_window: builtins.int + """ + TODO: REPLACE + """ + is_server: builtins.bool + """ + Set to true to let this node act as a server that stores received messages and resends them upon request. + """ + def __init__( + self, + *, + enabled: builtins.bool = ..., + heartbeat: builtins.bool = ..., + records: builtins.int = ..., + history_return_max: builtins.int = ..., + history_return_window: builtins.int = ..., + is_server: builtins.bool = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["enabled", b"enabled", "heartbeat", b"heartbeat", "history_return_max", b"history_return_max", "history_return_window", b"history_return_window", "is_server", b"is_server", "records", b"records"]) -> None: ... + + @typing.final + class RangeTestConfig(google.protobuf.message.Message): + """ + Preferences for the RangeTestModule + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ENABLED_FIELD_NUMBER: builtins.int + SENDER_FIELD_NUMBER: builtins.int + SAVE_FIELD_NUMBER: builtins.int + CLEAR_ON_REBOOT_FIELD_NUMBER: builtins.int + enabled: builtins.bool + """ + Enable the Range Test Module + """ + sender: builtins.int + """ + Send out range test messages from this node + """ + save: builtins.bool + """ + Bool value indicating that this node should save a RangeTest.csv file. + ESP32 Only + """ + clear_on_reboot: builtins.bool + """ + Bool indicating that the node should cleanup / destroy it's RangeTest.csv file. + ESP32 Only + """ + def __init__( + self, + *, + enabled: builtins.bool = ..., + sender: builtins.int = ..., + save: builtins.bool = ..., + clear_on_reboot: builtins.bool = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["clear_on_reboot", b"clear_on_reboot", "enabled", b"enabled", "save", b"save", "sender", b"sender"]) -> None: ... + + @typing.final + class TelemetryConfig(google.protobuf.message.Message): + """ + Configuration for both device and environment metrics + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + DEVICE_UPDATE_INTERVAL_FIELD_NUMBER: builtins.int + ENVIRONMENT_UPDATE_INTERVAL_FIELD_NUMBER: builtins.int + ENVIRONMENT_MEASUREMENT_ENABLED_FIELD_NUMBER: builtins.int + ENVIRONMENT_SCREEN_ENABLED_FIELD_NUMBER: builtins.int + ENVIRONMENT_DISPLAY_FAHRENHEIT_FIELD_NUMBER: builtins.int + AIR_QUALITY_ENABLED_FIELD_NUMBER: builtins.int + AIR_QUALITY_INTERVAL_FIELD_NUMBER: builtins.int + POWER_MEASUREMENT_ENABLED_FIELD_NUMBER: builtins.int + POWER_UPDATE_INTERVAL_FIELD_NUMBER: builtins.int + POWER_SCREEN_ENABLED_FIELD_NUMBER: builtins.int + HEALTH_MEASUREMENT_ENABLED_FIELD_NUMBER: builtins.int + HEALTH_UPDATE_INTERVAL_FIELD_NUMBER: builtins.int + HEALTH_SCREEN_ENABLED_FIELD_NUMBER: builtins.int + DEVICE_TELEMETRY_ENABLED_FIELD_NUMBER: builtins.int + device_update_interval: builtins.int + """ + Interval in seconds of how often we should try to send our + device metrics to the mesh + """ + environment_update_interval: builtins.int + """ + Interval in seconds of how often we should try to send our + environment measurements to the mesh + """ + environment_measurement_enabled: builtins.bool + """ + Preferences for the Telemetry Module (Environment) + Enable/Disable the telemetry measurement module measurement collection + """ + environment_screen_enabled: builtins.bool + """ + Enable/Disable the telemetry measurement module on-device display + """ + environment_display_fahrenheit: builtins.bool + """ + We'll always read the sensor in Celsius, but sometimes we might want to + display the results in Fahrenheit as a "user preference". + """ + air_quality_enabled: builtins.bool + """ + Enable/Disable the air quality metrics + """ + air_quality_interval: builtins.int + """ + Interval in seconds of how often we should try to send our + air quality metrics to the mesh + """ + power_measurement_enabled: builtins.bool + """ + Enable/disable Power metrics + """ + power_update_interval: builtins.int + """ + Interval in seconds of how often we should try to send our + power metrics to the mesh + """ + power_screen_enabled: builtins.bool + """ + Enable/Disable the power measurement module on-device display + """ + health_measurement_enabled: builtins.bool + """ + Preferences for the (Health) Telemetry Module + Enable/Disable the telemetry measurement module measurement collection + """ + health_update_interval: builtins.int + """ + Interval in seconds of how often we should try to send our + health metrics to the mesh + """ + health_screen_enabled: builtins.bool + """ + Enable/Disable the health telemetry module on-device display + """ + device_telemetry_enabled: builtins.bool + """ + Enable/Disable the device telemetry module to send metrics to the mesh + Note: We will still send telemtry to the connected phone / client every minute over the API + """ + def __init__( + self, + *, + device_update_interval: builtins.int = ..., + environment_update_interval: builtins.int = ..., + environment_measurement_enabled: builtins.bool = ..., + environment_screen_enabled: builtins.bool = ..., + environment_display_fahrenheit: builtins.bool = ..., + air_quality_enabled: builtins.bool = ..., + air_quality_interval: builtins.int = ..., + power_measurement_enabled: builtins.bool = ..., + power_update_interval: builtins.int = ..., + power_screen_enabled: builtins.bool = ..., + health_measurement_enabled: builtins.bool = ..., + health_update_interval: builtins.int = ..., + health_screen_enabled: builtins.bool = ..., + device_telemetry_enabled: builtins.bool = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["air_quality_enabled", b"air_quality_enabled", "air_quality_interval", b"air_quality_interval", "device_telemetry_enabled", b"device_telemetry_enabled", "device_update_interval", b"device_update_interval", "environment_display_fahrenheit", b"environment_display_fahrenheit", "environment_measurement_enabled", b"environment_measurement_enabled", "environment_screen_enabled", b"environment_screen_enabled", "environment_update_interval", b"environment_update_interval", "health_measurement_enabled", b"health_measurement_enabled", "health_screen_enabled", b"health_screen_enabled", "health_update_interval", b"health_update_interval", "power_measurement_enabled", b"power_measurement_enabled", "power_screen_enabled", b"power_screen_enabled", "power_update_interval", b"power_update_interval"]) -> None: ... + + @typing.final + class CannedMessageConfig(google.protobuf.message.Message): + """ + Canned Messages Module Config + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + class _InputEventChar: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + + class _InputEventCharEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[ModuleConfig.CannedMessageConfig._InputEventChar.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + NONE: ModuleConfig.CannedMessageConfig._InputEventChar.ValueType # 0 + """ + TODO: REPLACE + """ + UP: ModuleConfig.CannedMessageConfig._InputEventChar.ValueType # 17 + """ + TODO: REPLACE + """ + DOWN: ModuleConfig.CannedMessageConfig._InputEventChar.ValueType # 18 + """ + TODO: REPLACE + """ + LEFT: ModuleConfig.CannedMessageConfig._InputEventChar.ValueType # 19 + """ + TODO: REPLACE + """ + RIGHT: ModuleConfig.CannedMessageConfig._InputEventChar.ValueType # 20 + """ + TODO: REPLACE + """ + SELECT: ModuleConfig.CannedMessageConfig._InputEventChar.ValueType # 10 + """ + '\\n' + """ + BACK: ModuleConfig.CannedMessageConfig._InputEventChar.ValueType # 27 + """ + TODO: REPLACE + """ + CANCEL: ModuleConfig.CannedMessageConfig._InputEventChar.ValueType # 24 + """ + TODO: REPLACE + """ + + class InputEventChar(_InputEventChar, metaclass=_InputEventCharEnumTypeWrapper): + """ + TODO: REPLACE + """ + + NONE: ModuleConfig.CannedMessageConfig.InputEventChar.ValueType # 0 + """ + TODO: REPLACE + """ + UP: ModuleConfig.CannedMessageConfig.InputEventChar.ValueType # 17 + """ + TODO: REPLACE + """ + DOWN: ModuleConfig.CannedMessageConfig.InputEventChar.ValueType # 18 + """ + TODO: REPLACE + """ + LEFT: ModuleConfig.CannedMessageConfig.InputEventChar.ValueType # 19 + """ + TODO: REPLACE + """ + RIGHT: ModuleConfig.CannedMessageConfig.InputEventChar.ValueType # 20 + """ + TODO: REPLACE + """ + SELECT: ModuleConfig.CannedMessageConfig.InputEventChar.ValueType # 10 + """ + '\\n' + """ + BACK: ModuleConfig.CannedMessageConfig.InputEventChar.ValueType # 27 + """ + TODO: REPLACE + """ + CANCEL: ModuleConfig.CannedMessageConfig.InputEventChar.ValueType # 24 + """ + TODO: REPLACE + """ + + ROTARY1_ENABLED_FIELD_NUMBER: builtins.int + INPUTBROKER_PIN_A_FIELD_NUMBER: builtins.int + INPUTBROKER_PIN_B_FIELD_NUMBER: builtins.int + INPUTBROKER_PIN_PRESS_FIELD_NUMBER: builtins.int + INPUTBROKER_EVENT_CW_FIELD_NUMBER: builtins.int + INPUTBROKER_EVENT_CCW_FIELD_NUMBER: builtins.int + INPUTBROKER_EVENT_PRESS_FIELD_NUMBER: builtins.int + UPDOWN1_ENABLED_FIELD_NUMBER: builtins.int + ENABLED_FIELD_NUMBER: builtins.int + ALLOW_INPUT_SOURCE_FIELD_NUMBER: builtins.int + SEND_BELL_FIELD_NUMBER: builtins.int + rotary1_enabled: builtins.bool + """ + Enable the rotary encoder #1. This is a 'dumb' encoder sending pulses on both A and B pins while rotating. + """ + inputbroker_pin_a: builtins.int + """ + GPIO pin for rotary encoder A port. + """ + inputbroker_pin_b: builtins.int + """ + GPIO pin for rotary encoder B port. + """ + inputbroker_pin_press: builtins.int + """ + GPIO pin for rotary encoder Press port. + """ + inputbroker_event_cw: global___ModuleConfig.CannedMessageConfig.InputEventChar.ValueType + """ + Generate input event on CW of this kind. + """ + inputbroker_event_ccw: global___ModuleConfig.CannedMessageConfig.InputEventChar.ValueType + """ + Generate input event on CCW of this kind. + """ + inputbroker_event_press: global___ModuleConfig.CannedMessageConfig.InputEventChar.ValueType + """ + Generate input event on Press of this kind. + """ + updown1_enabled: builtins.bool + """ + Enable the Up/Down/Select input device. Can be RAK rotary encoder or 3 buttons. Uses the a/b/press definitions from inputbroker. + """ + enabled: builtins.bool + """ + Enable/disable CannedMessageModule. + """ + allow_input_source: builtins.str + """ + Input event origin accepted by the canned message module. + Can be e.g. "rotEnc1", "upDownEnc1", "scanAndSelect", "cardkb", "serialkb", or keyword "_any" + """ + send_bell: builtins.bool + """ + CannedMessageModule also sends a bell character with the messages. + ExternalNotificationModule can benefit from this feature. + """ + def __init__( + self, + *, + rotary1_enabled: builtins.bool = ..., + inputbroker_pin_a: builtins.int = ..., + inputbroker_pin_b: builtins.int = ..., + inputbroker_pin_press: builtins.int = ..., + inputbroker_event_cw: global___ModuleConfig.CannedMessageConfig.InputEventChar.ValueType = ..., + inputbroker_event_ccw: global___ModuleConfig.CannedMessageConfig.InputEventChar.ValueType = ..., + inputbroker_event_press: global___ModuleConfig.CannedMessageConfig.InputEventChar.ValueType = ..., + updown1_enabled: builtins.bool = ..., + enabled: builtins.bool = ..., + allow_input_source: builtins.str = ..., + send_bell: builtins.bool = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["allow_input_source", b"allow_input_source", "enabled", b"enabled", "inputbroker_event_ccw", b"inputbroker_event_ccw", "inputbroker_event_cw", b"inputbroker_event_cw", "inputbroker_event_press", b"inputbroker_event_press", "inputbroker_pin_a", b"inputbroker_pin_a", "inputbroker_pin_b", b"inputbroker_pin_b", "inputbroker_pin_press", b"inputbroker_pin_press", "rotary1_enabled", b"rotary1_enabled", "send_bell", b"send_bell", "updown1_enabled", b"updown1_enabled"]) -> None: ... + + @typing.final + class AmbientLightingConfig(google.protobuf.message.Message): + """ + Ambient Lighting Module - Settings for control of onboard LEDs to allow users to adjust the brightness levels and respective color levels. + Initially created for the RAK14001 RGB LED module. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + LED_STATE_FIELD_NUMBER: builtins.int + CURRENT_FIELD_NUMBER: builtins.int + RED_FIELD_NUMBER: builtins.int + GREEN_FIELD_NUMBER: builtins.int + BLUE_FIELD_NUMBER: builtins.int + led_state: builtins.bool + """ + Sets LED to on or off. + """ + current: builtins.int + """ + Sets the current for the LED output. Default is 10. + """ + red: builtins.int + """ + Sets the red LED level. Values are 0-255. + """ + green: builtins.int + """ + Sets the green LED level. Values are 0-255. + """ + blue: builtins.int + """ + Sets the blue LED level. Values are 0-255. + """ + def __init__( + self, + *, + led_state: builtins.bool = ..., + current: builtins.int = ..., + red: builtins.int = ..., + green: builtins.int = ..., + blue: builtins.int = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["blue", b"blue", "current", b"current", "green", b"green", "led_state", b"led_state", "red", b"red"]) -> None: ... + + MQTT_FIELD_NUMBER: builtins.int + SERIAL_FIELD_NUMBER: builtins.int + EXTERNAL_NOTIFICATION_FIELD_NUMBER: builtins.int + STORE_FORWARD_FIELD_NUMBER: builtins.int + RANGE_TEST_FIELD_NUMBER: builtins.int + TELEMETRY_FIELD_NUMBER: builtins.int + CANNED_MESSAGE_FIELD_NUMBER: builtins.int + AUDIO_FIELD_NUMBER: builtins.int + REMOTE_HARDWARE_FIELD_NUMBER: builtins.int + NEIGHBOR_INFO_FIELD_NUMBER: builtins.int + AMBIENT_LIGHTING_FIELD_NUMBER: builtins.int + DETECTION_SENSOR_FIELD_NUMBER: builtins.int + PAXCOUNTER_FIELD_NUMBER: builtins.int + @property + def mqtt(self) -> global___ModuleConfig.MQTTConfig: + """ + TODO: REPLACE + """ + + @property + def serial(self) -> global___ModuleConfig.SerialConfig: + """ + TODO: REPLACE + """ + + @property + def external_notification(self) -> global___ModuleConfig.ExternalNotificationConfig: + """ + TODO: REPLACE + """ + + @property + def store_forward(self) -> global___ModuleConfig.StoreForwardConfig: + """ + TODO: REPLACE + """ + + @property + def range_test(self) -> global___ModuleConfig.RangeTestConfig: + """ + TODO: REPLACE + """ + + @property + def telemetry(self) -> global___ModuleConfig.TelemetryConfig: + """ + TODO: REPLACE + """ + + @property + def canned_message(self) -> global___ModuleConfig.CannedMessageConfig: + """ + TODO: REPLACE + """ + + @property + def audio(self) -> global___ModuleConfig.AudioConfig: + """ + TODO: REPLACE + """ + + @property + def remote_hardware(self) -> global___ModuleConfig.RemoteHardwareConfig: + """ + TODO: REPLACE + """ + + @property + def neighbor_info(self) -> global___ModuleConfig.NeighborInfoConfig: + """ + TODO: REPLACE + """ + + @property + def ambient_lighting(self) -> global___ModuleConfig.AmbientLightingConfig: + """ + TODO: REPLACE + """ + + @property + def detection_sensor(self) -> global___ModuleConfig.DetectionSensorConfig: + """ + TODO: REPLACE + """ + + @property + def paxcounter(self) -> global___ModuleConfig.PaxcounterConfig: + """ + TODO: REPLACE + """ + + def __init__( + self, + *, + mqtt: global___ModuleConfig.MQTTConfig | None = ..., + serial: global___ModuleConfig.SerialConfig | None = ..., + external_notification: global___ModuleConfig.ExternalNotificationConfig | None = ..., + store_forward: global___ModuleConfig.StoreForwardConfig | None = ..., + range_test: global___ModuleConfig.RangeTestConfig | None = ..., + telemetry: global___ModuleConfig.TelemetryConfig | None = ..., + canned_message: global___ModuleConfig.CannedMessageConfig | None = ..., + audio: global___ModuleConfig.AudioConfig | None = ..., + remote_hardware: global___ModuleConfig.RemoteHardwareConfig | None = ..., + neighbor_info: global___ModuleConfig.NeighborInfoConfig | None = ..., + ambient_lighting: global___ModuleConfig.AmbientLightingConfig | None = ..., + detection_sensor: global___ModuleConfig.DetectionSensorConfig | None = ..., + paxcounter: global___ModuleConfig.PaxcounterConfig | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["ambient_lighting", b"ambient_lighting", "audio", b"audio", "canned_message", b"canned_message", "detection_sensor", b"detection_sensor", "external_notification", b"external_notification", "mqtt", b"mqtt", "neighbor_info", b"neighbor_info", "paxcounter", b"paxcounter", "payload_variant", b"payload_variant", "range_test", b"range_test", "remote_hardware", b"remote_hardware", "serial", b"serial", "store_forward", b"store_forward", "telemetry", b"telemetry"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["ambient_lighting", b"ambient_lighting", "audio", b"audio", "canned_message", b"canned_message", "detection_sensor", b"detection_sensor", "external_notification", b"external_notification", "mqtt", b"mqtt", "neighbor_info", b"neighbor_info", "paxcounter", b"paxcounter", "payload_variant", b"payload_variant", "range_test", b"range_test", "remote_hardware", b"remote_hardware", "serial", b"serial", "store_forward", b"store_forward", "telemetry", b"telemetry"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["payload_variant", b"payload_variant"]) -> typing.Literal["mqtt", "serial", "external_notification", "store_forward", "range_test", "telemetry", "canned_message", "audio", "remote_hardware", "neighbor_info", "ambient_lighting", "detection_sensor", "paxcounter"] | None: ... + +global___ModuleConfig = ModuleConfig + +@typing.final +class RemoteHardwarePin(google.protobuf.message.Message): + """ + A GPIO pin definition for remote hardware module + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + GPIO_PIN_FIELD_NUMBER: builtins.int + NAME_FIELD_NUMBER: builtins.int + TYPE_FIELD_NUMBER: builtins.int + gpio_pin: builtins.int + """ + GPIO Pin number (must match Arduino) + """ + name: builtins.str + """ + Name for the GPIO pin (i.e. Front gate, mailbox, etc) + """ + type: global___RemoteHardwarePinType.ValueType + """ + Type of GPIO access available to consumers on the mesh + """ + def __init__( + self, + *, + gpio_pin: builtins.int = ..., + name: builtins.str = ..., + type: global___RemoteHardwarePinType.ValueType = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["gpio_pin", b"gpio_pin", "name", b"name", "type", b"type"]) -> None: ... + +global___RemoteHardwarePin = RemoteHardwarePin diff --git a/meshtastic/protobuf/mqtt_pb2.py b/meshtastic/protobuf/mqtt_pb2.py new file mode 100644 index 00000000..7b4e4c0f --- /dev/null +++ b/meshtastic/protobuf/mqtt_pb2.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: meshtastic/protobuf/mqtt.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from meshtastic.protobuf import config_pb2 as meshtastic_dot_protobuf_dot_config__pb2 +from meshtastic.protobuf import mesh_pb2 as meshtastic_dot_protobuf_dot_mesh__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1emeshtastic/protobuf/mqtt.proto\x12\x13meshtastic.protobuf\x1a meshtastic/protobuf/config.proto\x1a\x1emeshtastic/protobuf/mesh.proto\"j\n\x0fServiceEnvelope\x12/\n\x06packet\x18\x01 \x01(\x0b\x32\x1f.meshtastic.protobuf.MeshPacket\x12\x12\n\nchannel_id\x18\x02 \x01(\t\x12\x12\n\ngateway_id\x18\x03 \x01(\t\"\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') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.mqtt_pb2', _globals) +if _descriptor._USE_C_DESCRIPTORS == False: + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'\n\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 +# @@protoc_insertion_point(module_scope) diff --git a/meshtastic/protobuf/mqtt_pb2.pyi b/meshtastic/protobuf/mqtt_pb2.pyi new file mode 100644 index 00000000..597fa079 --- /dev/null +++ b/meshtastic/protobuf/mqtt_pb2.pyi @@ -0,0 +1,155 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" + +import builtins +import google.protobuf.descriptor +import google.protobuf.message +import meshtastic.protobuf.config_pb2 +import meshtastic.protobuf.mesh_pb2 +import typing + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing.final +class ServiceEnvelope(google.protobuf.message.Message): + """ + This message wraps a MeshPacket with extra metadata about the sender and how it arrived. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + PACKET_FIELD_NUMBER: builtins.int + CHANNEL_ID_FIELD_NUMBER: builtins.int + GATEWAY_ID_FIELD_NUMBER: builtins.int + channel_id: builtins.str + """ + The global channel ID it was sent on + """ + gateway_id: builtins.str + """ + The sending gateway node ID. Can we use this to authenticate/prevent fake + nodeid impersonation for senders? - i.e. use gateway/mesh id (which is authenticated) + local node id as + the globally trusted nodenum + """ + @property + def packet(self) -> meshtastic.protobuf.mesh_pb2.MeshPacket: + """ + The (probably encrypted) packet + """ + + def __init__( + self, + *, + packet: meshtastic.protobuf.mesh_pb2.MeshPacket | None = ..., + channel_id: builtins.str = ..., + gateway_id: builtins.str = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["packet", b"packet"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["channel_id", b"channel_id", "gateway_id", b"gateway_id", "packet", b"packet"]) -> None: ... + +global___ServiceEnvelope = ServiceEnvelope + +@typing.final +class MapReport(google.protobuf.message.Message): + """ + Information about a node intended to be reported unencrypted to a map using MQTT. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + LONG_NAME_FIELD_NUMBER: builtins.int + SHORT_NAME_FIELD_NUMBER: builtins.int + ROLE_FIELD_NUMBER: builtins.int + HW_MODEL_FIELD_NUMBER: builtins.int + FIRMWARE_VERSION_FIELD_NUMBER: builtins.int + REGION_FIELD_NUMBER: builtins.int + MODEM_PRESET_FIELD_NUMBER: builtins.int + HAS_DEFAULT_CHANNEL_FIELD_NUMBER: builtins.int + LATITUDE_I_FIELD_NUMBER: builtins.int + LONGITUDE_I_FIELD_NUMBER: builtins.int + ALTITUDE_FIELD_NUMBER: builtins.int + POSITION_PRECISION_FIELD_NUMBER: builtins.int + NUM_ONLINE_LOCAL_NODES_FIELD_NUMBER: builtins.int + HAS_OPTED_REPORT_LOCATION_FIELD_NUMBER: builtins.int + long_name: builtins.str + """ + A full name for this user, i.e. "Kevin Hester" + """ + short_name: builtins.str + """ + A VERY short name, ideally two characters. + Suitable for a tiny OLED screen + """ + role: meshtastic.protobuf.config_pb2.Config.DeviceConfig.Role.ValueType + """ + Role of the node that applies specific settings for a particular use-case + """ + hw_model: meshtastic.protobuf.mesh_pb2.HardwareModel.ValueType + """ + Hardware model of the node, i.e. T-Beam, Heltec V3, etc... + """ + firmware_version: builtins.str + """ + Device firmware version string + """ + region: meshtastic.protobuf.config_pb2.Config.LoRaConfig.RegionCode.ValueType + """ + The region code for the radio (US, CN, EU433, etc...) + """ + modem_preset: meshtastic.protobuf.config_pb2.Config.LoRaConfig.ModemPreset.ValueType + """ + Modem preset used by the radio (LongFast, MediumSlow, etc...) + """ + has_default_channel: builtins.bool + """ + Whether the node has a channel with default PSK and name (LongFast, MediumSlow, etc...) + and it uses the default frequency slot given the region and modem preset. + """ + latitude_i: builtins.int + """ + Latitude: multiply by 1e-7 to get degrees in floating point + """ + longitude_i: builtins.int + """ + Longitude: multiply by 1e-7 to get degrees in floating point + """ + altitude: builtins.int + """ + Altitude in meters above MSL + """ + position_precision: builtins.int + """ + Indicates the bits of precision for latitude and longitude set by the sending node + """ + num_online_local_nodes: builtins.int + """ + Number of online nodes (heard in the last 2 hours) this node has in its list that were received locally (not via MQTT) + """ + has_opted_report_location: builtins.bool + """ + User has opted in to share their location (map report) with the mqtt server + Controlled by map_report.should_report_location + """ + def __init__( + self, + *, + long_name: builtins.str = ..., + short_name: builtins.str = ..., + role: meshtastic.protobuf.config_pb2.Config.DeviceConfig.Role.ValueType = ..., + hw_model: meshtastic.protobuf.mesh_pb2.HardwareModel.ValueType = ..., + firmware_version: builtins.str = ..., + region: meshtastic.protobuf.config_pb2.Config.LoRaConfig.RegionCode.ValueType = ..., + modem_preset: meshtastic.protobuf.config_pb2.Config.LoRaConfig.ModemPreset.ValueType = ..., + has_default_channel: builtins.bool = ..., + latitude_i: builtins.int = ..., + longitude_i: builtins.int = ..., + altitude: builtins.int = ..., + position_precision: builtins.int = ..., + num_online_local_nodes: builtins.int = ..., + has_opted_report_location: builtins.bool = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["altitude", b"altitude", "firmware_version", b"firmware_version", "has_default_channel", b"has_default_channel", "has_opted_report_location", b"has_opted_report_location", "hw_model", b"hw_model", "latitude_i", b"latitude_i", "long_name", b"long_name", "longitude_i", b"longitude_i", "modem_preset", b"modem_preset", "num_online_local_nodes", b"num_online_local_nodes", "position_precision", b"position_precision", "region", b"region", "role", b"role", "short_name", b"short_name"]) -> None: ... + +global___MapReport = MapReport diff --git a/meshtastic/protobuf/nanopb_pb2.py b/meshtastic/protobuf/nanopb_pb2.py new file mode 100644 index 00000000..797b8a07 --- /dev/null +++ b/meshtastic/protobuf/nanopb_pb2.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: meshtastic/protobuf/nanopb.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.protobuf import descriptor_pb2 as google_dot_protobuf_dot_descriptor__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n meshtastic/protobuf/nanopb.proto\x1a google/protobuf/descriptor.proto\"\xa4\x07\n\rNanoPBOptions\x12\x10\n\x08max_size\x18\x01 \x01(\x05\x12\x12\n\nmax_length\x18\x0e \x01(\x05\x12\x11\n\tmax_count\x18\x02 \x01(\x05\x12&\n\x08int_size\x18\x07 \x01(\x0e\x32\x08.IntSize:\nIS_DEFAULT\x12$\n\x04type\x18\x03 \x01(\x0e\x32\n.FieldType:\nFT_DEFAULT\x12\x18\n\nlong_names\x18\x04 \x01(\x08:\x04true\x12\x1c\n\rpacked_struct\x18\x05 \x01(\x08:\x05\x66\x61lse\x12\x1a\n\x0bpacked_enum\x18\n \x01(\x08:\x05\x66\x61lse\x12\x1b\n\x0cskip_message\x18\x06 \x01(\x08:\x05\x66\x61lse\x12\x18\n\tno_unions\x18\x08 \x01(\x08:\x05\x66\x61lse\x12\r\n\x05msgid\x18\t \x01(\r\x12\x1e\n\x0f\x61nonymous_oneof\x18\x0b \x01(\x08:\x05\x66\x61lse\x12\x15\n\x06proto3\x18\x0c \x01(\x08:\x05\x66\x61lse\x12#\n\x14proto3_singular_msgs\x18\x15 \x01(\x08:\x05\x66\x61lse\x12\x1d\n\x0e\x65num_to_string\x18\r \x01(\x08:\x05\x66\x61lse\x12\x1b\n\x0c\x66ixed_length\x18\x0f \x01(\x08:\x05\x66\x61lse\x12\x1a\n\x0b\x66ixed_count\x18\x10 \x01(\x08:\x05\x66\x61lse\x12\x1e\n\x0fsubmsg_callback\x18\x16 \x01(\x08:\x05\x66\x61lse\x12/\n\x0cmangle_names\x18\x11 \x01(\x0e\x32\x11.TypenameMangling:\x06M_NONE\x12(\n\x11\x63\x61llback_datatype\x18\x12 \x01(\t:\rpb_callback_t\x12\x34\n\x11\x63\x61llback_function\x18\x13 \x01(\t:\x19pb_default_field_callback\x12\x30\n\x0e\x64\x65scriptorsize\x18\x14 \x01(\x0e\x32\x0f.DescriptorSize:\x07\x44S_AUTO\x12\x1a\n\x0b\x64\x65\x66\x61ult_has\x18\x17 \x01(\x08:\x05\x66\x61lse\x12\x0f\n\x07include\x18\x18 \x03(\t\x12\x0f\n\x07\x65xclude\x18\x1a \x03(\t\x12\x0f\n\x07package\x18\x19 \x01(\t\x12\x41\n\rtype_override\x18\x1b \x01(\x0e\x32*.google.protobuf.FieldDescriptorProto.Type\x12\x19\n\x0bsort_by_tag\x18\x1c \x01(\x08:\x04true\x12.\n\rfallback_type\x18\x1d \x01(\x0e\x32\n.FieldType:\x0b\x46T_CALLBACK*i\n\tFieldType\x12\x0e\n\nFT_DEFAULT\x10\x00\x12\x0f\n\x0b\x46T_CALLBACK\x10\x01\x12\x0e\n\nFT_POINTER\x10\x04\x12\r\n\tFT_STATIC\x10\x02\x12\r\n\tFT_IGNORE\x10\x03\x12\r\n\tFT_INLINE\x10\x05*D\n\x07IntSize\x12\x0e\n\nIS_DEFAULT\x10\x00\x12\x08\n\x04IS_8\x10\x08\x12\t\n\x05IS_16\x10\x10\x12\t\n\x05IS_32\x10 \x12\t\n\x05IS_64\x10@*Z\n\x10TypenameMangling\x12\n\n\x06M_NONE\x10\x00\x12\x13\n\x0fM_STRIP_PACKAGE\x10\x01\x12\r\n\tM_FLATTEN\x10\x02\x12\x16\n\x12M_PACKAGE_INITIALS\x10\x03*E\n\x0e\x44\x65scriptorSize\x12\x0b\n\x07\x44S_AUTO\x10\x00\x12\x08\n\x04\x44S_1\x10\x01\x12\x08\n\x04\x44S_2\x10\x02\x12\x08\n\x04\x44S_4\x10\x04\x12\x08\n\x04\x44S_8\x10\x08:E\n\x0enanopb_fileopt\x12\x1c.google.protobuf.FileOptions\x18\xf2\x07 \x01(\x0b\x32\x0e.NanoPBOptions:G\n\rnanopb_msgopt\x12\x1f.google.protobuf.MessageOptions\x18\xf2\x07 \x01(\x0b\x32\x0e.NanoPBOptions:E\n\x0enanopb_enumopt\x12\x1c.google.protobuf.EnumOptions\x18\xf2\x07 \x01(\x0b\x32\x0e.NanoPBOptions:>\n\x06nanopb\x12\x1d.google.protobuf.FieldOptions\x18\xf2\x07 \x01(\x0b\x32\x0e.NanoPBOptionsB>\n\x18\x66i.kapsi.koti.jpa.nanopbZ\"github.com/meshtastic/go/generated') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.nanopb_pb2', _globals) +if _descriptor._USE_C_DESCRIPTORS == False: + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'\n\030fi.kapsi.koti.jpa.nanopbZ\"github.com/meshtastic/go/generated' + _globals['_FIELDTYPE']._serialized_start=1005 + _globals['_FIELDTYPE']._serialized_end=1110 + _globals['_INTSIZE']._serialized_start=1112 + _globals['_INTSIZE']._serialized_end=1180 + _globals['_TYPENAMEMANGLING']._serialized_start=1182 + _globals['_TYPENAMEMANGLING']._serialized_end=1272 + _globals['_DESCRIPTORSIZE']._serialized_start=1274 + _globals['_DESCRIPTORSIZE']._serialized_end=1343 + _globals['_NANOPBOPTIONS']._serialized_start=71 + _globals['_NANOPBOPTIONS']._serialized_end=1003 +# @@protoc_insertion_point(module_scope) diff --git a/meshtastic/protobuf/nanopb_pb2.pyi b/meshtastic/protobuf/nanopb_pb2.pyi new file mode 100644 index 00000000..c5d7d87a --- /dev/null +++ b/meshtastic/protobuf/nanopb_pb2.pyi @@ -0,0 +1,324 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +Custom options for defining: +- Maximum size of string/bytes +- Maximum number of elements in array + +These are used by nanopb to generate statically allocable structures +for memory-limited environments. +""" + +import builtins +import collections.abc +import google.protobuf.descriptor +import google.protobuf.descriptor_pb2 +import google.protobuf.internal.containers +import google.protobuf.internal.enum_type_wrapper +import google.protobuf.internal.extension_dict +import google.protobuf.message +import sys +import typing + +if sys.version_info >= (3, 10): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +class _FieldType: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _FieldTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_FieldType.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + FT_DEFAULT: _FieldType.ValueType # 0 + """Automatically decide field type, generate static field if possible.""" + FT_CALLBACK: _FieldType.ValueType # 1 + """Always generate a callback field.""" + FT_POINTER: _FieldType.ValueType # 4 + """Always generate a dynamically allocated field.""" + FT_STATIC: _FieldType.ValueType # 2 + """Generate a static field or raise an exception if not possible.""" + FT_IGNORE: _FieldType.ValueType # 3 + """Ignore the field completely.""" + FT_INLINE: _FieldType.ValueType # 5 + """Legacy option, use the separate 'fixed_length' option instead""" + +class FieldType(_FieldType, metaclass=_FieldTypeEnumTypeWrapper): ... + +FT_DEFAULT: FieldType.ValueType # 0 +"""Automatically decide field type, generate static field if possible.""" +FT_CALLBACK: FieldType.ValueType # 1 +"""Always generate a callback field.""" +FT_POINTER: FieldType.ValueType # 4 +"""Always generate a dynamically allocated field.""" +FT_STATIC: FieldType.ValueType # 2 +"""Generate a static field or raise an exception if not possible.""" +FT_IGNORE: FieldType.ValueType # 3 +"""Ignore the field completely.""" +FT_INLINE: FieldType.ValueType # 5 +"""Legacy option, use the separate 'fixed_length' option instead""" +global___FieldType = FieldType + +class _IntSize: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _IntSizeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_IntSize.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + IS_DEFAULT: _IntSize.ValueType # 0 + """Default, 32/64bit based on type in .proto""" + IS_8: _IntSize.ValueType # 8 + IS_16: _IntSize.ValueType # 16 + IS_32: _IntSize.ValueType # 32 + IS_64: _IntSize.ValueType # 64 + +class IntSize(_IntSize, metaclass=_IntSizeEnumTypeWrapper): ... + +IS_DEFAULT: IntSize.ValueType # 0 +"""Default, 32/64bit based on type in .proto""" +IS_8: IntSize.ValueType # 8 +IS_16: IntSize.ValueType # 16 +IS_32: IntSize.ValueType # 32 +IS_64: IntSize.ValueType # 64 +global___IntSize = IntSize + +class _TypenameMangling: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _TypenameManglingEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_TypenameMangling.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + M_NONE: _TypenameMangling.ValueType # 0 + """Default, no typename mangling""" + M_STRIP_PACKAGE: _TypenameMangling.ValueType # 1 + """Strip current package name""" + M_FLATTEN: _TypenameMangling.ValueType # 2 + """Only use last path component""" + M_PACKAGE_INITIALS: _TypenameMangling.ValueType # 3 + """Replace the package name by the initials""" + +class TypenameMangling(_TypenameMangling, metaclass=_TypenameManglingEnumTypeWrapper): ... + +M_NONE: TypenameMangling.ValueType # 0 +"""Default, no typename mangling""" +M_STRIP_PACKAGE: TypenameMangling.ValueType # 1 +"""Strip current package name""" +M_FLATTEN: TypenameMangling.ValueType # 2 +"""Only use last path component""" +M_PACKAGE_INITIALS: TypenameMangling.ValueType # 3 +"""Replace the package name by the initials""" +global___TypenameMangling = TypenameMangling + +class _DescriptorSize: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _DescriptorSizeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_DescriptorSize.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + DS_AUTO: _DescriptorSize.ValueType # 0 + """Select minimal size based on field type""" + DS_1: _DescriptorSize.ValueType # 1 + """1 word; up to 15 byte fields, no arrays""" + DS_2: _DescriptorSize.ValueType # 2 + """2 words; up to 4095 byte fields, 4095 entry arrays""" + DS_4: _DescriptorSize.ValueType # 4 + """4 words; up to 2^32-1 byte fields, 2^16-1 entry arrays""" + DS_8: _DescriptorSize.ValueType # 8 + """8 words; up to 2^32-1 entry arrays""" + +class DescriptorSize(_DescriptorSize, metaclass=_DescriptorSizeEnumTypeWrapper): ... + +DS_AUTO: DescriptorSize.ValueType # 0 +"""Select minimal size based on field type""" +DS_1: DescriptorSize.ValueType # 1 +"""1 word; up to 15 byte fields, no arrays""" +DS_2: DescriptorSize.ValueType # 2 +"""2 words; up to 4095 byte fields, 4095 entry arrays""" +DS_4: DescriptorSize.ValueType # 4 +"""4 words; up to 2^32-1 byte fields, 2^16-1 entry arrays""" +DS_8: DescriptorSize.ValueType # 8 +"""8 words; up to 2^32-1 entry arrays""" +global___DescriptorSize = DescriptorSize + +@typing.final +class NanoPBOptions(google.protobuf.message.Message): + """This is the inner options message, which basically defines options for + a field. When it is used in message or file scope, it applies to all + fields. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + MAX_SIZE_FIELD_NUMBER: builtins.int + MAX_LENGTH_FIELD_NUMBER: builtins.int + MAX_COUNT_FIELD_NUMBER: builtins.int + INT_SIZE_FIELD_NUMBER: builtins.int + TYPE_FIELD_NUMBER: builtins.int + LONG_NAMES_FIELD_NUMBER: builtins.int + PACKED_STRUCT_FIELD_NUMBER: builtins.int + PACKED_ENUM_FIELD_NUMBER: builtins.int + SKIP_MESSAGE_FIELD_NUMBER: builtins.int + NO_UNIONS_FIELD_NUMBER: builtins.int + MSGID_FIELD_NUMBER: builtins.int + ANONYMOUS_ONEOF_FIELD_NUMBER: builtins.int + PROTO3_FIELD_NUMBER: builtins.int + PROTO3_SINGULAR_MSGS_FIELD_NUMBER: builtins.int + ENUM_TO_STRING_FIELD_NUMBER: builtins.int + FIXED_LENGTH_FIELD_NUMBER: builtins.int + FIXED_COUNT_FIELD_NUMBER: builtins.int + SUBMSG_CALLBACK_FIELD_NUMBER: builtins.int + MANGLE_NAMES_FIELD_NUMBER: builtins.int + CALLBACK_DATATYPE_FIELD_NUMBER: builtins.int + CALLBACK_FUNCTION_FIELD_NUMBER: builtins.int + DESCRIPTORSIZE_FIELD_NUMBER: builtins.int + DEFAULT_HAS_FIELD_NUMBER: builtins.int + INCLUDE_FIELD_NUMBER: builtins.int + EXCLUDE_FIELD_NUMBER: builtins.int + PACKAGE_FIELD_NUMBER: builtins.int + TYPE_OVERRIDE_FIELD_NUMBER: builtins.int + SORT_BY_TAG_FIELD_NUMBER: builtins.int + FALLBACK_TYPE_FIELD_NUMBER: builtins.int + max_size: builtins.int + """Allocated size for 'bytes' and 'string' fields. + For string fields, this should include the space for null terminator. + """ + max_length: builtins.int + """Maximum length for 'string' fields. Setting this is equivalent + to setting max_size to a value of length+1. + """ + max_count: builtins.int + """Allocated number of entries in arrays ('repeated' fields)""" + int_size: global___IntSize.ValueType + """Size of integer fields. Can save some memory if you don't need + full 32 bits for the value. + """ + type: global___FieldType.ValueType + """Force type of field (callback or static allocation)""" + long_names: builtins.bool + """Use long names for enums, i.e. EnumName_EnumValue.""" + packed_struct: builtins.bool + """Add 'packed' attribute to generated structs. + Note: this cannot be used on CPUs that break on unaligned + accesses to variables. + """ + packed_enum: builtins.bool + """Add 'packed' attribute to generated enums.""" + skip_message: builtins.bool + """Skip this message""" + no_unions: builtins.bool + """Generate oneof fields as normal optional fields instead of union.""" + msgid: builtins.int + """integer type tag for a message""" + anonymous_oneof: builtins.bool + """decode oneof as anonymous union""" + proto3: builtins.bool + """Proto3 singular field does not generate a "has_" flag""" + proto3_singular_msgs: builtins.bool + """Force proto3 messages to have no "has_" flag. + This was default behavior until nanopb-0.4.0. + """ + enum_to_string: builtins.bool + """Generate an enum->string mapping function (can take up lots of space).""" + fixed_length: builtins.bool + """Generate bytes arrays with fixed length""" + fixed_count: builtins.bool + """Generate repeated field with fixed count""" + submsg_callback: builtins.bool + """Generate message-level callback that is called before decoding submessages. + This can be used to set callback fields for submsgs inside oneofs. + """ + mangle_names: global___TypenameMangling.ValueType + """Shorten or remove package names from type names. + This option applies only on the file level. + """ + callback_datatype: builtins.str + """Data type for storage associated with callback fields.""" + callback_function: builtins.str + """Callback function used for encoding and decoding. + Prior to nanopb-0.4.0, the callback was specified in per-field pb_callback_t + structure. This is still supported, but does not work inside e.g. oneof or pointer + fields. Instead, a new method allows specifying a per-message callback that + will be called for all callback fields in a message type. + """ + descriptorsize: global___DescriptorSize.ValueType + """Select the size of field descriptors. This option has to be defined + for the whole message, not per-field. Usually automatic selection is + ok, but if it results in compilation errors you can increase the field + size here. + """ + default_has: builtins.bool + """Set default value for has_ fields.""" + package: builtins.str + """Package name that applies only for nanopb.""" + type_override: google.protobuf.descriptor_pb2.FieldDescriptorProto.Type.ValueType + """Override type of the field in generated C code. Only to be used with related field types""" + sort_by_tag: builtins.bool + """Due to historical reasons, nanopb orders fields in structs by their tag number + instead of the order in .proto. Set this to false to keep the .proto order. + The default value will probably change to false in nanopb-0.5.0. + """ + fallback_type: global___FieldType.ValueType + """Set the FT_DEFAULT field conversion strategy. + A field that can become a static member of a c struct (e.g. int, bool, etc) + will be a a static field. + Fields with dynamic length are converted to either a pointer or a callback. + """ + @property + def include(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + """Extra files to include in generated `.pb.h`""" + + @property + def exclude(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + """Automatic includes to exclude from generated `.pb.h` + Same as nanopb_generator.py command line flag -x. + """ + + def __init__( + self, + *, + max_size: builtins.int | None = ..., + max_length: builtins.int | None = ..., + max_count: builtins.int | None = ..., + int_size: global___IntSize.ValueType | None = ..., + type: global___FieldType.ValueType | None = ..., + long_names: builtins.bool | None = ..., + packed_struct: builtins.bool | None = ..., + packed_enum: builtins.bool | None = ..., + skip_message: builtins.bool | None = ..., + no_unions: builtins.bool | None = ..., + msgid: builtins.int | None = ..., + anonymous_oneof: builtins.bool | None = ..., + proto3: builtins.bool | None = ..., + proto3_singular_msgs: builtins.bool | None = ..., + enum_to_string: builtins.bool | None = ..., + fixed_length: builtins.bool | None = ..., + fixed_count: builtins.bool | None = ..., + submsg_callback: builtins.bool | None = ..., + mangle_names: global___TypenameMangling.ValueType | None = ..., + callback_datatype: builtins.str | None = ..., + callback_function: builtins.str | None = ..., + descriptorsize: global___DescriptorSize.ValueType | None = ..., + default_has: builtins.bool | None = ..., + include: collections.abc.Iterable[builtins.str] | None = ..., + exclude: collections.abc.Iterable[builtins.str] | None = ..., + package: builtins.str | None = ..., + type_override: google.protobuf.descriptor_pb2.FieldDescriptorProto.Type.ValueType | None = ..., + sort_by_tag: builtins.bool | None = ..., + fallback_type: global___FieldType.ValueType | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["anonymous_oneof", b"anonymous_oneof", "callback_datatype", b"callback_datatype", "callback_function", b"callback_function", "default_has", b"default_has", "descriptorsize", b"descriptorsize", "enum_to_string", b"enum_to_string", "fallback_type", b"fallback_type", "fixed_count", b"fixed_count", "fixed_length", b"fixed_length", "int_size", b"int_size", "long_names", b"long_names", "mangle_names", b"mangle_names", "max_count", b"max_count", "max_length", b"max_length", "max_size", b"max_size", "msgid", b"msgid", "no_unions", b"no_unions", "package", b"package", "packed_enum", b"packed_enum", "packed_struct", b"packed_struct", "proto3", b"proto3", "proto3_singular_msgs", b"proto3_singular_msgs", "skip_message", b"skip_message", "sort_by_tag", b"sort_by_tag", "submsg_callback", b"submsg_callback", "type", b"type", "type_override", b"type_override"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["anonymous_oneof", b"anonymous_oneof", "callback_datatype", b"callback_datatype", "callback_function", b"callback_function", "default_has", b"default_has", "descriptorsize", b"descriptorsize", "enum_to_string", b"enum_to_string", "exclude", b"exclude", "fallback_type", b"fallback_type", "fixed_count", b"fixed_count", "fixed_length", b"fixed_length", "include", b"include", "int_size", b"int_size", "long_names", b"long_names", "mangle_names", b"mangle_names", "max_count", b"max_count", "max_length", b"max_length", "max_size", b"max_size", "msgid", b"msgid", "no_unions", b"no_unions", "package", b"package", "packed_enum", b"packed_enum", "packed_struct", b"packed_struct", "proto3", b"proto3", "proto3_singular_msgs", b"proto3_singular_msgs", "skip_message", b"skip_message", "sort_by_tag", b"sort_by_tag", "submsg_callback", b"submsg_callback", "type", b"type", "type_override", b"type_override"]) -> None: ... + +global___NanoPBOptions = NanoPBOptions + +NANOPB_FILEOPT_FIELD_NUMBER: builtins.int +NANOPB_MSGOPT_FIELD_NUMBER: builtins.int +NANOPB_ENUMOPT_FIELD_NUMBER: builtins.int +NANOPB_FIELD_NUMBER: builtins.int +nanopb_fileopt: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[google.protobuf.descriptor_pb2.FileOptions, global___NanoPBOptions] +nanopb_msgopt: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[google.protobuf.descriptor_pb2.MessageOptions, global___NanoPBOptions] +nanopb_enumopt: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[google.protobuf.descriptor_pb2.EnumOptions, global___NanoPBOptions] +nanopb: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[google.protobuf.descriptor_pb2.FieldOptions, global___NanoPBOptions] diff --git a/meshtastic/protobuf/paxcount_pb2.py b/meshtastic/protobuf/paxcount_pb2.py new file mode 100644 index 00000000..f2be49e7 --- /dev/null +++ b/meshtastic/protobuf/paxcount_pb2.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: meshtastic/protobuf/paxcount.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\"meshtastic/protobuf/paxcount.proto\x12\x13meshtastic.protobuf\"5\n\x08Paxcount\x12\x0c\n\x04wifi\x18\x01 \x01(\r\x12\x0b\n\x03\x62le\x18\x02 \x01(\r\x12\x0e\n\x06uptime\x18\x03 \x01(\rBd\n\x14org.meshtastic.protoB\x0ePaxcountProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.paxcount_pb2', _globals) +if _descriptor._USE_C_DESCRIPTORS == False: + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\016PaxcountProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' + _globals['_PAXCOUNT']._serialized_start=59 + _globals['_PAXCOUNT']._serialized_end=112 +# @@protoc_insertion_point(module_scope) diff --git a/meshtastic/protobuf/paxcount_pb2.pyi b/meshtastic/protobuf/paxcount_pb2.pyi new file mode 100644 index 00000000..cc7ed0d4 --- /dev/null +++ b/meshtastic/protobuf/paxcount_pb2.pyi @@ -0,0 +1,45 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" + +import builtins +import google.protobuf.descriptor +import google.protobuf.message +import typing + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing.final +class Paxcount(google.protobuf.message.Message): + """ + TODO: REPLACE + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + WIFI_FIELD_NUMBER: builtins.int + BLE_FIELD_NUMBER: builtins.int + UPTIME_FIELD_NUMBER: builtins.int + wifi: builtins.int + """ + seen Wifi devices + """ + ble: builtins.int + """ + Seen BLE devices + """ + uptime: builtins.int + """ + Uptime in seconds + """ + def __init__( + self, + *, + wifi: builtins.int = ..., + ble: builtins.int = ..., + uptime: builtins.int = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["ble", b"ble", "uptime", b"uptime", "wifi", b"wifi"]) -> None: ... + +global___Paxcount = Paxcount diff --git a/meshtastic/protobuf/portnums_pb2.py b/meshtastic/protobuf/portnums_pb2.py new file mode 100644 index 00000000..5364c583 --- /dev/null +++ b/meshtastic/protobuf/portnums_pb2.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: meshtastic/protobuf/portnums.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\"meshtastic/protobuf/portnums.proto\x12\x13meshtastic.protobuf*\xf6\x04\n\x07PortNum\x12\x0f\n\x0bUNKNOWN_APP\x10\x00\x12\x14\n\x10TEXT_MESSAGE_APP\x10\x01\x12\x17\n\x13REMOTE_HARDWARE_APP\x10\x02\x12\x10\n\x0cPOSITION_APP\x10\x03\x12\x10\n\x0cNODEINFO_APP\x10\x04\x12\x0f\n\x0bROUTING_APP\x10\x05\x12\r\n\tADMIN_APP\x10\x06\x12\x1f\n\x1bTEXT_MESSAGE_COMPRESSED_APP\x10\x07\x12\x10\n\x0cWAYPOINT_APP\x10\x08\x12\r\n\tAUDIO_APP\x10\t\x12\x18\n\x14\x44\x45TECTION_SENSOR_APP\x10\n\x12\r\n\tALERT_APP\x10\x0b\x12\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\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') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.portnums_pb2', _globals) +if _descriptor._USE_C_DESCRIPTORS == False: + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'\n\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=690 +# @@protoc_insertion_point(module_scope) diff --git a/meshtastic/protobuf/portnums_pb2.pyi b/meshtastic/protobuf/portnums_pb2.pyi new file mode 100644 index 00000000..c5b626f4 --- /dev/null +++ b/meshtastic/protobuf/portnums_pb2.pyi @@ -0,0 +1,416 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" + +import builtins +import google.protobuf.descriptor +import google.protobuf.internal.enum_type_wrapper +import sys +import typing + +if sys.version_info >= (3, 10): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +class _PortNum: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _PortNumEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_PortNum.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + UNKNOWN_APP: _PortNum.ValueType # 0 + """ + Deprecated: do not use in new code (formerly called OPAQUE) + A message sent from a device outside of the mesh, in a form the mesh does not understand + NOTE: This must be 0, because it is documented in IMeshService.aidl to be so + ENCODING: binary undefined + """ + TEXT_MESSAGE_APP: _PortNum.ValueType # 1 + """ + A simple UTF-8 text message, which even the little micros in the mesh + can understand and show on their screen eventually in some circumstances + even signal might send messages in this form (see below) + ENCODING: UTF-8 Plaintext (?) + """ + REMOTE_HARDWARE_APP: _PortNum.ValueType # 2 + """ + Reserved for built-in GPIO/example app. + See remote_hardware.proto/HardwareMessage for details on the message sent/received to this port number + ENCODING: Protobuf + """ + POSITION_APP: _PortNum.ValueType # 3 + """ + The built-in position messaging app. + Payload is a Position message. + ENCODING: Protobuf + """ + NODEINFO_APP: _PortNum.ValueType # 4 + """ + The built-in user info app. + Payload is a User message. + ENCODING: Protobuf + """ + ROUTING_APP: _PortNum.ValueType # 5 + """ + Protocol control packets for mesh protocol use. + Payload is a Routing message. + ENCODING: Protobuf + """ + ADMIN_APP: _PortNum.ValueType # 6 + """ + Admin control packets. + Payload is a AdminMessage message. + ENCODING: Protobuf + """ + TEXT_MESSAGE_COMPRESSED_APP: _PortNum.ValueType # 7 + """ + Compressed TEXT_MESSAGE payloads. + ENCODING: UTF-8 Plaintext (?) with Unishox2 Compression + NOTE: The Device Firmware converts a TEXT_MESSAGE_APP to TEXT_MESSAGE_COMPRESSED_APP if the compressed + payload is shorter. There's no need for app developers to do this themselves. Also the firmware will decompress + any incoming TEXT_MESSAGE_COMPRESSED_APP payload and convert to TEXT_MESSAGE_APP. + """ + WAYPOINT_APP: _PortNum.ValueType # 8 + """ + Waypoint payloads. + Payload is a Waypoint message. + ENCODING: Protobuf + """ + AUDIO_APP: _PortNum.ValueType # 9 + """ + Audio Payloads. + Encapsulated codec2 packets. On 2.4 GHZ Bandwidths only for now + ENCODING: codec2 audio frames + NOTE: audio frames contain a 3 byte header (0xc0 0xde 0xc2) and a one byte marker for the decompressed bitrate. + This marker comes from the 'moduleConfig.audio.bitrate' enum minus one. + """ + DETECTION_SENSOR_APP: _PortNum.ValueType # 10 + """ + Same as Text Message but originating from Detection Sensor Module. + NOTE: This portnum traffic is not sent to the public MQTT starting at firmware version 2.2.9 + """ + ALERT_APP: _PortNum.ValueType # 11 + """ + Same as Text Message but used for critical alerts. + """ + KEY_VERIFICATION_APP: _PortNum.ValueType # 12 + """ + Module/port for handling key verification requests. + """ + REPLY_APP: _PortNum.ValueType # 32 + """ + Provides a 'ping' service that replies to any packet it receives. + Also serves as a small example module. + ENCODING: ASCII Plaintext + """ + IP_TUNNEL_APP: _PortNum.ValueType # 33 + """ + Used for the python IP tunnel feature + ENCODING: IP Packet. Handled by the python API, firmware ignores this one and pases on. + """ + PAXCOUNTER_APP: _PortNum.ValueType # 34 + """ + Paxcounter lib included in the firmware + ENCODING: protobuf + """ + SERIAL_APP: _PortNum.ValueType # 64 + """ + Provides a hardware serial interface to send and receive from the Meshtastic network. + Connect to the RX/TX pins of a device with 38400 8N1. Packets received from the Meshtastic + network is forwarded to the RX pin while sending a packet to TX will go out to the Mesh network. + Maximum packet size of 240 bytes. + Module is disabled by default can be turned on by setting SERIAL_MODULE_ENABLED = 1 in SerialPlugh.cpp. + ENCODING: binary undefined + """ + STORE_FORWARD_APP: _PortNum.ValueType # 65 + """ + STORE_FORWARD_APP (Work in Progress) + Maintained by Jm Casler (MC Hamster) : jm@casler.org + ENCODING: Protobuf + """ + RANGE_TEST_APP: _PortNum.ValueType # 66 + """ + Optional port for messages for the range test module. + ENCODING: ASCII Plaintext + NOTE: This portnum traffic is not sent to the public MQTT starting at firmware version 2.2.9 + """ + TELEMETRY_APP: _PortNum.ValueType # 67 + """ + Provides a format to send and receive telemetry data from the Meshtastic network. + Maintained by Charles Crossan (crossan007) : crossan007@gmail.com + ENCODING: Protobuf + """ + ZPS_APP: _PortNum.ValueType # 68 + """ + Experimental tools for estimating node position without a GPS + Maintained by Github user a-f-G-U-C (a Meshtastic contributor) + Project files at https://github.com/a-f-G-U-C/Meshtastic-ZPS + ENCODING: arrays of int64 fields + """ + SIMULATOR_APP: _PortNum.ValueType # 69 + """ + Used to let multiple instances of Linux native applications communicate + as if they did using their LoRa chip. + Maintained by GitHub user GUVWAF. + Project files at https://github.com/GUVWAF/Meshtasticator + ENCODING: Protobuf (?) + """ + TRACEROUTE_APP: _PortNum.ValueType # 70 + """ + Provides a traceroute functionality to show the route a packet towards + a certain destination would take on the mesh. Contains a RouteDiscovery message as payload. + ENCODING: Protobuf + """ + NEIGHBORINFO_APP: _PortNum.ValueType # 71 + """ + Aggregates edge info for the network by sending out a list of each node's neighbors + ENCODING: Protobuf + """ + ATAK_PLUGIN: _PortNum.ValueType # 72 + """ + ATAK Plugin + Portnum for payloads from the official Meshtastic ATAK plugin + """ + MAP_REPORT_APP: _PortNum.ValueType # 73 + """ + Provides unencrypted information about a node for consumption by a map via MQTT + """ + POWERSTRESS_APP: _PortNum.ValueType # 74 + """ + PowerStress based monitoring support (for automated power consumption testing) + """ + RETICULUM_TUNNEL_APP: _PortNum.ValueType # 76 + """ + Reticulum Network Stack Tunnel App + ENCODING: Fragmented RNS Packet. Handled by Meshtastic RNS interface + """ + CAYENNE_APP: _PortNum.ValueType # 77 + """ + App for transporting Cayenne Low Power Payload, popular for LoRaWAN sensor nodes. Offers ability to send + arbitrary telemetry over meshtastic that is not covered by telemetry.proto + ENCODING: CayenneLLP + """ + PRIVATE_APP: _PortNum.ValueType # 256 + """ + Private applications should use portnums >= 256. + To simplify initial development and testing you can use "PRIVATE_APP" + in your code without needing to rebuild protobuf files (via [regen-protos.sh](https://github.com/meshtastic/firmware/blob/master/bin/regen-protos.sh)) + """ + ATAK_FORWARDER: _PortNum.ValueType # 257 + """ + ATAK Forwarder Module https://github.com/paulmandal/atak-forwarder + ENCODING: libcotshrink + """ + MAX: _PortNum.ValueType # 511 + """ + Currently we limit port nums to no higher than this value + """ + +class PortNum(_PortNum, metaclass=_PortNumEnumTypeWrapper): + """ + For any new 'apps' that run on the device or via sister apps on phones/PCs they should pick and use a + unique 'portnum' for their application. + If you are making a new app using meshtastic, please send in a pull request to add your 'portnum' to this + master table. + PortNums should be assigned in the following range: + 0-63 Core Meshtastic use, do not use for third party apps + 64-127 Registered 3rd party apps, send in a pull request that adds a new entry to portnums.proto to register your application + 256-511 Use one of these portnums for your private applications that you don't want to register publically + All other values are reserved. + Note: This was formerly a Type enum named 'typ' with the same id # + We have change to this 'portnum' based scheme for specifying app handlers for particular payloads. + This change is backwards compatible by treating the legacy OPAQUE/CLEAR_TEXT values identically. + """ + +UNKNOWN_APP: PortNum.ValueType # 0 +""" +Deprecated: do not use in new code (formerly called OPAQUE) +A message sent from a device outside of the mesh, in a form the mesh does not understand +NOTE: This must be 0, because it is documented in IMeshService.aidl to be so +ENCODING: binary undefined +""" +TEXT_MESSAGE_APP: PortNum.ValueType # 1 +""" +A simple UTF-8 text message, which even the little micros in the mesh +can understand and show on their screen eventually in some circumstances +even signal might send messages in this form (see below) +ENCODING: UTF-8 Plaintext (?) +""" +REMOTE_HARDWARE_APP: PortNum.ValueType # 2 +""" +Reserved for built-in GPIO/example app. +See remote_hardware.proto/HardwareMessage for details on the message sent/received to this port number +ENCODING: Protobuf +""" +POSITION_APP: PortNum.ValueType # 3 +""" +The built-in position messaging app. +Payload is a Position message. +ENCODING: Protobuf +""" +NODEINFO_APP: PortNum.ValueType # 4 +""" +The built-in user info app. +Payload is a User message. +ENCODING: Protobuf +""" +ROUTING_APP: PortNum.ValueType # 5 +""" +Protocol control packets for mesh protocol use. +Payload is a Routing message. +ENCODING: Protobuf +""" +ADMIN_APP: PortNum.ValueType # 6 +""" +Admin control packets. +Payload is a AdminMessage message. +ENCODING: Protobuf +""" +TEXT_MESSAGE_COMPRESSED_APP: PortNum.ValueType # 7 +""" +Compressed TEXT_MESSAGE payloads. +ENCODING: UTF-8 Plaintext (?) with Unishox2 Compression +NOTE: The Device Firmware converts a TEXT_MESSAGE_APP to TEXT_MESSAGE_COMPRESSED_APP if the compressed +payload is shorter. There's no need for app developers to do this themselves. Also the firmware will decompress +any incoming TEXT_MESSAGE_COMPRESSED_APP payload and convert to TEXT_MESSAGE_APP. +""" +WAYPOINT_APP: PortNum.ValueType # 8 +""" +Waypoint payloads. +Payload is a Waypoint message. +ENCODING: Protobuf +""" +AUDIO_APP: PortNum.ValueType # 9 +""" +Audio Payloads. +Encapsulated codec2 packets. On 2.4 GHZ Bandwidths only for now +ENCODING: codec2 audio frames +NOTE: audio frames contain a 3 byte header (0xc0 0xde 0xc2) and a one byte marker for the decompressed bitrate. +This marker comes from the 'moduleConfig.audio.bitrate' enum minus one. +""" +DETECTION_SENSOR_APP: PortNum.ValueType # 10 +""" +Same as Text Message but originating from Detection Sensor Module. +NOTE: This portnum traffic is not sent to the public MQTT starting at firmware version 2.2.9 +""" +ALERT_APP: PortNum.ValueType # 11 +""" +Same as Text Message but used for critical alerts. +""" +KEY_VERIFICATION_APP: PortNum.ValueType # 12 +""" +Module/port for handling key verification requests. +""" +REPLY_APP: PortNum.ValueType # 32 +""" +Provides a 'ping' service that replies to any packet it receives. +Also serves as a small example module. +ENCODING: ASCII Plaintext +""" +IP_TUNNEL_APP: PortNum.ValueType # 33 +""" +Used for the python IP tunnel feature +ENCODING: IP Packet. Handled by the python API, firmware ignores this one and pases on. +""" +PAXCOUNTER_APP: PortNum.ValueType # 34 +""" +Paxcounter lib included in the firmware +ENCODING: protobuf +""" +SERIAL_APP: PortNum.ValueType # 64 +""" +Provides a hardware serial interface to send and receive from the Meshtastic network. +Connect to the RX/TX pins of a device with 38400 8N1. Packets received from the Meshtastic +network is forwarded to the RX pin while sending a packet to TX will go out to the Mesh network. +Maximum packet size of 240 bytes. +Module is disabled by default can be turned on by setting SERIAL_MODULE_ENABLED = 1 in SerialPlugh.cpp. +ENCODING: binary undefined +""" +STORE_FORWARD_APP: PortNum.ValueType # 65 +""" +STORE_FORWARD_APP (Work in Progress) +Maintained by Jm Casler (MC Hamster) : jm@casler.org +ENCODING: Protobuf +""" +RANGE_TEST_APP: PortNum.ValueType # 66 +""" +Optional port for messages for the range test module. +ENCODING: ASCII Plaintext +NOTE: This portnum traffic is not sent to the public MQTT starting at firmware version 2.2.9 +""" +TELEMETRY_APP: PortNum.ValueType # 67 +""" +Provides a format to send and receive telemetry data from the Meshtastic network. +Maintained by Charles Crossan (crossan007) : crossan007@gmail.com +ENCODING: Protobuf +""" +ZPS_APP: PortNum.ValueType # 68 +""" +Experimental tools for estimating node position without a GPS +Maintained by Github user a-f-G-U-C (a Meshtastic contributor) +Project files at https://github.com/a-f-G-U-C/Meshtastic-ZPS +ENCODING: arrays of int64 fields +""" +SIMULATOR_APP: PortNum.ValueType # 69 +""" +Used to let multiple instances of Linux native applications communicate +as if they did using their LoRa chip. +Maintained by GitHub user GUVWAF. +Project files at https://github.com/GUVWAF/Meshtasticator +ENCODING: Protobuf (?) +""" +TRACEROUTE_APP: PortNum.ValueType # 70 +""" +Provides a traceroute functionality to show the route a packet towards +a certain destination would take on the mesh. Contains a RouteDiscovery message as payload. +ENCODING: Protobuf +""" +NEIGHBORINFO_APP: PortNum.ValueType # 71 +""" +Aggregates edge info for the network by sending out a list of each node's neighbors +ENCODING: Protobuf +""" +ATAK_PLUGIN: PortNum.ValueType # 72 +""" +ATAK Plugin +Portnum for payloads from the official Meshtastic ATAK plugin +""" +MAP_REPORT_APP: PortNum.ValueType # 73 +""" +Provides unencrypted information about a node for consumption by a map via MQTT +""" +POWERSTRESS_APP: PortNum.ValueType # 74 +""" +PowerStress based monitoring support (for automated power consumption testing) +""" +RETICULUM_TUNNEL_APP: PortNum.ValueType # 76 +""" +Reticulum Network Stack Tunnel App +ENCODING: Fragmented RNS Packet. Handled by Meshtastic RNS interface +""" +CAYENNE_APP: PortNum.ValueType # 77 +""" +App for transporting Cayenne Low Power Payload, popular for LoRaWAN sensor nodes. Offers ability to send +arbitrary telemetry over meshtastic that is not covered by telemetry.proto +ENCODING: CayenneLLP +""" +PRIVATE_APP: PortNum.ValueType # 256 +""" +Private applications should use portnums >= 256. +To simplify initial development and testing you can use "PRIVATE_APP" +in your code without needing to rebuild protobuf files (via [regen-protos.sh](https://github.com/meshtastic/firmware/blob/master/bin/regen-protos.sh)) +""" +ATAK_FORWARDER: PortNum.ValueType # 257 +""" +ATAK Forwarder Module https://github.com/paulmandal/atak-forwarder +ENCODING: libcotshrink +""" +MAX: PortNum.ValueType # 511 +""" +Currently we limit port nums to no higher than this value +""" +global___PortNum = PortNum diff --git a/meshtastic/protobuf/powermon_pb2.py b/meshtastic/protobuf/powermon_pb2.py new file mode 100644 index 00000000..1895a953 --- /dev/null +++ b/meshtastic/protobuf/powermon_pb2.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: meshtastic/protobuf/powermon.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\"meshtastic/protobuf/powermon.proto\x12\x13meshtastic.protobuf\"\xe0\x01\n\x08PowerMon\"\xd3\x01\n\x05State\x12\x08\n\x04None\x10\x00\x12\x11\n\rCPU_DeepSleep\x10\x01\x12\x12\n\x0e\x43PU_LightSleep\x10\x02\x12\x0c\n\x08Vext1_On\x10\x04\x12\r\n\tLora_RXOn\x10\x08\x12\r\n\tLora_TXOn\x10\x10\x12\x11\n\rLora_RXActive\x10 \x12\t\n\x05\x42T_On\x10@\x12\x0b\n\x06LED_On\x10\x80\x01\x12\x0e\n\tScreen_On\x10\x80\x02\x12\x13\n\x0eScreen_Drawing\x10\x80\x04\x12\x0c\n\x07Wifi_On\x10\x80\x08\x12\x0f\n\nGPS_Active\x10\x80\x10\"\x88\x03\n\x12PowerStressMessage\x12;\n\x03\x63md\x18\x01 \x01(\x0e\x32..meshtastic.protobuf.PowerStressMessage.Opcode\x12\x13\n\x0bnum_seconds\x18\x02 \x01(\x02\"\x9f\x02\n\x06Opcode\x12\t\n\x05UNSET\x10\x00\x12\x0e\n\nPRINT_INFO\x10\x01\x12\x0f\n\x0b\x46ORCE_QUIET\x10\x02\x12\r\n\tEND_QUIET\x10\x03\x12\r\n\tSCREEN_ON\x10\x10\x12\x0e\n\nSCREEN_OFF\x10\x11\x12\x0c\n\x08\x43PU_IDLE\x10 \x12\x11\n\rCPU_DEEPSLEEP\x10!\x12\x0e\n\nCPU_FULLON\x10\"\x12\n\n\x06LED_ON\x10\x30\x12\x0b\n\x07LED_OFF\x10\x31\x12\x0c\n\x08LORA_OFF\x10@\x12\x0b\n\x07LORA_TX\x10\x41\x12\x0b\n\x07LORA_RX\x10\x42\x12\n\n\x06\x42T_OFF\x10P\x12\t\n\x05\x42T_ON\x10Q\x12\x0c\n\x08WIFI_OFF\x10`\x12\x0b\n\x07WIFI_ON\x10\x61\x12\x0b\n\x07GPS_OFF\x10p\x12\n\n\x06GPS_ON\x10qBd\n\x14org.meshtastic.protoB\x0ePowerMonProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.powermon_pb2', _globals) +if _descriptor._USE_C_DESCRIPTORS == False: + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\016PowerMonProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' + _globals['_POWERMON']._serialized_start=60 + _globals['_POWERMON']._serialized_end=284 + _globals['_POWERMON_STATE']._serialized_start=73 + _globals['_POWERMON_STATE']._serialized_end=284 + _globals['_POWERSTRESSMESSAGE']._serialized_start=287 + _globals['_POWERSTRESSMESSAGE']._serialized_end=679 + _globals['_POWERSTRESSMESSAGE_OPCODE']._serialized_start=392 + _globals['_POWERSTRESSMESSAGE_OPCODE']._serialized_end=679 +# @@protoc_insertion_point(module_scope) diff --git a/meshtastic/protobuf/powermon_pb2.pyi b/meshtastic/protobuf/powermon_pb2.pyi new file mode 100644 index 00000000..6c51a281 --- /dev/null +++ b/meshtastic/protobuf/powermon_pb2.pyi @@ -0,0 +1,221 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" + +import builtins +import google.protobuf.descriptor +import google.protobuf.internal.enum_type_wrapper +import google.protobuf.message +import sys +import typing + +if sys.version_info >= (3, 10): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing.final +class PowerMon(google.protobuf.message.Message): + """Note: There are no 'PowerMon' messages normally in use (PowerMons are sent only as structured logs - slogs). + But we wrap our State enum in this message to effectively nest a namespace (without our linter yelling at us) + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + class _State: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + + class _StateEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[PowerMon._State.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + CPU_DeepSleep: PowerMon._State.ValueType # 1 + CPU_LightSleep: PowerMon._State.ValueType # 2 + Vext1_On: PowerMon._State.ValueType # 4 + """ + The external Vext1 power is on. Many boards have auxillary power rails that the CPU turns on only + occasionally. In cases where that rail has multiple devices on it we usually want to have logging on + the state of that rail as an independent record. + For instance on the Heltec Tracker 1.1 board, this rail is the power source for the GPS and screen. + + The log messages will be short and complete (see PowerMon.Event in the protobufs for details). + something like "S:PM:C,0x00001234,REASON" where the hex number is the bitmask of all current states. + (We use a bitmask for states so that if a log message gets lost it won't be fatal) + """ + Lora_RXOn: PowerMon._State.ValueType # 8 + Lora_TXOn: PowerMon._State.ValueType # 16 + Lora_RXActive: PowerMon._State.ValueType # 32 + BT_On: PowerMon._State.ValueType # 64 + LED_On: PowerMon._State.ValueType # 128 + Screen_On: PowerMon._State.ValueType # 256 + Screen_Drawing: PowerMon._State.ValueType # 512 + Wifi_On: PowerMon._State.ValueType # 1024 + GPS_Active: PowerMon._State.ValueType # 2048 + """ + GPS is actively trying to find our location + See GPSPowerState for more details + """ + + class State(_State, metaclass=_StateEnumTypeWrapper): + """Any significant power changing event in meshtastic should be tagged with a powermon state transition. + If you are making new meshtastic features feel free to add new entries at the end of this definition. + """ + + CPU_DeepSleep: PowerMon.State.ValueType # 1 + CPU_LightSleep: PowerMon.State.ValueType # 2 + Vext1_On: PowerMon.State.ValueType # 4 + """ + The external Vext1 power is on. Many boards have auxillary power rails that the CPU turns on only + occasionally. In cases where that rail has multiple devices on it we usually want to have logging on + the state of that rail as an independent record. + For instance on the Heltec Tracker 1.1 board, this rail is the power source for the GPS and screen. + + The log messages will be short and complete (see PowerMon.Event in the protobufs for details). + something like "S:PM:C,0x00001234,REASON" where the hex number is the bitmask of all current states. + (We use a bitmask for states so that if a log message gets lost it won't be fatal) + """ + Lora_RXOn: PowerMon.State.ValueType # 8 + Lora_TXOn: PowerMon.State.ValueType # 16 + Lora_RXActive: PowerMon.State.ValueType # 32 + BT_On: PowerMon.State.ValueType # 64 + LED_On: PowerMon.State.ValueType # 128 + Screen_On: PowerMon.State.ValueType # 256 + Screen_Drawing: PowerMon.State.ValueType # 512 + Wifi_On: PowerMon.State.ValueType # 1024 + GPS_Active: PowerMon.State.ValueType # 2048 + """ + GPS is actively trying to find our location + See GPSPowerState for more details + """ + + def __init__( + self, + ) -> None: ... + +global___PowerMon = PowerMon + +@typing.final +class PowerStressMessage(google.protobuf.message.Message): + """ + PowerStress testing support via the C++ PowerStress module + """ + + 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[PowerStressMessage._Opcode.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + UNSET: PowerStressMessage._Opcode.ValueType # 0 + """ + Unset/unused + """ + PRINT_INFO: PowerStressMessage._Opcode.ValueType # 1 + """Print board version slog and send an ack that we are alive and ready to process commands""" + FORCE_QUIET: PowerStressMessage._Opcode.ValueType # 2 + """Try to turn off all automatic processing of packets, screen, sleeping, etc (to make it easier to measure in isolation)""" + END_QUIET: PowerStressMessage._Opcode.ValueType # 3 + """Stop powerstress processing - probably by just rebooting the board""" + SCREEN_ON: PowerStressMessage._Opcode.ValueType # 16 + """Turn the screen on""" + SCREEN_OFF: PowerStressMessage._Opcode.ValueType # 17 + """Turn the screen off""" + CPU_IDLE: PowerStressMessage._Opcode.ValueType # 32 + """Let the CPU run but we assume mostly idling for num_seconds""" + CPU_DEEPSLEEP: PowerStressMessage._Opcode.ValueType # 33 + """Force deep sleep for FIXME seconds""" + CPU_FULLON: PowerStressMessage._Opcode.ValueType # 34 + """Spin the CPU as fast as possible for num_seconds""" + LED_ON: PowerStressMessage._Opcode.ValueType # 48 + """Turn the LED on for num_seconds (and leave it on - for baseline power measurement purposes)""" + LED_OFF: PowerStressMessage._Opcode.ValueType # 49 + """Force the LED off for num_seconds""" + LORA_OFF: PowerStressMessage._Opcode.ValueType # 64 + """Completely turn off the LORA radio for num_seconds""" + LORA_TX: PowerStressMessage._Opcode.ValueType # 65 + """Send Lora packets for num_seconds""" + LORA_RX: PowerStressMessage._Opcode.ValueType # 66 + """Receive Lora packets for num_seconds (node will be mostly just listening, unless an external agent is helping stress this by sending packets on the current channel)""" + BT_OFF: PowerStressMessage._Opcode.ValueType # 80 + """Turn off the BT radio for num_seconds""" + BT_ON: PowerStressMessage._Opcode.ValueType # 81 + """Turn on the BT radio for num_seconds""" + WIFI_OFF: PowerStressMessage._Opcode.ValueType # 96 + """Turn off the WIFI radio for num_seconds""" + WIFI_ON: PowerStressMessage._Opcode.ValueType # 97 + """Turn on the WIFI radio for num_seconds""" + GPS_OFF: PowerStressMessage._Opcode.ValueType # 112 + """Turn off the GPS radio for num_seconds""" + GPS_ON: PowerStressMessage._Opcode.ValueType # 113 + """Turn on the GPS radio for num_seconds""" + + class Opcode(_Opcode, metaclass=_OpcodeEnumTypeWrapper): + """ + What operation would we like the UUT to perform. + note: senders should probably set want_response in their request packets, so that they can know when the state + machine has started processing their request + """ + + UNSET: PowerStressMessage.Opcode.ValueType # 0 + """ + Unset/unused + """ + PRINT_INFO: PowerStressMessage.Opcode.ValueType # 1 + """Print board version slog and send an ack that we are alive and ready to process commands""" + FORCE_QUIET: PowerStressMessage.Opcode.ValueType # 2 + """Try to turn off all automatic processing of packets, screen, sleeping, etc (to make it easier to measure in isolation)""" + END_QUIET: PowerStressMessage.Opcode.ValueType # 3 + """Stop powerstress processing - probably by just rebooting the board""" + SCREEN_ON: PowerStressMessage.Opcode.ValueType # 16 + """Turn the screen on""" + SCREEN_OFF: PowerStressMessage.Opcode.ValueType # 17 + """Turn the screen off""" + CPU_IDLE: PowerStressMessage.Opcode.ValueType # 32 + """Let the CPU run but we assume mostly idling for num_seconds""" + CPU_DEEPSLEEP: PowerStressMessage.Opcode.ValueType # 33 + """Force deep sleep for FIXME seconds""" + CPU_FULLON: PowerStressMessage.Opcode.ValueType # 34 + """Spin the CPU as fast as possible for num_seconds""" + LED_ON: PowerStressMessage.Opcode.ValueType # 48 + """Turn the LED on for num_seconds (and leave it on - for baseline power measurement purposes)""" + LED_OFF: PowerStressMessage.Opcode.ValueType # 49 + """Force the LED off for num_seconds""" + LORA_OFF: PowerStressMessage.Opcode.ValueType # 64 + """Completely turn off the LORA radio for num_seconds""" + LORA_TX: PowerStressMessage.Opcode.ValueType # 65 + """Send Lora packets for num_seconds""" + LORA_RX: PowerStressMessage.Opcode.ValueType # 66 + """Receive Lora packets for num_seconds (node will be mostly just listening, unless an external agent is helping stress this by sending packets on the current channel)""" + BT_OFF: PowerStressMessage.Opcode.ValueType # 80 + """Turn off the BT radio for num_seconds""" + BT_ON: PowerStressMessage.Opcode.ValueType # 81 + """Turn on the BT radio for num_seconds""" + WIFI_OFF: PowerStressMessage.Opcode.ValueType # 96 + """Turn off the WIFI radio for num_seconds""" + WIFI_ON: PowerStressMessage.Opcode.ValueType # 97 + """Turn on the WIFI radio for num_seconds""" + GPS_OFF: PowerStressMessage.Opcode.ValueType # 112 + """Turn off the GPS radio for num_seconds""" + GPS_ON: PowerStressMessage.Opcode.ValueType # 113 + """Turn on the GPS radio for num_seconds""" + + CMD_FIELD_NUMBER: builtins.int + NUM_SECONDS_FIELD_NUMBER: builtins.int + cmd: global___PowerStressMessage.Opcode.ValueType + """ + What type of HardwareMessage is this? + """ + num_seconds: builtins.float + def __init__( + self, + *, + cmd: global___PowerStressMessage.Opcode.ValueType = ..., + num_seconds: builtins.float = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["cmd", b"cmd", "num_seconds", b"num_seconds"]) -> None: ... + +global___PowerStressMessage = PowerStressMessage diff --git a/meshtastic/protobuf/remote_hardware_pb2.py b/meshtastic/protobuf/remote_hardware_pb2.py new file mode 100644 index 00000000..dab1f09c --- /dev/null +++ b/meshtastic/protobuf/remote_hardware_pb2.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: meshtastic/protobuf/remote_hardware.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n)meshtastic/protobuf/remote_hardware.proto\x12\x13meshtastic.protobuf\"\xdf\x01\n\x0fHardwareMessage\x12\x37\n\x04type\x18\x01 \x01(\x0e\x32).meshtastic.protobuf.HardwareMessage.Type\x12\x11\n\tgpio_mask\x18\x02 \x01(\x04\x12\x12\n\ngpio_value\x18\x03 \x01(\x04\"l\n\x04Type\x12\t\n\x05UNSET\x10\x00\x12\x0f\n\x0bWRITE_GPIOS\x10\x01\x12\x0f\n\x0bWATCH_GPIOS\x10\x02\x12\x11\n\rGPIOS_CHANGED\x10\x03\x12\x0e\n\nREAD_GPIOS\x10\x04\x12\x14\n\x10READ_GPIOS_REPLY\x10\x05\x42\x64\n\x14org.meshtastic.protoB\x0eRemoteHardwareZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.remote_hardware_pb2', _globals) +if _descriptor._USE_C_DESCRIPTORS == False: + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\016RemoteHardwareZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' + _globals['_HARDWAREMESSAGE']._serialized_start=67 + _globals['_HARDWAREMESSAGE']._serialized_end=290 + _globals['_HARDWAREMESSAGE_TYPE']._serialized_start=182 + _globals['_HARDWAREMESSAGE_TYPE']._serialized_end=290 +# @@protoc_insertion_point(module_scope) diff --git a/meshtastic/protobuf/remote_hardware_pb2.pyi b/meshtastic/protobuf/remote_hardware_pb2.pyi new file mode 100644 index 00000000..c2f7afa0 --- /dev/null +++ b/meshtastic/protobuf/remote_hardware_pb2.pyi @@ -0,0 +1,126 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" + +import builtins +import google.protobuf.descriptor +import google.protobuf.internal.enum_type_wrapper +import google.protobuf.message +import sys +import typing + +if sys.version_info >= (3, 10): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing.final +class HardwareMessage(google.protobuf.message.Message): + """ + An example app to show off the module system. This message is used for + REMOTE_HARDWARE_APP PortNums. + Also provides easy remote access to any GPIO. + In the future other remote hardware operations can be added based on user interest + (i.e. serial output, spi/i2c input/output). + FIXME - currently this feature is turned on by default which is dangerous + because no security yet (beyond the channel mechanism). + It should be off by default and then protected based on some TBD mechanism + (a special channel once multichannel support is included?) + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + class _Type: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + + class _TypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[HardwareMessage._Type.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + UNSET: HardwareMessage._Type.ValueType # 0 + """ + Unset/unused + """ + WRITE_GPIOS: HardwareMessage._Type.ValueType # 1 + """ + Set gpio gpios based on gpio_mask/gpio_value + """ + WATCH_GPIOS: HardwareMessage._Type.ValueType # 2 + """ + We are now interested in watching the gpio_mask gpios. + If the selected gpios change, please broadcast GPIOS_CHANGED. + Will implicitly change the gpios requested to be INPUT gpios. + """ + GPIOS_CHANGED: HardwareMessage._Type.ValueType # 3 + """ + The gpios listed in gpio_mask have changed, the new values are listed in gpio_value + """ + READ_GPIOS: HardwareMessage._Type.ValueType # 4 + """ + Read the gpios specified in gpio_mask, send back a READ_GPIOS_REPLY reply with gpio_value populated + """ + READ_GPIOS_REPLY: HardwareMessage._Type.ValueType # 5 + """ + A reply to READ_GPIOS. gpio_mask and gpio_value will be populated + """ + + class Type(_Type, metaclass=_TypeEnumTypeWrapper): + """ + TODO: REPLACE + """ + + UNSET: HardwareMessage.Type.ValueType # 0 + """ + Unset/unused + """ + WRITE_GPIOS: HardwareMessage.Type.ValueType # 1 + """ + Set gpio gpios based on gpio_mask/gpio_value + """ + WATCH_GPIOS: HardwareMessage.Type.ValueType # 2 + """ + We are now interested in watching the gpio_mask gpios. + If the selected gpios change, please broadcast GPIOS_CHANGED. + Will implicitly change the gpios requested to be INPUT gpios. + """ + GPIOS_CHANGED: HardwareMessage.Type.ValueType # 3 + """ + The gpios listed in gpio_mask have changed, the new values are listed in gpio_value + """ + READ_GPIOS: HardwareMessage.Type.ValueType # 4 + """ + Read the gpios specified in gpio_mask, send back a READ_GPIOS_REPLY reply with gpio_value populated + """ + READ_GPIOS_REPLY: HardwareMessage.Type.ValueType # 5 + """ + A reply to READ_GPIOS. gpio_mask and gpio_value will be populated + """ + + TYPE_FIELD_NUMBER: builtins.int + GPIO_MASK_FIELD_NUMBER: builtins.int + GPIO_VALUE_FIELD_NUMBER: builtins.int + type: global___HardwareMessage.Type.ValueType + """ + What type of HardwareMessage is this? + """ + gpio_mask: builtins.int + """ + What gpios are we changing. Not used for all MessageTypes, see MessageType for details + """ + gpio_value: builtins.int + """ + For gpios that were listed in gpio_mask as valid, what are the signal levels for those gpios. + Not used for all MessageTypes, see MessageType for details + """ + def __init__( + self, + *, + type: global___HardwareMessage.Type.ValueType = ..., + gpio_mask: builtins.int = ..., + gpio_value: builtins.int = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["gpio_mask", b"gpio_mask", "gpio_value", b"gpio_value", "type", b"type"]) -> None: ... + +global___HardwareMessage = HardwareMessage diff --git a/meshtastic/protobuf/rtttl_pb2.py b/meshtastic/protobuf/rtttl_pb2.py new file mode 100644 index 00000000..12842c25 --- /dev/null +++ b/meshtastic/protobuf/rtttl_pb2.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: meshtastic/protobuf/rtttl.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1fmeshtastic/protobuf/rtttl.proto\x12\x13meshtastic.protobuf\"\x1f\n\x0bRTTTLConfig\x12\x10\n\x08ringtone\x18\x01 \x01(\tBg\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) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.rtttl_pb2', _globals) +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 +# @@protoc_insertion_point(module_scope) diff --git a/meshtastic/protobuf/rtttl_pb2.pyi b/meshtastic/protobuf/rtttl_pb2.pyi new file mode 100644 index 00000000..fe91f94d --- /dev/null +++ b/meshtastic/protobuf/rtttl_pb2.pyi @@ -0,0 +1,33 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" + +import builtins +import google.protobuf.descriptor +import google.protobuf.message +import typing + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing.final +class RTTTLConfig(google.protobuf.message.Message): + """ + Canned message module configuration. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + RINGTONE_FIELD_NUMBER: builtins.int + ringtone: builtins.str + """ + Ringtone for PWM Buzzer in RTTTL Format. + """ + def __init__( + self, + *, + ringtone: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["ringtone", b"ringtone"]) -> None: ... + +global___RTTTLConfig = RTTTLConfig diff --git a/meshtastic/protobuf/storeforward_pb2.py b/meshtastic/protobuf/storeforward_pb2.py new file mode 100644 index 00000000..cd57ff62 --- /dev/null +++ b/meshtastic/protobuf/storeforward_pb2.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: meshtastic/protobuf/storeforward.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n&meshtastic/protobuf/storeforward.proto\x12\x13meshtastic.protobuf\"\xc0\x07\n\x0fStoreAndForward\x12@\n\x02rr\x18\x01 \x01(\x0e\x32\x34.meshtastic.protobuf.StoreAndForward.RequestResponse\x12@\n\x05stats\x18\x02 \x01(\x0b\x32/.meshtastic.protobuf.StoreAndForward.StatisticsH\x00\x12?\n\x07history\x18\x03 \x01(\x0b\x32,.meshtastic.protobuf.StoreAndForward.HistoryH\x00\x12\x43\n\theartbeat\x18\x04 \x01(\x0b\x32..meshtastic.protobuf.StoreAndForward.HeartbeatH\x00\x12\x0e\n\x04text\x18\x05 \x01(\x0cH\x00\x1a\xcd\x01\n\nStatistics\x12\x16\n\x0emessages_total\x18\x01 \x01(\r\x12\x16\n\x0emessages_saved\x18\x02 \x01(\r\x12\x14\n\x0cmessages_max\x18\x03 \x01(\r\x12\x0f\n\x07up_time\x18\x04 \x01(\r\x12\x10\n\x08requests\x18\x05 \x01(\r\x12\x18\n\x10requests_history\x18\x06 \x01(\r\x12\x11\n\theartbeat\x18\x07 \x01(\x08\x12\x12\n\nreturn_max\x18\x08 \x01(\r\x12\x15\n\rreturn_window\x18\t \x01(\r\x1aI\n\x07History\x12\x18\n\x10history_messages\x18\x01 \x01(\r\x12\x0e\n\x06window\x18\x02 \x01(\r\x12\x14\n\x0clast_request\x18\x03 \x01(\r\x1a.\n\tHeartbeat\x12\x0e\n\x06period\x18\x01 \x01(\r\x12\x11\n\tsecondary\x18\x02 \x01(\r\"\xbc\x02\n\x0fRequestResponse\x12\t\n\x05UNSET\x10\x00\x12\x10\n\x0cROUTER_ERROR\x10\x01\x12\x14\n\x10ROUTER_HEARTBEAT\x10\x02\x12\x0f\n\x0bROUTER_PING\x10\x03\x12\x0f\n\x0bROUTER_PONG\x10\x04\x12\x0f\n\x0bROUTER_BUSY\x10\x05\x12\x12\n\x0eROUTER_HISTORY\x10\x06\x12\x10\n\x0cROUTER_STATS\x10\x07\x12\x16\n\x12ROUTER_TEXT_DIRECT\x10\x08\x12\x19\n\x15ROUTER_TEXT_BROADCAST\x10\t\x12\x10\n\x0c\x43LIENT_ERROR\x10@\x12\x12\n\x0e\x43LIENT_HISTORY\x10\x41\x12\x10\n\x0c\x43LIENT_STATS\x10\x42\x12\x0f\n\x0b\x43LIENT_PING\x10\x43\x12\x0f\n\x0b\x43LIENT_PONG\x10\x44\x12\x10\n\x0c\x43LIENT_ABORT\x10jB\t\n\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) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.storeforward_pb2', _globals) +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 +# @@protoc_insertion_point(module_scope) diff --git a/meshtastic/protobuf/storeforward_pb2.pyi b/meshtastic/protobuf/storeforward_pb2.pyi new file mode 100644 index 00000000..aff4f74f --- /dev/null +++ b/meshtastic/protobuf/storeforward_pb2.pyi @@ -0,0 +1,345 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" + +import builtins +import google.protobuf.descriptor +import google.protobuf.internal.enum_type_wrapper +import google.protobuf.message +import sys +import typing + +if sys.version_info >= (3, 10): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing.final +class StoreAndForward(google.protobuf.message.Message): + """ + TODO: REPLACE + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + class _RequestResponse: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + + class _RequestResponseEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[StoreAndForward._RequestResponse.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + UNSET: StoreAndForward._RequestResponse.ValueType # 0 + """ + Unset/unused + """ + ROUTER_ERROR: StoreAndForward._RequestResponse.ValueType # 1 + """ + Router is an in error state. + """ + ROUTER_HEARTBEAT: StoreAndForward._RequestResponse.ValueType # 2 + """ + Router heartbeat + """ + ROUTER_PING: StoreAndForward._RequestResponse.ValueType # 3 + """ + Router has requested the client respond. This can work as a + "are you there" message. + """ + ROUTER_PONG: StoreAndForward._RequestResponse.ValueType # 4 + """ + The response to a "Ping" + """ + ROUTER_BUSY: StoreAndForward._RequestResponse.ValueType # 5 + """ + Router is currently busy. Please try again later. + """ + ROUTER_HISTORY: StoreAndForward._RequestResponse.ValueType # 6 + """ + Router is responding to a request for history. + """ + ROUTER_STATS: StoreAndForward._RequestResponse.ValueType # 7 + """ + Router is responding to a request for stats. + """ + ROUTER_TEXT_DIRECT: StoreAndForward._RequestResponse.ValueType # 8 + """ + Router sends a text message from its history that was a direct message. + """ + ROUTER_TEXT_BROADCAST: StoreAndForward._RequestResponse.ValueType # 9 + """ + Router sends a text message from its history that was a broadcast. + """ + CLIENT_ERROR: StoreAndForward._RequestResponse.ValueType # 64 + """ + Client is an in error state. + """ + CLIENT_HISTORY: StoreAndForward._RequestResponse.ValueType # 65 + """ + Client has requested a replay from the router. + """ + CLIENT_STATS: StoreAndForward._RequestResponse.ValueType # 66 + """ + Client has requested stats from the router. + """ + CLIENT_PING: StoreAndForward._RequestResponse.ValueType # 67 + """ + Client has requested the router respond. This can work as a + "are you there" message. + """ + CLIENT_PONG: StoreAndForward._RequestResponse.ValueType # 68 + """ + The response to a "Ping" + """ + CLIENT_ABORT: StoreAndForward._RequestResponse.ValueType # 106 + """ + Client has requested that the router abort processing the client's request + """ + + class RequestResponse(_RequestResponse, metaclass=_RequestResponseEnumTypeWrapper): + """ + 001 - 063 = From Router + 064 - 127 = From Client + """ + + UNSET: StoreAndForward.RequestResponse.ValueType # 0 + """ + Unset/unused + """ + ROUTER_ERROR: StoreAndForward.RequestResponse.ValueType # 1 + """ + Router is an in error state. + """ + ROUTER_HEARTBEAT: StoreAndForward.RequestResponse.ValueType # 2 + """ + Router heartbeat + """ + ROUTER_PING: StoreAndForward.RequestResponse.ValueType # 3 + """ + Router has requested the client respond. This can work as a + "are you there" message. + """ + ROUTER_PONG: StoreAndForward.RequestResponse.ValueType # 4 + """ + The response to a "Ping" + """ + ROUTER_BUSY: StoreAndForward.RequestResponse.ValueType # 5 + """ + Router is currently busy. Please try again later. + """ + ROUTER_HISTORY: StoreAndForward.RequestResponse.ValueType # 6 + """ + Router is responding to a request for history. + """ + ROUTER_STATS: StoreAndForward.RequestResponse.ValueType # 7 + """ + Router is responding to a request for stats. + """ + ROUTER_TEXT_DIRECT: StoreAndForward.RequestResponse.ValueType # 8 + """ + Router sends a text message from its history that was a direct message. + """ + ROUTER_TEXT_BROADCAST: StoreAndForward.RequestResponse.ValueType # 9 + """ + Router sends a text message from its history that was a broadcast. + """ + CLIENT_ERROR: StoreAndForward.RequestResponse.ValueType # 64 + """ + Client is an in error state. + """ + CLIENT_HISTORY: StoreAndForward.RequestResponse.ValueType # 65 + """ + Client has requested a replay from the router. + """ + CLIENT_STATS: StoreAndForward.RequestResponse.ValueType # 66 + """ + Client has requested stats from the router. + """ + CLIENT_PING: StoreAndForward.RequestResponse.ValueType # 67 + """ + Client has requested the router respond. This can work as a + "are you there" message. + """ + CLIENT_PONG: StoreAndForward.RequestResponse.ValueType # 68 + """ + The response to a "Ping" + """ + CLIENT_ABORT: StoreAndForward.RequestResponse.ValueType # 106 + """ + Client has requested that the router abort processing the client's request + """ + + @typing.final + class Statistics(google.protobuf.message.Message): + """ + TODO: REPLACE + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + MESSAGES_TOTAL_FIELD_NUMBER: builtins.int + MESSAGES_SAVED_FIELD_NUMBER: builtins.int + MESSAGES_MAX_FIELD_NUMBER: builtins.int + UP_TIME_FIELD_NUMBER: builtins.int + REQUESTS_FIELD_NUMBER: builtins.int + REQUESTS_HISTORY_FIELD_NUMBER: builtins.int + HEARTBEAT_FIELD_NUMBER: builtins.int + RETURN_MAX_FIELD_NUMBER: builtins.int + RETURN_WINDOW_FIELD_NUMBER: builtins.int + messages_total: builtins.int + """ + Number of messages we have ever seen + """ + messages_saved: builtins.int + """ + Number of messages we have currently saved our history. + """ + messages_max: builtins.int + """ + Maximum number of messages we will save + """ + up_time: builtins.int + """ + Router uptime in seconds + """ + requests: builtins.int + """ + Number of times any client sent a request to the S&F. + """ + requests_history: builtins.int + """ + Number of times the history was requested. + """ + heartbeat: builtins.bool + """ + Is the heartbeat enabled on the server? + """ + return_max: builtins.int + """ + Maximum number of messages the server will return. + """ + return_window: builtins.int + """ + Maximum history window in minutes the server will return messages from. + """ + def __init__( + self, + *, + messages_total: builtins.int = ..., + messages_saved: builtins.int = ..., + messages_max: builtins.int = ..., + up_time: builtins.int = ..., + requests: builtins.int = ..., + requests_history: builtins.int = ..., + heartbeat: builtins.bool = ..., + return_max: builtins.int = ..., + return_window: builtins.int = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["heartbeat", b"heartbeat", "messages_max", b"messages_max", "messages_saved", b"messages_saved", "messages_total", b"messages_total", "requests", b"requests", "requests_history", b"requests_history", "return_max", b"return_max", "return_window", b"return_window", "up_time", b"up_time"]) -> None: ... + + @typing.final + class History(google.protobuf.message.Message): + """ + TODO: REPLACE + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + HISTORY_MESSAGES_FIELD_NUMBER: builtins.int + WINDOW_FIELD_NUMBER: builtins.int + LAST_REQUEST_FIELD_NUMBER: builtins.int + history_messages: builtins.int + """ + Number of that will be sent to the client + """ + window: builtins.int + """ + The window of messages that was used to filter the history client requested + """ + last_request: builtins.int + """ + Index in the packet history of the last message sent in a previous request to the server. + Will be sent to the client before sending the history and can be set in a subsequent request to avoid getting packets the server already sent to the client. + """ + def __init__( + self, + *, + history_messages: builtins.int = ..., + window: builtins.int = ..., + last_request: builtins.int = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["history_messages", b"history_messages", "last_request", b"last_request", "window", b"window"]) -> None: ... + + @typing.final + class Heartbeat(google.protobuf.message.Message): + """ + TODO: REPLACE + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + PERIOD_FIELD_NUMBER: builtins.int + SECONDARY_FIELD_NUMBER: builtins.int + period: builtins.int + """ + Period in seconds that the heartbeat is sent out that will be sent to the client + """ + secondary: builtins.int + """ + If set, this is not the primary Store & Forward router on the mesh + """ + def __init__( + self, + *, + period: builtins.int = ..., + secondary: builtins.int = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["period", b"period", "secondary", b"secondary"]) -> None: ... + + RR_FIELD_NUMBER: builtins.int + STATS_FIELD_NUMBER: builtins.int + HISTORY_FIELD_NUMBER: builtins.int + HEARTBEAT_FIELD_NUMBER: builtins.int + TEXT_FIELD_NUMBER: builtins.int + rr: global___StoreAndForward.RequestResponse.ValueType + """ + TODO: REPLACE + """ + text: builtins.bytes + """ + Text from history message. + """ + @property + def stats(self) -> global___StoreAndForward.Statistics: + """ + TODO: REPLACE + """ + + @property + def history(self) -> global___StoreAndForward.History: + """ + TODO: REPLACE + """ + + @property + def heartbeat(self) -> global___StoreAndForward.Heartbeat: + """ + TODO: REPLACE + """ + + def __init__( + self, + *, + rr: global___StoreAndForward.RequestResponse.ValueType = ..., + stats: global___StoreAndForward.Statistics | None = ..., + history: global___StoreAndForward.History | None = ..., + heartbeat: global___StoreAndForward.Heartbeat | None = ..., + text: builtins.bytes = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["heartbeat", b"heartbeat", "history", b"history", "stats", b"stats", "text", b"text", "variant", b"variant"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["heartbeat", b"heartbeat", "history", b"history", "rr", b"rr", "stats", b"stats", "text", b"text", "variant", b"variant"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["variant", b"variant"]) -> typing.Literal["stats", "history", "heartbeat", "text"] | None: ... + +global___StoreAndForward = StoreAndForward diff --git a/meshtastic/protobuf/telemetry_pb2.py b/meshtastic/protobuf/telemetry_pb2.py new file mode 100644 index 00000000..20f5c472 --- /dev/null +++ b/meshtastic/protobuf/telemetry_pb2.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: meshtastic/protobuf/telemetry.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n#meshtastic/protobuf/telemetry.proto\x12\x13meshtastic.protobuf\"\xf3\x01\n\rDeviceMetrics\x12\x1a\n\rbattery_level\x18\x01 \x01(\rH\x00\x88\x01\x01\x12\x14\n\x07voltage\x18\x02 \x01(\x02H\x01\x88\x01\x01\x12 \n\x13\x63hannel_utilization\x18\x03 \x01(\x02H\x02\x88\x01\x01\x12\x18\n\x0b\x61ir_util_tx\x18\x04 \x01(\x02H\x03\x88\x01\x01\x12\x1b\n\x0euptime_seconds\x18\x05 \x01(\rH\x04\x88\x01\x01\x42\x10\n\x0e_battery_levelB\n\n\x08_voltageB\x16\n\x14_channel_utilizationB\x0e\n\x0c_air_util_txB\x11\n\x0f_uptime_seconds\"\x82\x07\n\x12\x45nvironmentMetrics\x12\x18\n\x0btemperature\x18\x01 \x01(\x02H\x00\x88\x01\x01\x12\x1e\n\x11relative_humidity\x18\x02 \x01(\x02H\x01\x88\x01\x01\x12 \n\x13\x62\x61rometric_pressure\x18\x03 \x01(\x02H\x02\x88\x01\x01\x12\x1b\n\x0egas_resistance\x18\x04 \x01(\x02H\x03\x88\x01\x01\x12\x14\n\x07voltage\x18\x05 \x01(\x02H\x04\x88\x01\x01\x12\x14\n\x07\x63urrent\x18\x06 \x01(\x02H\x05\x88\x01\x01\x12\x10\n\x03iaq\x18\x07 \x01(\rH\x06\x88\x01\x01\x12\x15\n\x08\x64istance\x18\x08 \x01(\x02H\x07\x88\x01\x01\x12\x10\n\x03lux\x18\t \x01(\x02H\x08\x88\x01\x01\x12\x16\n\twhite_lux\x18\n \x01(\x02H\t\x88\x01\x01\x12\x13\n\x06ir_lux\x18\x0b \x01(\x02H\n\x88\x01\x01\x12\x13\n\x06uv_lux\x18\x0c \x01(\x02H\x0b\x88\x01\x01\x12\x1b\n\x0ewind_direction\x18\r \x01(\rH\x0c\x88\x01\x01\x12\x17\n\nwind_speed\x18\x0e \x01(\x02H\r\x88\x01\x01\x12\x13\n\x06weight\x18\x0f \x01(\x02H\x0e\x88\x01\x01\x12\x16\n\twind_gust\x18\x10 \x01(\x02H\x0f\x88\x01\x01\x12\x16\n\twind_lull\x18\x11 \x01(\x02H\x10\x88\x01\x01\x12\x16\n\tradiation\x18\x12 \x01(\x02H\x11\x88\x01\x01\x12\x18\n\x0brainfall_1h\x18\x13 \x01(\x02H\x12\x88\x01\x01\x12\x19\n\x0crainfall_24h\x18\x14 \x01(\x02H\x13\x88\x01\x01\x12\x1a\n\rsoil_moisture\x18\x15 \x01(\rH\x14\x88\x01\x01\x12\x1d\n\x10soil_temperature\x18\x16 \x01(\x02H\x15\x88\x01\x01\x42\x0e\n\x0c_temperatureB\x14\n\x12_relative_humidityB\x16\n\x14_barometric_pressureB\x11\n\x0f_gas_resistanceB\n\n\x08_voltageB\n\n\x08_currentB\x06\n\x04_iaqB\x0b\n\t_distanceB\x06\n\x04_luxB\x0c\n\n_white_luxB\t\n\x07_ir_luxB\t\n\x07_uv_luxB\x11\n\x0f_wind_directionB\r\n\x0b_wind_speedB\t\n\x07_weightB\x0c\n\n_wind_gustB\x0c\n\n_wind_lullB\x0c\n\n_radiationB\x0e\n\x0c_rainfall_1hB\x0f\n\r_rainfall_24hB\x10\n\x0e_soil_moistureB\x13\n\x11_soil_temperature\"\xae\x05\n\x0cPowerMetrics\x12\x18\n\x0b\x63h1_voltage\x18\x01 \x01(\x02H\x00\x88\x01\x01\x12\x18\n\x0b\x63h1_current\x18\x02 \x01(\x02H\x01\x88\x01\x01\x12\x18\n\x0b\x63h2_voltage\x18\x03 \x01(\x02H\x02\x88\x01\x01\x12\x18\n\x0b\x63h2_current\x18\x04 \x01(\x02H\x03\x88\x01\x01\x12\x18\n\x0b\x63h3_voltage\x18\x05 \x01(\x02H\x04\x88\x01\x01\x12\x18\n\x0b\x63h3_current\x18\x06 \x01(\x02H\x05\x88\x01\x01\x12\x18\n\x0b\x63h4_voltage\x18\x07 \x01(\x02H\x06\x88\x01\x01\x12\x18\n\x0b\x63h4_current\x18\x08 \x01(\x02H\x07\x88\x01\x01\x12\x18\n\x0b\x63h5_voltage\x18\t \x01(\x02H\x08\x88\x01\x01\x12\x18\n\x0b\x63h5_current\x18\n \x01(\x02H\t\x88\x01\x01\x12\x18\n\x0b\x63h6_voltage\x18\x0b \x01(\x02H\n\x88\x01\x01\x12\x18\n\x0b\x63h6_current\x18\x0c \x01(\x02H\x0b\x88\x01\x01\x12\x18\n\x0b\x63h7_voltage\x18\r \x01(\x02H\x0c\x88\x01\x01\x12\x18\n\x0b\x63h7_current\x18\x0e \x01(\x02H\r\x88\x01\x01\x12\x18\n\x0b\x63h8_voltage\x18\x0f \x01(\x02H\x0e\x88\x01\x01\x12\x18\n\x0b\x63h8_current\x18\x10 \x01(\x02H\x0f\x88\x01\x01\x42\x0e\n\x0c_ch1_voltageB\x0e\n\x0c_ch1_currentB\x0e\n\x0c_ch2_voltageB\x0e\n\x0c_ch2_currentB\x0e\n\x0c_ch3_voltageB\x0e\n\x0c_ch3_currentB\x0e\n\x0c_ch4_voltageB\x0e\n\x0c_ch4_currentB\x0e\n\x0c_ch5_voltageB\x0e\n\x0c_ch5_currentB\x0e\n\x0c_ch6_voltageB\x0e\n\x0c_ch6_currentB\x0e\n\x0c_ch7_voltageB\x0e\n\x0c_ch7_currentB\x0e\n\x0c_ch8_voltageB\x0e\n\x0c_ch8_current\"\xb1\t\n\x11\x41irQualityMetrics\x12\x1a\n\rpm10_standard\x18\x01 \x01(\rH\x00\x88\x01\x01\x12\x1a\n\rpm25_standard\x18\x02 \x01(\rH\x01\x88\x01\x01\x12\x1b\n\x0epm100_standard\x18\x03 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12pm10_environmental\x18\x04 \x01(\rH\x03\x88\x01\x01\x12\x1f\n\x12pm25_environmental\x18\x05 \x01(\rH\x04\x88\x01\x01\x12 \n\x13pm100_environmental\x18\x06 \x01(\rH\x05\x88\x01\x01\x12\x1b\n\x0eparticles_03um\x18\x07 \x01(\rH\x06\x88\x01\x01\x12\x1b\n\x0eparticles_05um\x18\x08 \x01(\rH\x07\x88\x01\x01\x12\x1b\n\x0eparticles_10um\x18\t \x01(\rH\x08\x88\x01\x01\x12\x1b\n\x0eparticles_25um\x18\n \x01(\rH\t\x88\x01\x01\x12\x1b\n\x0eparticles_50um\x18\x0b \x01(\rH\n\x88\x01\x01\x12\x1c\n\x0fparticles_100um\x18\x0c \x01(\rH\x0b\x88\x01\x01\x12\x10\n\x03\x63o2\x18\r \x01(\rH\x0c\x88\x01\x01\x12\x1c\n\x0f\x63o2_temperature\x18\x0e \x01(\x02H\r\x88\x01\x01\x12\x19\n\x0c\x63o2_humidity\x18\x0f \x01(\x02H\x0e\x88\x01\x01\x12\x1e\n\x11\x66orm_formaldehyde\x18\x10 \x01(\x02H\x0f\x88\x01\x01\x12\x1a\n\rform_humidity\x18\x11 \x01(\x02H\x10\x88\x01\x01\x12\x1d\n\x10\x66orm_temperature\x18\x12 \x01(\x02H\x11\x88\x01\x01\x12\x1a\n\rpm40_standard\x18\x13 \x01(\rH\x12\x88\x01\x01\x12\x1b\n\x0eparticles_40um\x18\x14 \x01(\rH\x13\x88\x01\x01\x12\x1b\n\x0epm_temperature\x18\x15 \x01(\x02H\x14\x88\x01\x01\x12\x18\n\x0bpm_humidity\x18\x16 \x01(\x02H\x15\x88\x01\x01\x12\x17\n\npm_voc_idx\x18\x17 \x01(\x02H\x16\x88\x01\x01\x12\x17\n\npm_nox_idx\x18\x18 \x01(\x02H\x17\x88\x01\x01\x12\x1a\n\rparticles_tps\x18\x19 \x01(\x02H\x18\x88\x01\x01\x42\x10\n\x0e_pm10_standardB\x10\n\x0e_pm25_standardB\x11\n\x0f_pm100_standardB\x15\n\x13_pm10_environmentalB\x15\n\x13_pm25_environmentalB\x16\n\x14_pm100_environmentalB\x11\n\x0f_particles_03umB\x11\n\x0f_particles_05umB\x11\n\x0f_particles_10umB\x11\n\x0f_particles_25umB\x11\n\x0f_particles_50umB\x12\n\x10_particles_100umB\x06\n\x04_co2B\x12\n\x10_co2_temperatureB\x0f\n\r_co2_humidityB\x14\n\x12_form_formaldehydeB\x10\n\x0e_form_humidityB\x13\n\x11_form_temperatureB\x10\n\x0e_pm40_standardB\x11\n\x0f_particles_40umB\x11\n\x0f_pm_temperatureB\x0e\n\x0c_pm_humidityB\r\n\x0b_pm_voc_idxB\r\n\x0b_pm_nox_idxB\x10\n\x0e_particles_tps\"\xea\x02\n\nLocalStats\x12\x16\n\x0euptime_seconds\x18\x01 \x01(\r\x12\x1b\n\x13\x63hannel_utilization\x18\x02 \x01(\x02\x12\x13\n\x0b\x61ir_util_tx\x18\x03 \x01(\x02\x12\x16\n\x0enum_packets_tx\x18\x04 \x01(\r\x12\x16\n\x0enum_packets_rx\x18\x05 \x01(\r\x12\x1a\n\x12num_packets_rx_bad\x18\x06 \x01(\r\x12\x18\n\x10num_online_nodes\x18\x07 \x01(\r\x12\x17\n\x0fnum_total_nodes\x18\x08 \x01(\r\x12\x13\n\x0bnum_rx_dupe\x18\t \x01(\r\x12\x14\n\x0cnum_tx_relay\x18\n \x01(\r\x12\x1d\n\x15num_tx_relay_canceled\x18\x0b \x01(\r\x12\x18\n\x10heap_total_bytes\x18\x0c \x01(\r\x12\x17\n\x0fheap_free_bytes\x18\r \x01(\r\x12\x16\n\x0enum_tx_dropped\x18\x0e \x01(\r\"{\n\rHealthMetrics\x12\x16\n\theart_bpm\x18\x01 \x01(\rH\x00\x88\x01\x01\x12\x11\n\x04spO2\x18\x02 \x01(\rH\x01\x88\x01\x01\x12\x18\n\x0btemperature\x18\x03 \x01(\x02H\x02\x88\x01\x01\x42\x0c\n\n_heart_bpmB\x07\n\x05_spO2B\x0e\n\x0c_temperature\"\x91\x02\n\x0bHostMetrics\x12\x16\n\x0euptime_seconds\x18\x01 \x01(\r\x12\x15\n\rfreemem_bytes\x18\x02 \x01(\x04\x12\x17\n\x0f\x64iskfree1_bytes\x18\x03 \x01(\x04\x12\x1c\n\x0f\x64iskfree2_bytes\x18\x04 \x01(\x04H\x00\x88\x01\x01\x12\x1c\n\x0f\x64iskfree3_bytes\x18\x05 \x01(\x04H\x01\x88\x01\x01\x12\r\n\x05load1\x18\x06 \x01(\r\x12\r\n\x05load5\x18\x07 \x01(\r\x12\x0e\n\x06load15\x18\x08 \x01(\r\x12\x18\n\x0buser_string\x18\t \x01(\tH\x02\x88\x01\x01\x42\x12\n\x10_diskfree2_bytesB\x12\n\x10_diskfree3_bytesB\x0e\n\x0c_user_string\"\xdd\x03\n\tTelemetry\x12\x0c\n\x04time\x18\x01 \x01(\x07\x12<\n\x0e\x64\x65vice_metrics\x18\x02 \x01(\x0b\x32\".meshtastic.protobuf.DeviceMetricsH\x00\x12\x46\n\x13\x65nvironment_metrics\x18\x03 \x01(\x0b\x32\'.meshtastic.protobuf.EnvironmentMetricsH\x00\x12\x45\n\x13\x61ir_quality_metrics\x18\x04 \x01(\x0b\x32&.meshtastic.protobuf.AirQualityMetricsH\x00\x12:\n\rpower_metrics\x18\x05 \x01(\x0b\x32!.meshtastic.protobuf.PowerMetricsH\x00\x12\x36\n\x0blocal_stats\x18\x06 \x01(\x0b\x32\x1f.meshtastic.protobuf.LocalStatsH\x00\x12<\n\x0ehealth_metrics\x18\x07 \x01(\x0b\x32\".meshtastic.protobuf.HealthMetricsH\x00\x12\x38\n\x0chost_metrics\x18\x08 \x01(\x0b\x32 .meshtastic.protobuf.HostMetricsH\x00\x42\t\n\x07variant\">\n\rNau7802Config\x12\x12\n\nzeroOffset\x18\x01 \x01(\x05\x12\x19\n\x11\x63\x61librationFactor\x18\x02 \x01(\x02*\xf9\x04\n\x13TelemetrySensorType\x12\x10\n\x0cSENSOR_UNSET\x10\x00\x12\n\n\x06\x42ME280\x10\x01\x12\n\n\x06\x42ME680\x10\x02\x12\x0b\n\x07MCP9808\x10\x03\x12\n\n\x06INA260\x10\x04\x12\n\n\x06INA219\x10\x05\x12\n\n\x06\x42MP280\x10\x06\x12\t\n\x05SHTC3\x10\x07\x12\t\n\x05LPS22\x10\x08\x12\x0b\n\x07QMC6310\x10\t\x12\x0b\n\x07QMI8658\x10\n\x12\x0c\n\x08QMC5883L\x10\x0b\x12\t\n\x05SHT31\x10\x0c\x12\x0c\n\x08PMSA003I\x10\r\x12\x0b\n\x07INA3221\x10\x0e\x12\n\n\x06\x42MP085\x10\x0f\x12\x0c\n\x08RCWL9620\x10\x10\x12\t\n\x05SHT4X\x10\x11\x12\x0c\n\x08VEML7700\x10\x12\x12\x0c\n\x08MLX90632\x10\x13\x12\x0b\n\x07OPT3001\x10\x14\x12\x0c\n\x08LTR390UV\x10\x15\x12\x0e\n\nTSL25911FN\x10\x16\x12\t\n\x05\x41HT10\x10\x17\x12\x10\n\x0c\x44\x46ROBOT_LARK\x10\x18\x12\x0b\n\x07NAU7802\x10\x19\x12\n\n\x06\x42MP3XX\x10\x1a\x12\x0c\n\x08ICM20948\x10\x1b\x12\x0c\n\x08MAX17048\x10\x1c\x12\x11\n\rCUSTOM_SENSOR\x10\x1d\x12\x0c\n\x08MAX30102\x10\x1e\x12\x0c\n\x08MLX90614\x10\x1f\x12\t\n\x05SCD4X\x10 \x12\x0b\n\x07RADSENS\x10!\x12\n\n\x06INA226\x10\"\x12\x10\n\x0c\x44\x46ROBOT_RAIN\x10#\x12\n\n\x06\x44PS310\x10$\x12\x0c\n\x08RAK12035\x10%\x12\x0c\n\x08MAX17261\x10&\x12\x0b\n\x07PCT2075\x10\'\x12\x0b\n\x07\x41\x44S1X15\x10(\x12\x0f\n\x0b\x41\x44S1X15_ALT\x10)\x12\t\n\x05SFA30\x10*\x12\t\n\x05SEN5X\x10+\x12\x0b\n\x07TSL2561\x10,\x12\n\n\x06\x42H1750\x10-Be\n\x14org.meshtastic.protoB\x0fTelemetryProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.telemetry_pb2', _globals) +if _descriptor._USE_C_DESCRIPTORS == False: + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\017TelemetryProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' + _globals['_TELEMETRYSENSORTYPE']._serialized_start=4411 + _globals['_TELEMETRYSENSORTYPE']._serialized_end=5044 + _globals['_DEVICEMETRICS']._serialized_start=61 + _globals['_DEVICEMETRICS']._serialized_end=304 + _globals['_ENVIRONMENTMETRICS']._serialized_start=307 + _globals['_ENVIRONMENTMETRICS']._serialized_end=1205 + _globals['_POWERMETRICS']._serialized_start=1208 + _globals['_POWERMETRICS']._serialized_end=1894 + _globals['_AIRQUALITYMETRICS']._serialized_start=1897 + _globals['_AIRQUALITYMETRICS']._serialized_end=3098 + _globals['_LOCALSTATS']._serialized_start=3101 + _globals['_LOCALSTATS']._serialized_end=3463 + _globals['_HEALTHMETRICS']._serialized_start=3465 + _globals['_HEALTHMETRICS']._serialized_end=3588 + _globals['_HOSTMETRICS']._serialized_start=3591 + _globals['_HOSTMETRICS']._serialized_end=3864 + _globals['_TELEMETRY']._serialized_start=3867 + _globals['_TELEMETRY']._serialized_end=4344 + _globals['_NAU7802CONFIG']._serialized_start=4346 + _globals['_NAU7802CONFIG']._serialized_end=4408 +# @@protoc_insertion_point(module_scope) diff --git a/meshtastic/protobuf/telemetry_pb2.pyi b/meshtastic/protobuf/telemetry_pb2.pyi new file mode 100644 index 00000000..3e3c4065 --- /dev/null +++ b/meshtastic/protobuf/telemetry_pb2.pyi @@ -0,0 +1,1343 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" + +import builtins +import google.protobuf.descriptor +import google.protobuf.internal.enum_type_wrapper +import google.protobuf.message +import sys +import typing + +if sys.version_info >= (3, 10): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +class _TelemetrySensorType: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _TelemetrySensorTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_TelemetrySensorType.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + SENSOR_UNSET: _TelemetrySensorType.ValueType # 0 + """ + No external telemetry sensor explicitly set + """ + BME280: _TelemetrySensorType.ValueType # 1 + """ + High accuracy temperature, pressure, humidity + """ + BME680: _TelemetrySensorType.ValueType # 2 + """ + High accuracy temperature, pressure, humidity, and air resistance + """ + MCP9808: _TelemetrySensorType.ValueType # 3 + """ + Very high accuracy temperature + """ + INA260: _TelemetrySensorType.ValueType # 4 + """ + Moderate accuracy current and voltage + """ + INA219: _TelemetrySensorType.ValueType # 5 + """ + Moderate accuracy current and voltage + """ + BMP280: _TelemetrySensorType.ValueType # 6 + """ + High accuracy temperature and pressure + """ + SHTC3: _TelemetrySensorType.ValueType # 7 + """ + High accuracy temperature and humidity + """ + LPS22: _TelemetrySensorType.ValueType # 8 + """ + High accuracy pressure + """ + QMC6310: _TelemetrySensorType.ValueType # 9 + """ + 3-Axis magnetic sensor + """ + QMI8658: _TelemetrySensorType.ValueType # 10 + """ + 6-Axis inertial measurement sensor + """ + QMC5883L: _TelemetrySensorType.ValueType # 11 + """ + 3-Axis magnetic sensor + """ + SHT31: _TelemetrySensorType.ValueType # 12 + """ + High accuracy temperature and humidity + """ + PMSA003I: _TelemetrySensorType.ValueType # 13 + """ + PM2.5 air quality sensor + """ + INA3221: _TelemetrySensorType.ValueType # 14 + """ + INA3221 3 Channel Voltage / Current Sensor + """ + BMP085: _TelemetrySensorType.ValueType # 15 + """ + BMP085/BMP180 High accuracy temperature and pressure (older Version of BMP280) + """ + RCWL9620: _TelemetrySensorType.ValueType # 16 + """ + RCWL-9620 Doppler Radar Distance Sensor, used for water level detection + """ + SHT4X: _TelemetrySensorType.ValueType # 17 + """ + Sensirion High accuracy temperature and humidity + """ + VEML7700: _TelemetrySensorType.ValueType # 18 + """ + VEML7700 high accuracy ambient light(Lux) digital 16-bit resolution sensor. + """ + MLX90632: _TelemetrySensorType.ValueType # 19 + """ + MLX90632 non-contact IR temperature sensor. + """ + OPT3001: _TelemetrySensorType.ValueType # 20 + """ + TI OPT3001 Ambient Light Sensor + """ + LTR390UV: _TelemetrySensorType.ValueType # 21 + """ + Lite On LTR-390UV-01 UV Light Sensor + """ + TSL25911FN: _TelemetrySensorType.ValueType # 22 + """ + AMS TSL25911FN RGB Light Sensor + """ + AHT10: _TelemetrySensorType.ValueType # 23 + """ + AHT10 Integrated temperature and humidity sensor + """ + DFROBOT_LARK: _TelemetrySensorType.ValueType # 24 + """ + DFRobot Lark Weather station (temperature, humidity, pressure, wind speed and direction) + """ + NAU7802: _TelemetrySensorType.ValueType # 25 + """ + NAU7802 Scale Chip or compatible + """ + BMP3XX: _TelemetrySensorType.ValueType # 26 + """ + BMP3XX High accuracy temperature and pressure + """ + ICM20948: _TelemetrySensorType.ValueType # 27 + """ + ICM-20948 9-Axis digital motion processor + """ + MAX17048: _TelemetrySensorType.ValueType # 28 + """ + MAX17048 1S lipo battery sensor (voltage, state of charge, time to go) + """ + CUSTOM_SENSOR: _TelemetrySensorType.ValueType # 29 + """ + Custom I2C sensor implementation based on https://github.com/meshtastic/i2c-sensor + """ + MAX30102: _TelemetrySensorType.ValueType # 30 + """ + MAX30102 Pulse Oximeter and Heart-Rate Sensor + """ + MLX90614: _TelemetrySensorType.ValueType # 31 + """ + MLX90614 non-contact IR temperature sensor + """ + SCD4X: _TelemetrySensorType.ValueType # 32 + """ + SCD40/SCD41 CO2, humidity, temperature sensor + """ + RADSENS: _TelemetrySensorType.ValueType # 33 + """ + ClimateGuard RadSens, radiation, Geiger-Muller Tube + """ + INA226: _TelemetrySensorType.ValueType # 34 + """ + High accuracy current and voltage + """ + DFROBOT_RAIN: _TelemetrySensorType.ValueType # 35 + """ + DFRobot Gravity tipping bucket rain gauge + """ + DPS310: _TelemetrySensorType.ValueType # 36 + """ + Infineon DPS310 High accuracy pressure and temperature + """ + RAK12035: _TelemetrySensorType.ValueType # 37 + """ + RAKWireless RAK12035 Soil Moisture Sensor Module + """ + MAX17261: _TelemetrySensorType.ValueType # 38 + """ + MAX17261 lipo battery gauge + """ + PCT2075: _TelemetrySensorType.ValueType # 39 + """ + PCT2075 Temperature Sensor + """ + ADS1X15: _TelemetrySensorType.ValueType # 40 + """ + ADS1X15 ADC + """ + ADS1X15_ALT: _TelemetrySensorType.ValueType # 41 + """ + ADS1X15 ADC_ALT + """ + SFA30: _TelemetrySensorType.ValueType # 42 + """ + Sensirion SFA30 Formaldehyde sensor + """ + SEN5X: _TelemetrySensorType.ValueType # 43 + """ + SEN5X PM SENSORS + """ + TSL2561: _TelemetrySensorType.ValueType # 44 + """ + TSL2561 light sensor + """ + BH1750: _TelemetrySensorType.ValueType # 45 + """ + BH1750 light sensor + """ + +class TelemetrySensorType(_TelemetrySensorType, metaclass=_TelemetrySensorTypeEnumTypeWrapper): + """ + Supported I2C Sensors for telemetry in Meshtastic + """ + +SENSOR_UNSET: TelemetrySensorType.ValueType # 0 +""" +No external telemetry sensor explicitly set +""" +BME280: TelemetrySensorType.ValueType # 1 +""" +High accuracy temperature, pressure, humidity +""" +BME680: TelemetrySensorType.ValueType # 2 +""" +High accuracy temperature, pressure, humidity, and air resistance +""" +MCP9808: TelemetrySensorType.ValueType # 3 +""" +Very high accuracy temperature +""" +INA260: TelemetrySensorType.ValueType # 4 +""" +Moderate accuracy current and voltage +""" +INA219: TelemetrySensorType.ValueType # 5 +""" +Moderate accuracy current and voltage +""" +BMP280: TelemetrySensorType.ValueType # 6 +""" +High accuracy temperature and pressure +""" +SHTC3: TelemetrySensorType.ValueType # 7 +""" +High accuracy temperature and humidity +""" +LPS22: TelemetrySensorType.ValueType # 8 +""" +High accuracy pressure +""" +QMC6310: TelemetrySensorType.ValueType # 9 +""" +3-Axis magnetic sensor +""" +QMI8658: TelemetrySensorType.ValueType # 10 +""" +6-Axis inertial measurement sensor +""" +QMC5883L: TelemetrySensorType.ValueType # 11 +""" +3-Axis magnetic sensor +""" +SHT31: TelemetrySensorType.ValueType # 12 +""" +High accuracy temperature and humidity +""" +PMSA003I: TelemetrySensorType.ValueType # 13 +""" +PM2.5 air quality sensor +""" +INA3221: TelemetrySensorType.ValueType # 14 +""" +INA3221 3 Channel Voltage / Current Sensor +""" +BMP085: TelemetrySensorType.ValueType # 15 +""" +BMP085/BMP180 High accuracy temperature and pressure (older Version of BMP280) +""" +RCWL9620: TelemetrySensorType.ValueType # 16 +""" +RCWL-9620 Doppler Radar Distance Sensor, used for water level detection +""" +SHT4X: TelemetrySensorType.ValueType # 17 +""" +Sensirion High accuracy temperature and humidity +""" +VEML7700: TelemetrySensorType.ValueType # 18 +""" +VEML7700 high accuracy ambient light(Lux) digital 16-bit resolution sensor. +""" +MLX90632: TelemetrySensorType.ValueType # 19 +""" +MLX90632 non-contact IR temperature sensor. +""" +OPT3001: TelemetrySensorType.ValueType # 20 +""" +TI OPT3001 Ambient Light Sensor +""" +LTR390UV: TelemetrySensorType.ValueType # 21 +""" +Lite On LTR-390UV-01 UV Light Sensor +""" +TSL25911FN: TelemetrySensorType.ValueType # 22 +""" +AMS TSL25911FN RGB Light Sensor +""" +AHT10: TelemetrySensorType.ValueType # 23 +""" +AHT10 Integrated temperature and humidity sensor +""" +DFROBOT_LARK: TelemetrySensorType.ValueType # 24 +""" +DFRobot Lark Weather station (temperature, humidity, pressure, wind speed and direction) +""" +NAU7802: TelemetrySensorType.ValueType # 25 +""" +NAU7802 Scale Chip or compatible +""" +BMP3XX: TelemetrySensorType.ValueType # 26 +""" +BMP3XX High accuracy temperature and pressure +""" +ICM20948: TelemetrySensorType.ValueType # 27 +""" +ICM-20948 9-Axis digital motion processor +""" +MAX17048: TelemetrySensorType.ValueType # 28 +""" +MAX17048 1S lipo battery sensor (voltage, state of charge, time to go) +""" +CUSTOM_SENSOR: TelemetrySensorType.ValueType # 29 +""" +Custom I2C sensor implementation based on https://github.com/meshtastic/i2c-sensor +""" +MAX30102: TelemetrySensorType.ValueType # 30 +""" +MAX30102 Pulse Oximeter and Heart-Rate Sensor +""" +MLX90614: TelemetrySensorType.ValueType # 31 +""" +MLX90614 non-contact IR temperature sensor +""" +SCD4X: TelemetrySensorType.ValueType # 32 +""" +SCD40/SCD41 CO2, humidity, temperature sensor +""" +RADSENS: TelemetrySensorType.ValueType # 33 +""" +ClimateGuard RadSens, radiation, Geiger-Muller Tube +""" +INA226: TelemetrySensorType.ValueType # 34 +""" +High accuracy current and voltage +""" +DFROBOT_RAIN: TelemetrySensorType.ValueType # 35 +""" +DFRobot Gravity tipping bucket rain gauge +""" +DPS310: TelemetrySensorType.ValueType # 36 +""" +Infineon DPS310 High accuracy pressure and temperature +""" +RAK12035: TelemetrySensorType.ValueType # 37 +""" +RAKWireless RAK12035 Soil Moisture Sensor Module +""" +MAX17261: TelemetrySensorType.ValueType # 38 +""" +MAX17261 lipo battery gauge +""" +PCT2075: TelemetrySensorType.ValueType # 39 +""" +PCT2075 Temperature Sensor +""" +ADS1X15: TelemetrySensorType.ValueType # 40 +""" +ADS1X15 ADC +""" +ADS1X15_ALT: TelemetrySensorType.ValueType # 41 +""" +ADS1X15 ADC_ALT +""" +SFA30: TelemetrySensorType.ValueType # 42 +""" +Sensirion SFA30 Formaldehyde sensor +""" +SEN5X: TelemetrySensorType.ValueType # 43 +""" +SEN5X PM SENSORS +""" +TSL2561: TelemetrySensorType.ValueType # 44 +""" +TSL2561 light sensor +""" +BH1750: TelemetrySensorType.ValueType # 45 +""" +BH1750 light sensor +""" +global___TelemetrySensorType = TelemetrySensorType + +@typing.final +class DeviceMetrics(google.protobuf.message.Message): + """ + Key native device metrics such as battery level + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + BATTERY_LEVEL_FIELD_NUMBER: builtins.int + VOLTAGE_FIELD_NUMBER: builtins.int + CHANNEL_UTILIZATION_FIELD_NUMBER: builtins.int + AIR_UTIL_TX_FIELD_NUMBER: builtins.int + UPTIME_SECONDS_FIELD_NUMBER: builtins.int + battery_level: builtins.int + """ + 0-100 (>100 means powered) + """ + voltage: builtins.float + """ + Voltage measured + """ + channel_utilization: builtins.float + """ + Utilization for the current channel, including well formed TX, RX and malformed RX (aka noise). + """ + air_util_tx: builtins.float + """ + Percent of airtime for transmission used within the last hour. + """ + uptime_seconds: builtins.int + """ + How long the device has been running since the last reboot (in seconds) + """ + def __init__( + self, + *, + battery_level: builtins.int | None = ..., + voltage: builtins.float | None = ..., + channel_utilization: builtins.float | None = ..., + air_util_tx: builtins.float | None = ..., + uptime_seconds: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["_air_util_tx", b"_air_util_tx", "_battery_level", b"_battery_level", "_channel_utilization", b"_channel_utilization", "_uptime_seconds", b"_uptime_seconds", "_voltage", b"_voltage", "air_util_tx", b"air_util_tx", "battery_level", b"battery_level", "channel_utilization", b"channel_utilization", "uptime_seconds", b"uptime_seconds", "voltage", b"voltage"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["_air_util_tx", b"_air_util_tx", "_battery_level", b"_battery_level", "_channel_utilization", b"_channel_utilization", "_uptime_seconds", b"_uptime_seconds", "_voltage", b"_voltage", "air_util_tx", b"air_util_tx", "battery_level", b"battery_level", "channel_utilization", b"channel_utilization", "uptime_seconds", b"uptime_seconds", "voltage", b"voltage"]) -> None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_air_util_tx", b"_air_util_tx"]) -> typing.Literal["air_util_tx"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_battery_level", b"_battery_level"]) -> typing.Literal["battery_level"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_channel_utilization", b"_channel_utilization"]) -> typing.Literal["channel_utilization"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_uptime_seconds", b"_uptime_seconds"]) -> typing.Literal["uptime_seconds"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_voltage", b"_voltage"]) -> typing.Literal["voltage"] | None: ... + +global___DeviceMetrics = DeviceMetrics + +@typing.final +class EnvironmentMetrics(google.protobuf.message.Message): + """ + Weather station or other environmental metrics + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + TEMPERATURE_FIELD_NUMBER: builtins.int + RELATIVE_HUMIDITY_FIELD_NUMBER: builtins.int + BAROMETRIC_PRESSURE_FIELD_NUMBER: builtins.int + GAS_RESISTANCE_FIELD_NUMBER: builtins.int + VOLTAGE_FIELD_NUMBER: builtins.int + CURRENT_FIELD_NUMBER: builtins.int + IAQ_FIELD_NUMBER: builtins.int + DISTANCE_FIELD_NUMBER: builtins.int + LUX_FIELD_NUMBER: builtins.int + WHITE_LUX_FIELD_NUMBER: builtins.int + IR_LUX_FIELD_NUMBER: builtins.int + UV_LUX_FIELD_NUMBER: builtins.int + WIND_DIRECTION_FIELD_NUMBER: builtins.int + WIND_SPEED_FIELD_NUMBER: builtins.int + WEIGHT_FIELD_NUMBER: builtins.int + WIND_GUST_FIELD_NUMBER: builtins.int + WIND_LULL_FIELD_NUMBER: builtins.int + RADIATION_FIELD_NUMBER: builtins.int + RAINFALL_1H_FIELD_NUMBER: builtins.int + RAINFALL_24H_FIELD_NUMBER: builtins.int + SOIL_MOISTURE_FIELD_NUMBER: builtins.int + SOIL_TEMPERATURE_FIELD_NUMBER: builtins.int + temperature: builtins.float + """ + Temperature measured + """ + relative_humidity: builtins.float + """ + Relative humidity percent measured + """ + barometric_pressure: builtins.float + """ + Barometric pressure in hPA measured + """ + gas_resistance: builtins.float + """ + Gas resistance in MOhm measured + """ + voltage: builtins.float + """ + Voltage measured (To be depreciated in favor of PowerMetrics in Meshtastic 3.x) + """ + current: builtins.float + """ + Current measured (To be depreciated in favor of PowerMetrics in Meshtastic 3.x) + """ + iaq: builtins.int + """ + relative scale IAQ value as measured by Bosch BME680 . value 0-500. + Belongs to Air Quality but is not particle but VOC measurement. Other VOC values can also be put in here. + """ + distance: builtins.float + """ + RCWL9620 Doppler Radar Distance Sensor, used for water level detection. Float value in mm. + """ + lux: builtins.float + """ + VEML7700 high accuracy ambient light(Lux) digital 16-bit resolution sensor. + """ + white_lux: builtins.float + """ + VEML7700 high accuracy white light(irradiance) not calibrated digital 16-bit resolution sensor. + """ + ir_lux: builtins.float + """ + Infrared lux + """ + uv_lux: builtins.float + """ + Ultraviolet lux + """ + wind_direction: builtins.int + """ + Wind direction in degrees + 0 degrees = North, 90 = East, etc... + """ + wind_speed: builtins.float + """ + Wind speed in m/s + """ + weight: builtins.float + """ + Weight in KG + """ + wind_gust: builtins.float + """ + Wind gust in m/s + """ + wind_lull: builtins.float + """ + Wind lull in m/s + """ + radiation: builtins.float + """ + Radiation in µR/h + """ + rainfall_1h: builtins.float + """ + Rainfall in the last hour in mm + """ + rainfall_24h: builtins.float + """ + Rainfall in the last 24 hours in mm + """ + soil_moisture: builtins.int + """ + Soil moisture measured (% 1-100) + """ + soil_temperature: builtins.float + """ + Soil temperature measured (*C) + """ + def __init__( + self, + *, + temperature: builtins.float | None = ..., + relative_humidity: builtins.float | None = ..., + barometric_pressure: builtins.float | None = ..., + gas_resistance: builtins.float | None = ..., + voltage: builtins.float | None = ..., + current: builtins.float | None = ..., + iaq: builtins.int | None = ..., + distance: builtins.float | None = ..., + lux: builtins.float | None = ..., + white_lux: builtins.float | None = ..., + ir_lux: builtins.float | None = ..., + uv_lux: builtins.float | None = ..., + wind_direction: builtins.int | None = ..., + wind_speed: builtins.float | None = ..., + weight: builtins.float | None = ..., + wind_gust: builtins.float | None = ..., + wind_lull: builtins.float | None = ..., + radiation: builtins.float | None = ..., + rainfall_1h: builtins.float | None = ..., + rainfall_24h: builtins.float | None = ..., + soil_moisture: builtins.int | None = ..., + soil_temperature: 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: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_barometric_pressure", b"_barometric_pressure"]) -> typing.Literal["barometric_pressure"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_current", b"_current"]) -> typing.Literal["current"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_distance", b"_distance"]) -> typing.Literal["distance"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_gas_resistance", b"_gas_resistance"]) -> typing.Literal["gas_resistance"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_iaq", b"_iaq"]) -> typing.Literal["iaq"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_ir_lux", b"_ir_lux"]) -> typing.Literal["ir_lux"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_lux", b"_lux"]) -> typing.Literal["lux"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_radiation", b"_radiation"]) -> typing.Literal["radiation"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_rainfall_1h", b"_rainfall_1h"]) -> typing.Literal["rainfall_1h"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_rainfall_24h", b"_rainfall_24h"]) -> typing.Literal["rainfall_24h"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_relative_humidity", b"_relative_humidity"]) -> typing.Literal["relative_humidity"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_soil_moisture", b"_soil_moisture"]) -> typing.Literal["soil_moisture"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_soil_temperature", b"_soil_temperature"]) -> typing.Literal["soil_temperature"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_temperature", b"_temperature"]) -> typing.Literal["temperature"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_uv_lux", b"_uv_lux"]) -> typing.Literal["uv_lux"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_voltage", b"_voltage"]) -> typing.Literal["voltage"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_weight", b"_weight"]) -> typing.Literal["weight"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_white_lux", b"_white_lux"]) -> typing.Literal["white_lux"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_wind_direction", b"_wind_direction"]) -> typing.Literal["wind_direction"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_wind_gust", b"_wind_gust"]) -> typing.Literal["wind_gust"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_wind_lull", b"_wind_lull"]) -> typing.Literal["wind_lull"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_wind_speed", b"_wind_speed"]) -> typing.Literal["wind_speed"] | None: ... + +global___EnvironmentMetrics = EnvironmentMetrics + +@typing.final +class PowerMetrics(google.protobuf.message.Message): + """ + Power Metrics (voltage / current / etc) + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + CH1_VOLTAGE_FIELD_NUMBER: builtins.int + CH1_CURRENT_FIELD_NUMBER: builtins.int + CH2_VOLTAGE_FIELD_NUMBER: builtins.int + CH2_CURRENT_FIELD_NUMBER: builtins.int + CH3_VOLTAGE_FIELD_NUMBER: builtins.int + CH3_CURRENT_FIELD_NUMBER: builtins.int + CH4_VOLTAGE_FIELD_NUMBER: builtins.int + CH4_CURRENT_FIELD_NUMBER: builtins.int + CH5_VOLTAGE_FIELD_NUMBER: builtins.int + CH5_CURRENT_FIELD_NUMBER: builtins.int + CH6_VOLTAGE_FIELD_NUMBER: builtins.int + CH6_CURRENT_FIELD_NUMBER: builtins.int + CH7_VOLTAGE_FIELD_NUMBER: builtins.int + CH7_CURRENT_FIELD_NUMBER: builtins.int + CH8_VOLTAGE_FIELD_NUMBER: builtins.int + CH8_CURRENT_FIELD_NUMBER: builtins.int + ch1_voltage: builtins.float + """ + Voltage (Ch1) + """ + ch1_current: builtins.float + """ + Current (Ch1) + """ + ch2_voltage: builtins.float + """ + Voltage (Ch2) + """ + ch2_current: builtins.float + """ + Current (Ch2) + """ + ch3_voltage: builtins.float + """ + Voltage (Ch3) + """ + ch3_current: builtins.float + """ + Current (Ch3) + """ + ch4_voltage: builtins.float + """ + Voltage (Ch4) + """ + ch4_current: builtins.float + """ + Current (Ch4) + """ + ch5_voltage: builtins.float + """ + Voltage (Ch5) + """ + ch5_current: builtins.float + """ + Current (Ch5) + """ + ch6_voltage: builtins.float + """ + Voltage (Ch6) + """ + ch6_current: builtins.float + """ + Current (Ch6) + """ + ch7_voltage: builtins.float + """ + Voltage (Ch7) + """ + ch7_current: builtins.float + """ + Current (Ch7) + """ + ch8_voltage: builtins.float + """ + Voltage (Ch8) + """ + ch8_current: builtins.float + """ + Current (Ch8) + """ + def __init__( + self, + *, + ch1_voltage: builtins.float | None = ..., + ch1_current: builtins.float | None = ..., + ch2_voltage: builtins.float | None = ..., + ch2_current: builtins.float | None = ..., + ch3_voltage: builtins.float | None = ..., + ch3_current: builtins.float | None = ..., + ch4_voltage: builtins.float | None = ..., + ch4_current: builtins.float | None = ..., + ch5_voltage: builtins.float | None = ..., + ch5_current: builtins.float | None = ..., + ch6_voltage: builtins.float | None = ..., + ch6_current: builtins.float | None = ..., + ch7_voltage: builtins.float | None = ..., + ch7_current: builtins.float | None = ..., + ch8_voltage: builtins.float | None = ..., + ch8_current: builtins.float | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["_ch1_current", b"_ch1_current", "_ch1_voltage", b"_ch1_voltage", "_ch2_current", b"_ch2_current", "_ch2_voltage", b"_ch2_voltage", "_ch3_current", b"_ch3_current", "_ch3_voltage", b"_ch3_voltage", "_ch4_current", b"_ch4_current", "_ch4_voltage", b"_ch4_voltage", "_ch5_current", b"_ch5_current", "_ch5_voltage", b"_ch5_voltage", "_ch6_current", b"_ch6_current", "_ch6_voltage", b"_ch6_voltage", "_ch7_current", b"_ch7_current", "_ch7_voltage", b"_ch7_voltage", "_ch8_current", b"_ch8_current", "_ch8_voltage", b"_ch8_voltage", "ch1_current", b"ch1_current", "ch1_voltage", b"ch1_voltage", "ch2_current", b"ch2_current", "ch2_voltage", b"ch2_voltage", "ch3_current", b"ch3_current", "ch3_voltage", b"ch3_voltage", "ch4_current", b"ch4_current", "ch4_voltage", b"ch4_voltage", "ch5_current", b"ch5_current", "ch5_voltage", b"ch5_voltage", "ch6_current", b"ch6_current", "ch6_voltage", b"ch6_voltage", "ch7_current", b"ch7_current", "ch7_voltage", b"ch7_voltage", "ch8_current", b"ch8_current", "ch8_voltage", b"ch8_voltage"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["_ch1_current", b"_ch1_current", "_ch1_voltage", b"_ch1_voltage", "_ch2_current", b"_ch2_current", "_ch2_voltage", b"_ch2_voltage", "_ch3_current", b"_ch3_current", "_ch3_voltage", b"_ch3_voltage", "_ch4_current", b"_ch4_current", "_ch4_voltage", b"_ch4_voltage", "_ch5_current", b"_ch5_current", "_ch5_voltage", b"_ch5_voltage", "_ch6_current", b"_ch6_current", "_ch6_voltage", b"_ch6_voltage", "_ch7_current", b"_ch7_current", "_ch7_voltage", b"_ch7_voltage", "_ch8_current", b"_ch8_current", "_ch8_voltage", b"_ch8_voltage", "ch1_current", b"ch1_current", "ch1_voltage", b"ch1_voltage", "ch2_current", b"ch2_current", "ch2_voltage", b"ch2_voltage", "ch3_current", b"ch3_current", "ch3_voltage", b"ch3_voltage", "ch4_current", b"ch4_current", "ch4_voltage", b"ch4_voltage", "ch5_current", b"ch5_current", "ch5_voltage", b"ch5_voltage", "ch6_current", b"ch6_current", "ch6_voltage", b"ch6_voltage", "ch7_current", b"ch7_current", "ch7_voltage", b"ch7_voltage", "ch8_current", b"ch8_current", "ch8_voltage", b"ch8_voltage"]) -> None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_ch1_current", b"_ch1_current"]) -> typing.Literal["ch1_current"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_ch1_voltage", b"_ch1_voltage"]) -> typing.Literal["ch1_voltage"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_ch2_current", b"_ch2_current"]) -> typing.Literal["ch2_current"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_ch2_voltage", b"_ch2_voltage"]) -> typing.Literal["ch2_voltage"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_ch3_current", b"_ch3_current"]) -> typing.Literal["ch3_current"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_ch3_voltage", b"_ch3_voltage"]) -> typing.Literal["ch3_voltage"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_ch4_current", b"_ch4_current"]) -> typing.Literal["ch4_current"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_ch4_voltage", b"_ch4_voltage"]) -> typing.Literal["ch4_voltage"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_ch5_current", b"_ch5_current"]) -> typing.Literal["ch5_current"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_ch5_voltage", b"_ch5_voltage"]) -> typing.Literal["ch5_voltage"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_ch6_current", b"_ch6_current"]) -> typing.Literal["ch6_current"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_ch6_voltage", b"_ch6_voltage"]) -> typing.Literal["ch6_voltage"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_ch7_current", b"_ch7_current"]) -> typing.Literal["ch7_current"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_ch7_voltage", b"_ch7_voltage"]) -> typing.Literal["ch7_voltage"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_ch8_current", b"_ch8_current"]) -> typing.Literal["ch8_current"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_ch8_voltage", b"_ch8_voltage"]) -> typing.Literal["ch8_voltage"] | None: ... + +global___PowerMetrics = PowerMetrics + +@typing.final +class AirQualityMetrics(google.protobuf.message.Message): + """ + Air quality metrics + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + PM10_STANDARD_FIELD_NUMBER: builtins.int + PM25_STANDARD_FIELD_NUMBER: builtins.int + PM100_STANDARD_FIELD_NUMBER: builtins.int + PM10_ENVIRONMENTAL_FIELD_NUMBER: builtins.int + PM25_ENVIRONMENTAL_FIELD_NUMBER: builtins.int + PM100_ENVIRONMENTAL_FIELD_NUMBER: builtins.int + PARTICLES_03UM_FIELD_NUMBER: builtins.int + PARTICLES_05UM_FIELD_NUMBER: builtins.int + PARTICLES_10UM_FIELD_NUMBER: builtins.int + PARTICLES_25UM_FIELD_NUMBER: builtins.int + PARTICLES_50UM_FIELD_NUMBER: builtins.int + PARTICLES_100UM_FIELD_NUMBER: builtins.int + CO2_FIELD_NUMBER: builtins.int + CO2_TEMPERATURE_FIELD_NUMBER: builtins.int + CO2_HUMIDITY_FIELD_NUMBER: builtins.int + FORM_FORMALDEHYDE_FIELD_NUMBER: builtins.int + FORM_HUMIDITY_FIELD_NUMBER: builtins.int + FORM_TEMPERATURE_FIELD_NUMBER: builtins.int + PM40_STANDARD_FIELD_NUMBER: builtins.int + PARTICLES_40UM_FIELD_NUMBER: builtins.int + PM_TEMPERATURE_FIELD_NUMBER: builtins.int + PM_HUMIDITY_FIELD_NUMBER: builtins.int + PM_VOC_IDX_FIELD_NUMBER: builtins.int + PM_NOX_IDX_FIELD_NUMBER: builtins.int + PARTICLES_TPS_FIELD_NUMBER: builtins.int + pm10_standard: builtins.int + """ + Concentration Units Standard PM1.0 in ug/m3 + """ + pm25_standard: builtins.int + """ + Concentration Units Standard PM2.5 in ug/m3 + """ + pm100_standard: builtins.int + """ + Concentration Units Standard PM10.0 in ug/m3 + """ + pm10_environmental: builtins.int + """ + Concentration Units Environmental PM1.0 in ug/m3 + """ + pm25_environmental: builtins.int + """ + Concentration Units Environmental PM2.5 in ug/m3 + """ + pm100_environmental: builtins.int + """ + Concentration Units Environmental PM10.0 in ug/m3 + """ + particles_03um: builtins.int + """ + 0.3um Particle Count in #/0.1l + """ + particles_05um: builtins.int + """ + 0.5um Particle Count in #/0.1l + """ + particles_10um: builtins.int + """ + 1.0um Particle Count in #/0.1l + """ + particles_25um: builtins.int + """ + 2.5um Particle Count in #/0.1l + """ + particles_50um: builtins.int + """ + 5.0um Particle Count in #/0.1l + """ + particles_100um: builtins.int + """ + 10.0um Particle Count in #/0.1l + """ + co2: builtins.int + """ + CO2 concentration in ppm + """ + co2_temperature: builtins.float + """ + CO2 sensor temperature in degC + """ + co2_humidity: builtins.float + """ + CO2 sensor relative humidity in % + """ + form_formaldehyde: builtins.float + """ + Formaldehyde sensor formaldehyde concentration in ppb + """ + form_humidity: builtins.float + """ + Formaldehyde sensor relative humidity in %RH + """ + form_temperature: builtins.float + """ + Formaldehyde sensor temperature in degrees Celsius + """ + pm40_standard: builtins.int + """ + Concentration Units Standard PM4.0 in ug/m3 + """ + particles_40um: builtins.int + """ + 4.0um Particle Count in #/0.1l + """ + pm_temperature: builtins.float + """ + PM Sensor Temperature + """ + pm_humidity: builtins.float + """ + PM Sensor humidity + """ + pm_voc_idx: builtins.float + """ + PM Sensor VOC Index + """ + pm_nox_idx: builtins.float + """ + PM Sensor NOx Index + """ + particles_tps: builtins.float + """ + Typical Particle Size in um + """ + def __init__( + self, + *, + pm10_standard: builtins.int | None = ..., + pm25_standard: builtins.int | None = ..., + pm100_standard: builtins.int | None = ..., + pm10_environmental: builtins.int | None = ..., + pm25_environmental: builtins.int | None = ..., + pm100_environmental: builtins.int | None = ..., + particles_03um: builtins.int | None = ..., + particles_05um: builtins.int | None = ..., + particles_10um: builtins.int | None = ..., + particles_25um: builtins.int | None = ..., + particles_50um: builtins.int | None = ..., + particles_100um: builtins.int | None = ..., + co2: builtins.int | None = ..., + co2_temperature: builtins.float | None = ..., + co2_humidity: builtins.float | None = ..., + form_formaldehyde: builtins.float | None = ..., + form_humidity: builtins.float | None = ..., + form_temperature: builtins.float | None = ..., + pm40_standard: builtins.int | None = ..., + particles_40um: builtins.int | None = ..., + pm_temperature: builtins.float | None = ..., + pm_humidity: builtins.float | None = ..., + pm_voc_idx: builtins.float | None = ..., + pm_nox_idx: builtins.float | None = ..., + particles_tps: builtins.float | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["_co2", b"_co2", "_co2_humidity", b"_co2_humidity", "_co2_temperature", b"_co2_temperature", "_form_formaldehyde", b"_form_formaldehyde", "_form_humidity", b"_form_humidity", "_form_temperature", b"_form_temperature", "_particles_03um", b"_particles_03um", "_particles_05um", b"_particles_05um", "_particles_100um", b"_particles_100um", "_particles_10um", b"_particles_10um", "_particles_25um", b"_particles_25um", "_particles_40um", b"_particles_40um", "_particles_50um", b"_particles_50um", "_particles_tps", b"_particles_tps", "_pm100_environmental", b"_pm100_environmental", "_pm100_standard", b"_pm100_standard", "_pm10_environmental", b"_pm10_environmental", "_pm10_standard", b"_pm10_standard", "_pm25_environmental", b"_pm25_environmental", "_pm25_standard", b"_pm25_standard", "_pm40_standard", b"_pm40_standard", "_pm_humidity", b"_pm_humidity", "_pm_nox_idx", b"_pm_nox_idx", "_pm_temperature", b"_pm_temperature", "_pm_voc_idx", b"_pm_voc_idx", "co2", b"co2", "co2_humidity", b"co2_humidity", "co2_temperature", b"co2_temperature", "form_formaldehyde", b"form_formaldehyde", "form_humidity", b"form_humidity", "form_temperature", b"form_temperature", "particles_03um", b"particles_03um", "particles_05um", b"particles_05um", "particles_100um", b"particles_100um", "particles_10um", b"particles_10um", "particles_25um", b"particles_25um", "particles_40um", b"particles_40um", "particles_50um", b"particles_50um", "particles_tps", b"particles_tps", "pm100_environmental", b"pm100_environmental", "pm100_standard", b"pm100_standard", "pm10_environmental", b"pm10_environmental", "pm10_standard", b"pm10_standard", "pm25_environmental", b"pm25_environmental", "pm25_standard", b"pm25_standard", "pm40_standard", b"pm40_standard", "pm_humidity", b"pm_humidity", "pm_nox_idx", b"pm_nox_idx", "pm_temperature", b"pm_temperature", "pm_voc_idx", b"pm_voc_idx"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["_co2", b"_co2", "_co2_humidity", b"_co2_humidity", "_co2_temperature", b"_co2_temperature", "_form_formaldehyde", b"_form_formaldehyde", "_form_humidity", b"_form_humidity", "_form_temperature", b"_form_temperature", "_particles_03um", b"_particles_03um", "_particles_05um", b"_particles_05um", "_particles_100um", b"_particles_100um", "_particles_10um", b"_particles_10um", "_particles_25um", b"_particles_25um", "_particles_40um", b"_particles_40um", "_particles_50um", b"_particles_50um", "_particles_tps", b"_particles_tps", "_pm100_environmental", b"_pm100_environmental", "_pm100_standard", b"_pm100_standard", "_pm10_environmental", b"_pm10_environmental", "_pm10_standard", b"_pm10_standard", "_pm25_environmental", b"_pm25_environmental", "_pm25_standard", b"_pm25_standard", "_pm40_standard", b"_pm40_standard", "_pm_humidity", b"_pm_humidity", "_pm_nox_idx", b"_pm_nox_idx", "_pm_temperature", b"_pm_temperature", "_pm_voc_idx", b"_pm_voc_idx", "co2", b"co2", "co2_humidity", b"co2_humidity", "co2_temperature", b"co2_temperature", "form_formaldehyde", b"form_formaldehyde", "form_humidity", b"form_humidity", "form_temperature", b"form_temperature", "particles_03um", b"particles_03um", "particles_05um", b"particles_05um", "particles_100um", b"particles_100um", "particles_10um", b"particles_10um", "particles_25um", b"particles_25um", "particles_40um", b"particles_40um", "particles_50um", b"particles_50um", "particles_tps", b"particles_tps", "pm100_environmental", b"pm100_environmental", "pm100_standard", b"pm100_standard", "pm10_environmental", b"pm10_environmental", "pm10_standard", b"pm10_standard", "pm25_environmental", b"pm25_environmental", "pm25_standard", b"pm25_standard", "pm40_standard", b"pm40_standard", "pm_humidity", b"pm_humidity", "pm_nox_idx", b"pm_nox_idx", "pm_temperature", b"pm_temperature", "pm_voc_idx", b"pm_voc_idx"]) -> None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_co2", b"_co2"]) -> typing.Literal["co2"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_co2_humidity", b"_co2_humidity"]) -> typing.Literal["co2_humidity"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_co2_temperature", b"_co2_temperature"]) -> typing.Literal["co2_temperature"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_form_formaldehyde", b"_form_formaldehyde"]) -> typing.Literal["form_formaldehyde"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_form_humidity", b"_form_humidity"]) -> typing.Literal["form_humidity"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_form_temperature", b"_form_temperature"]) -> typing.Literal["form_temperature"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_particles_03um", b"_particles_03um"]) -> typing.Literal["particles_03um"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_particles_05um", b"_particles_05um"]) -> typing.Literal["particles_05um"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_particles_100um", b"_particles_100um"]) -> typing.Literal["particles_100um"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_particles_10um", b"_particles_10um"]) -> typing.Literal["particles_10um"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_particles_25um", b"_particles_25um"]) -> typing.Literal["particles_25um"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_particles_40um", b"_particles_40um"]) -> typing.Literal["particles_40um"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_particles_50um", b"_particles_50um"]) -> typing.Literal["particles_50um"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_particles_tps", b"_particles_tps"]) -> typing.Literal["particles_tps"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_pm100_environmental", b"_pm100_environmental"]) -> typing.Literal["pm100_environmental"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_pm100_standard", b"_pm100_standard"]) -> typing.Literal["pm100_standard"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_pm10_environmental", b"_pm10_environmental"]) -> typing.Literal["pm10_environmental"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_pm10_standard", b"_pm10_standard"]) -> typing.Literal["pm10_standard"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_pm25_environmental", b"_pm25_environmental"]) -> typing.Literal["pm25_environmental"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_pm25_standard", b"_pm25_standard"]) -> typing.Literal["pm25_standard"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_pm40_standard", b"_pm40_standard"]) -> typing.Literal["pm40_standard"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_pm_humidity", b"_pm_humidity"]) -> typing.Literal["pm_humidity"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_pm_nox_idx", b"_pm_nox_idx"]) -> typing.Literal["pm_nox_idx"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_pm_temperature", b"_pm_temperature"]) -> typing.Literal["pm_temperature"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_pm_voc_idx", b"_pm_voc_idx"]) -> typing.Literal["pm_voc_idx"] | None: ... + +global___AirQualityMetrics = AirQualityMetrics + +@typing.final +class LocalStats(google.protobuf.message.Message): + """ + Local device mesh statistics + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + UPTIME_SECONDS_FIELD_NUMBER: builtins.int + CHANNEL_UTILIZATION_FIELD_NUMBER: builtins.int + AIR_UTIL_TX_FIELD_NUMBER: builtins.int + NUM_PACKETS_TX_FIELD_NUMBER: builtins.int + NUM_PACKETS_RX_FIELD_NUMBER: builtins.int + NUM_PACKETS_RX_BAD_FIELD_NUMBER: builtins.int + NUM_ONLINE_NODES_FIELD_NUMBER: builtins.int + NUM_TOTAL_NODES_FIELD_NUMBER: builtins.int + NUM_RX_DUPE_FIELD_NUMBER: builtins.int + NUM_TX_RELAY_FIELD_NUMBER: builtins.int + NUM_TX_RELAY_CANCELED_FIELD_NUMBER: builtins.int + HEAP_TOTAL_BYTES_FIELD_NUMBER: builtins.int + HEAP_FREE_BYTES_FIELD_NUMBER: builtins.int + NUM_TX_DROPPED_FIELD_NUMBER: builtins.int + uptime_seconds: builtins.int + """ + How long the device has been running since the last reboot (in seconds) + """ + channel_utilization: builtins.float + """ + Utilization for the current channel, including well formed TX, RX and malformed RX (aka noise). + """ + air_util_tx: builtins.float + """ + Percent of airtime for transmission used within the last hour. + """ + num_packets_tx: builtins.int + """ + Number of packets sent + """ + num_packets_rx: builtins.int + """ + Number of packets received (both good and bad) + """ + num_packets_rx_bad: builtins.int + """ + Number of packets received that are malformed or violate the protocol + """ + num_online_nodes: builtins.int + """ + Number of nodes online (in the past 2 hours) + """ + num_total_nodes: builtins.int + """ + Number of nodes total + """ + num_rx_dupe: builtins.int + """ + Number of received packets that were duplicates (due to multiple nodes relaying). + If this number is high, there are nodes in the mesh relaying packets when it's unnecessary, for example due to the ROUTER/REPEATER role. + """ + num_tx_relay: builtins.int + """ + Number of packets we transmitted that were a relay for others (not originating from ourselves). + """ + num_tx_relay_canceled: builtins.int + """ + Number of times we canceled a packet to be relayed, because someone else did it before us. + This will always be zero for ROUTERs/REPEATERs. If this number is high, some other node(s) is/are relaying faster than you. + """ + heap_total_bytes: builtins.int + """ + Number of bytes used in the heap + """ + heap_free_bytes: builtins.int + """ + Number of bytes free in the heap + """ + num_tx_dropped: builtins.int + """ + Number of packets that were dropped because the transmit queue was full. + """ + def __init__( + self, + *, + uptime_seconds: builtins.int = ..., + channel_utilization: builtins.float = ..., + air_util_tx: builtins.float = ..., + num_packets_tx: builtins.int = ..., + num_packets_rx: builtins.int = ..., + num_packets_rx_bad: builtins.int = ..., + num_online_nodes: builtins.int = ..., + num_total_nodes: builtins.int = ..., + num_rx_dupe: builtins.int = ..., + num_tx_relay: builtins.int = ..., + num_tx_relay_canceled: builtins.int = ..., + heap_total_bytes: builtins.int = ..., + heap_free_bytes: builtins.int = ..., + num_tx_dropped: builtins.int = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["air_util_tx", b"air_util_tx", "channel_utilization", b"channel_utilization", "heap_free_bytes", b"heap_free_bytes", "heap_total_bytes", b"heap_total_bytes", "num_online_nodes", b"num_online_nodes", "num_packets_rx", b"num_packets_rx", "num_packets_rx_bad", b"num_packets_rx_bad", "num_packets_tx", b"num_packets_tx", "num_rx_dupe", b"num_rx_dupe", "num_total_nodes", b"num_total_nodes", "num_tx_dropped", b"num_tx_dropped", "num_tx_relay", b"num_tx_relay", "num_tx_relay_canceled", b"num_tx_relay_canceled", "uptime_seconds", b"uptime_seconds"]) -> None: ... + +global___LocalStats = LocalStats + +@typing.final +class HealthMetrics(google.protobuf.message.Message): + """ + Health telemetry metrics + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + HEART_BPM_FIELD_NUMBER: builtins.int + SPO2_FIELD_NUMBER: builtins.int + TEMPERATURE_FIELD_NUMBER: builtins.int + heart_bpm: builtins.int + """ + Heart rate (beats per minute) + """ + spO2: builtins.int + """ + SpO2 (blood oxygen saturation) level + """ + temperature: builtins.float + """ + Body temperature in degrees Celsius + """ + def __init__( + self, + *, + heart_bpm: builtins.int | None = ..., + spO2: builtins.int | None = ..., + temperature: builtins.float | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["_heart_bpm", b"_heart_bpm", "_spO2", b"_spO2", "_temperature", b"_temperature", "heart_bpm", b"heart_bpm", "spO2", b"spO2", "temperature", b"temperature"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["_heart_bpm", b"_heart_bpm", "_spO2", b"_spO2", "_temperature", b"_temperature", "heart_bpm", b"heart_bpm", "spO2", b"spO2", "temperature", b"temperature"]) -> None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_heart_bpm", b"_heart_bpm"]) -> typing.Literal["heart_bpm"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_spO2", b"_spO2"]) -> typing.Literal["spO2"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_temperature", b"_temperature"]) -> typing.Literal["temperature"] | None: ... + +global___HealthMetrics = HealthMetrics + +@typing.final +class HostMetrics(google.protobuf.message.Message): + """ + Linux host metrics + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + UPTIME_SECONDS_FIELD_NUMBER: builtins.int + FREEMEM_BYTES_FIELD_NUMBER: builtins.int + DISKFREE1_BYTES_FIELD_NUMBER: builtins.int + DISKFREE2_BYTES_FIELD_NUMBER: builtins.int + DISKFREE3_BYTES_FIELD_NUMBER: builtins.int + LOAD1_FIELD_NUMBER: builtins.int + LOAD5_FIELD_NUMBER: builtins.int + LOAD15_FIELD_NUMBER: builtins.int + USER_STRING_FIELD_NUMBER: builtins.int + uptime_seconds: builtins.int + """ + Host system uptime + """ + freemem_bytes: builtins.int + """ + Host system free memory + """ + diskfree1_bytes: builtins.int + """ + Host system disk space free for / + """ + diskfree2_bytes: builtins.int + """ + Secondary system disk space free + """ + diskfree3_bytes: builtins.int + """ + Tertiary disk space free + """ + load1: builtins.int + """ + Host system one minute load in 1/100ths + """ + load5: builtins.int + """ + Host system five minute load in 1/100ths + """ + load15: builtins.int + """ + Host system fifteen minute load in 1/100ths + """ + user_string: builtins.str + """ + Optional User-provided string for arbitrary host system information + that doesn't make sense as a dedicated entry. + """ + def __init__( + self, + *, + uptime_seconds: builtins.int = ..., + freemem_bytes: builtins.int = ..., + diskfree1_bytes: builtins.int = ..., + diskfree2_bytes: builtins.int | None = ..., + diskfree3_bytes: builtins.int | None = ..., + load1: builtins.int = ..., + load5: builtins.int = ..., + load15: builtins.int = ..., + user_string: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["_diskfree2_bytes", b"_diskfree2_bytes", "_diskfree3_bytes", b"_diskfree3_bytes", "_user_string", b"_user_string", "diskfree2_bytes", b"diskfree2_bytes", "diskfree3_bytes", b"diskfree3_bytes", "user_string", b"user_string"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["_diskfree2_bytes", b"_diskfree2_bytes", "_diskfree3_bytes", b"_diskfree3_bytes", "_user_string", b"_user_string", "diskfree1_bytes", b"diskfree1_bytes", "diskfree2_bytes", b"diskfree2_bytes", "diskfree3_bytes", b"diskfree3_bytes", "freemem_bytes", b"freemem_bytes", "load1", b"load1", "load15", b"load15", "load5", b"load5", "uptime_seconds", b"uptime_seconds", "user_string", b"user_string"]) -> None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_diskfree2_bytes", b"_diskfree2_bytes"]) -> typing.Literal["diskfree2_bytes"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_diskfree3_bytes", b"_diskfree3_bytes"]) -> typing.Literal["diskfree3_bytes"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal["_user_string", b"_user_string"]) -> typing.Literal["user_string"] | None: ... + +global___HostMetrics = HostMetrics + +@typing.final +class Telemetry(google.protobuf.message.Message): + """ + Types of Measurements the telemetry module is equipped to handle + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + TIME_FIELD_NUMBER: builtins.int + DEVICE_METRICS_FIELD_NUMBER: builtins.int + ENVIRONMENT_METRICS_FIELD_NUMBER: builtins.int + AIR_QUALITY_METRICS_FIELD_NUMBER: builtins.int + POWER_METRICS_FIELD_NUMBER: builtins.int + LOCAL_STATS_FIELD_NUMBER: builtins.int + HEALTH_METRICS_FIELD_NUMBER: builtins.int + HOST_METRICS_FIELD_NUMBER: builtins.int + time: builtins.int + """ + Seconds since 1970 - or 0 for unknown/unset + """ + @property + def device_metrics(self) -> global___DeviceMetrics: + """ + Key native device metrics such as battery level + """ + + @property + def environment_metrics(self) -> global___EnvironmentMetrics: + """ + Weather station or other environmental metrics + """ + + @property + def air_quality_metrics(self) -> global___AirQualityMetrics: + """ + Air quality metrics + """ + + @property + def power_metrics(self) -> global___PowerMetrics: + """ + Power Metrics + """ + + @property + def local_stats(self) -> global___LocalStats: + """ + Local device mesh statistics + """ + + @property + def health_metrics(self) -> global___HealthMetrics: + """ + Health telemetry metrics + """ + + @property + def host_metrics(self) -> global___HostMetrics: + """ + Linux host metrics + """ + + def __init__( + self, + *, + time: builtins.int = ..., + device_metrics: global___DeviceMetrics | None = ..., + environment_metrics: global___EnvironmentMetrics | None = ..., + air_quality_metrics: global___AirQualityMetrics | None = ..., + power_metrics: global___PowerMetrics | None = ..., + local_stats: global___LocalStats | None = ..., + health_metrics: global___HealthMetrics | None = ..., + host_metrics: global___HostMetrics | 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: ... + +global___Telemetry = Telemetry + +@typing.final +class Nau7802Config(google.protobuf.message.Message): + """ + NAU7802 Telemetry configuration, for saving to flash + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ZEROOFFSET_FIELD_NUMBER: builtins.int + CALIBRATIONFACTOR_FIELD_NUMBER: builtins.int + zeroOffset: builtins.int + """ + The offset setting for the NAU7802 + """ + calibrationFactor: builtins.float + """ + The calibration factor for the NAU7802 + """ + def __init__( + self, + *, + zeroOffset: builtins.int = ..., + calibrationFactor: builtins.float = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["calibrationFactor", b"calibrationFactor", "zeroOffset", b"zeroOffset"]) -> None: ... + +global___Nau7802Config = Nau7802Config diff --git a/meshtastic/protobuf/xmodem_pb2.py b/meshtastic/protobuf/xmodem_pb2.py new file mode 100644 index 00000000..3c3dec26 --- /dev/null +++ b/meshtastic/protobuf/xmodem_pb2.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: meshtastic/protobuf/xmodem.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n meshtastic/protobuf/xmodem.proto\x12\x13meshtastic.protobuf\"\xbf\x01\n\x06XModem\x12\x34\n\x07\x63ontrol\x18\x01 \x01(\x0e\x32#.meshtastic.protobuf.XModem.Control\x12\x0b\n\x03seq\x18\x02 \x01(\r\x12\r\n\x05\x63rc16\x18\x03 \x01(\r\x12\x0e\n\x06\x62uffer\x18\x04 \x01(\x0c\"S\n\x07\x43ontrol\x12\x07\n\x03NUL\x10\x00\x12\x07\n\x03SOH\x10\x01\x12\x07\n\x03STX\x10\x02\x12\x07\n\x03\x45OT\x10\x04\x12\x07\n\x03\x41\x43K\x10\x06\x12\x07\n\x03NAK\x10\x15\x12\x07\n\x03\x43\x41N\x10\x18\x12\t\n\x05\x43TRLZ\x10\x1a\x42\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) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.xmodem_pb2', _globals) +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 +# @@protoc_insertion_point(module_scope) diff --git a/meshtastic/protobuf/xmodem_pb2.pyi b/meshtastic/protobuf/xmodem_pb2.pyi new file mode 100644 index 00000000..d362ad0f --- /dev/null +++ b/meshtastic/protobuf/xmodem_pb2.pyi @@ -0,0 +1,67 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" + +import builtins +import google.protobuf.descriptor +import google.protobuf.internal.enum_type_wrapper +import google.protobuf.message +import sys +import typing + +if sys.version_info >= (3, 10): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing.final +class XModem(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + class _Control: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + + class _ControlEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[XModem._Control.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + NUL: XModem._Control.ValueType # 0 + SOH: XModem._Control.ValueType # 1 + STX: XModem._Control.ValueType # 2 + EOT: XModem._Control.ValueType # 4 + ACK: XModem._Control.ValueType # 6 + NAK: XModem._Control.ValueType # 21 + CAN: XModem._Control.ValueType # 24 + CTRLZ: XModem._Control.ValueType # 26 + + class Control(_Control, metaclass=_ControlEnumTypeWrapper): ... + NUL: XModem.Control.ValueType # 0 + SOH: XModem.Control.ValueType # 1 + STX: XModem.Control.ValueType # 2 + EOT: XModem.Control.ValueType # 4 + ACK: XModem.Control.ValueType # 6 + NAK: XModem.Control.ValueType # 21 + CAN: XModem.Control.ValueType # 24 + CTRLZ: XModem.Control.ValueType # 26 + + CONTROL_FIELD_NUMBER: builtins.int + SEQ_FIELD_NUMBER: builtins.int + CRC16_FIELD_NUMBER: builtins.int + BUFFER_FIELD_NUMBER: builtins.int + control: global___XModem.Control.ValueType + seq: builtins.int + crc16: builtins.int + buffer: builtins.bytes + def __init__( + self, + *, + control: global___XModem.Control.ValueType = ..., + seq: builtins.int = ..., + crc16: builtins.int = ..., + buffer: builtins.bytes = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["buffer", b"buffer", "control", b"control", "crc16", b"crc16", "seq", b"seq"]) -> None: ... + +global___XModem = XModem diff --git a/meshtastic/remote_hardware_pb2.py b/meshtastic/remote_hardware_pb2.py deleted file mode 100644 index c1365ff2..00000000 --- a/meshtastic/remote_hardware_pb2.py +++ /dev/null @@ -1,28 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: meshtastic/remote_hardware.proto -"""Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder -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 -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - - - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n meshtastic/remote_hardware.proto\x12\nmeshtastic\"\xd6\x01\n\x0fHardwareMessage\x12.\n\x04type\x18\x01 \x01(\x0e\x32 .meshtastic.HardwareMessage.Type\x12\x11\n\tgpio_mask\x18\x02 \x01(\x04\x12\x12\n\ngpio_value\x18\x03 \x01(\x04\"l\n\x04Type\x12\t\n\x05UNSET\x10\x00\x12\x0f\n\x0bWRITE_GPIOS\x10\x01\x12\x0f\n\x0bWATCH_GPIOS\x10\x02\x12\x11\n\rGPIOS_CHANGED\x10\x03\x12\x0e\n\nREAD_GPIOS\x10\x04\x12\x14\n\x10READ_GPIOS_REPLY\x10\x05\x42\x63\n\x13\x63om.geeksville.meshB\x0eRemoteHardwareZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') - -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.remote_hardware_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\016RemoteHardwareZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' - _HARDWAREMESSAGE._serialized_start=49 - _HARDWAREMESSAGE._serialized_end=263 - _HARDWAREMESSAGE_TYPE._serialized_start=155 - _HARDWAREMESSAGE_TYPE._serialized_end=263 -# @@protoc_insertion_point(module_scope) diff --git a/meshtastic/rtttl_pb2.py b/meshtastic/rtttl_pb2.py deleted file mode 100644 index a69b8681..00000000 --- a/meshtastic/rtttl_pb2.py +++ /dev/null @@ -1,26 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: meshtastic/rtttl.proto -"""Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder -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 -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - - - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16meshtastic/rtttl.proto\x12\nmeshtastic\"\x1f\n\x0bRTTTLConfig\x12\x10\n\x08ringtone\x18\x01 \x01(\tBf\n\x13\x63om.geeksville.meshB\x11RTTTLConfigProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') - -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.rtttl_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\021RTTTLConfigProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' - _RTTTLCONFIG._serialized_start=38 - _RTTTLCONFIG._serialized_end=69 -# @@protoc_insertion_point(module_scope) diff --git a/meshtastic/storeforward_pb2.py b/meshtastic/storeforward_pb2.py deleted file mode 100644 index b81fd730..00000000 --- a/meshtastic/storeforward_pb2.py +++ /dev/null @@ -1,34 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: meshtastic/storeforward.proto -"""Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder -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 -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - - - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1dmeshtastic/storeforward.proto\x12\nmeshtastic\"\x9c\x07\n\x0fStoreAndForward\x12\x37\n\x02rr\x18\x01 \x01(\x0e\x32+.meshtastic.StoreAndForward.RequestResponse\x12\x37\n\x05stats\x18\x02 \x01(\x0b\x32&.meshtastic.StoreAndForward.StatisticsH\x00\x12\x36\n\x07history\x18\x03 \x01(\x0b\x32#.meshtastic.StoreAndForward.HistoryH\x00\x12:\n\theartbeat\x18\x04 \x01(\x0b\x32%.meshtastic.StoreAndForward.HeartbeatH\x00\x12\x0e\n\x04text\x18\x05 \x01(\x0cH\x00\x1a\xcd\x01\n\nStatistics\x12\x16\n\x0emessages_total\x18\x01 \x01(\r\x12\x16\n\x0emessages_saved\x18\x02 \x01(\r\x12\x14\n\x0cmessages_max\x18\x03 \x01(\r\x12\x0f\n\x07up_time\x18\x04 \x01(\r\x12\x10\n\x08requests\x18\x05 \x01(\r\x12\x18\n\x10requests_history\x18\x06 \x01(\r\x12\x11\n\theartbeat\x18\x07 \x01(\x08\x12\x12\n\nreturn_max\x18\x08 \x01(\r\x12\x15\n\rreturn_window\x18\t \x01(\r\x1aI\n\x07History\x12\x18\n\x10history_messages\x18\x01 \x01(\r\x12\x0e\n\x06window\x18\x02 \x01(\r\x12\x14\n\x0clast_request\x18\x03 \x01(\r\x1a.\n\tHeartbeat\x12\x0e\n\x06period\x18\x01 \x01(\r\x12\x11\n\tsecondary\x18\x02 \x01(\r\"\xbc\x02\n\x0fRequestResponse\x12\t\n\x05UNSET\x10\x00\x12\x10\n\x0cROUTER_ERROR\x10\x01\x12\x14\n\x10ROUTER_HEARTBEAT\x10\x02\x12\x0f\n\x0bROUTER_PING\x10\x03\x12\x0f\n\x0bROUTER_PONG\x10\x04\x12\x0f\n\x0bROUTER_BUSY\x10\x05\x12\x12\n\x0eROUTER_HISTORY\x10\x06\x12\x10\n\x0cROUTER_STATS\x10\x07\x12\x16\n\x12ROUTER_TEXT_DIRECT\x10\x08\x12\x19\n\x15ROUTER_TEXT_BROADCAST\x10\t\x12\x10\n\x0c\x43LIENT_ERROR\x10@\x12\x12\n\x0e\x43LIENT_HISTORY\x10\x41\x12\x10\n\x0c\x43LIENT_STATS\x10\x42\x12\x0f\n\x0b\x43LIENT_PING\x10\x43\x12\x0f\n\x0b\x43LIENT_PONG\x10\x44\x12\x10\n\x0c\x43LIENT_ABORT\x10jB\t\n\x07variantBj\n\x13\x63om.geeksville.meshB\x15StoreAndForwardProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') - -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.storeforward_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\025StoreAndForwardProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' - _STOREANDFORWARD._serialized_start=46 - _STOREANDFORWARD._serialized_end=970 - _STOREANDFORWARD_STATISTICS._serialized_start=312 - _STOREANDFORWARD_STATISTICS._serialized_end=517 - _STOREANDFORWARD_HISTORY._serialized_start=519 - _STOREANDFORWARD_HISTORY._serialized_end=592 - _STOREANDFORWARD_HEARTBEAT._serialized_start=594 - _STOREANDFORWARD_HEARTBEAT._serialized_end=640 - _STOREANDFORWARD_REQUESTRESPONSE._serialized_start=643 - _STOREANDFORWARD_REQUESTRESPONSE._serialized_end=959 -# @@protoc_insertion_point(module_scope) diff --git a/meshtastic/telemetry_pb2.py b/meshtastic/telemetry_pb2.py deleted file mode 100644 index ef360fa2..00000000 --- a/meshtastic/telemetry_pb2.py +++ /dev/null @@ -1,40 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: meshtastic/telemetry.proto -"""Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder -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 -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - - - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1ameshtastic/telemetry.proto\x12\nmeshtastic\"\xf3\x01\n\rDeviceMetrics\x12\x1a\n\rbattery_level\x18\x01 \x01(\rH\x00\x88\x01\x01\x12\x14\n\x07voltage\x18\x02 \x01(\x02H\x01\x88\x01\x01\x12 \n\x13\x63hannel_utilization\x18\x03 \x01(\x02H\x02\x88\x01\x01\x12\x18\n\x0b\x61ir_util_tx\x18\x04 \x01(\x02H\x03\x88\x01\x01\x12\x1b\n\x0euptime_seconds\x18\x05 \x01(\rH\x04\x88\x01\x01\x42\x10\n\x0e_battery_levelB\n\n\x08_voltageB\x16\n\x14_channel_utilizationB\x0e\n\x0c_air_util_txB\x11\n\x0f_uptime_seconds\"\xa4\x05\n\x12\x45nvironmentMetrics\x12\x18\n\x0btemperature\x18\x01 \x01(\x02H\x00\x88\x01\x01\x12\x1e\n\x11relative_humidity\x18\x02 \x01(\x02H\x01\x88\x01\x01\x12 \n\x13\x62\x61rometric_pressure\x18\x03 \x01(\x02H\x02\x88\x01\x01\x12\x1b\n\x0egas_resistance\x18\x04 \x01(\x02H\x03\x88\x01\x01\x12\x14\n\x07voltage\x18\x05 \x01(\x02H\x04\x88\x01\x01\x12\x14\n\x07\x63urrent\x18\x06 \x01(\x02H\x05\x88\x01\x01\x12\x10\n\x03iaq\x18\x07 \x01(\rH\x06\x88\x01\x01\x12\x15\n\x08\x64istance\x18\x08 \x01(\x02H\x07\x88\x01\x01\x12\x10\n\x03lux\x18\t \x01(\x02H\x08\x88\x01\x01\x12\x16\n\twhite_lux\x18\n \x01(\x02H\t\x88\x01\x01\x12\x13\n\x06ir_lux\x18\x0b \x01(\x02H\n\x88\x01\x01\x12\x13\n\x06uv_lux\x18\x0c \x01(\x02H\x0b\x88\x01\x01\x12\x1b\n\x0ewind_direction\x18\r \x01(\rH\x0c\x88\x01\x01\x12\x17\n\nwind_speed\x18\x0e \x01(\x02H\r\x88\x01\x01\x12\x13\n\x06weight\x18\x0f \x01(\x02H\x0e\x88\x01\x01\x12\x16\n\twind_gust\x18\x10 \x01(\x02H\x0f\x88\x01\x01\x12\x16\n\twind_lull\x18\x11 \x01(\x02H\x10\x88\x01\x01\x42\x0e\n\x0c_temperatureB\x14\n\x12_relative_humidityB\x16\n\x14_barometric_pressureB\x11\n\x0f_gas_resistanceB\n\n\x08_voltageB\n\n\x08_currentB\x06\n\x04_iaqB\x0b\n\t_distanceB\x06\n\x04_luxB\x0c\n\n_white_luxB\t\n\x07_ir_luxB\t\n\x07_uv_luxB\x11\n\x0f_wind_directionB\r\n\x0b_wind_speedB\t\n\x07_weightB\x0c\n\n_wind_gustB\x0c\n\n_wind_lull\"\x8a\x02\n\x0cPowerMetrics\x12\x18\n\x0b\x63h1_voltage\x18\x01 \x01(\x02H\x00\x88\x01\x01\x12\x18\n\x0b\x63h1_current\x18\x02 \x01(\x02H\x01\x88\x01\x01\x12\x18\n\x0b\x63h2_voltage\x18\x03 \x01(\x02H\x02\x88\x01\x01\x12\x18\n\x0b\x63h2_current\x18\x04 \x01(\x02H\x03\x88\x01\x01\x12\x18\n\x0b\x63h3_voltage\x18\x05 \x01(\x02H\x04\x88\x01\x01\x12\x18\n\x0b\x63h3_current\x18\x06 \x01(\x02H\x05\x88\x01\x01\x42\x0e\n\x0c_ch1_voltageB\x0e\n\x0c_ch1_currentB\x0e\n\x0c_ch2_voltageB\x0e\n\x0c_ch2_currentB\x0e\n\x0c_ch3_voltageB\x0e\n\x0c_ch3_current\"\xeb\x04\n\x11\x41irQualityMetrics\x12\x1a\n\rpm10_standard\x18\x01 \x01(\rH\x00\x88\x01\x01\x12\x1a\n\rpm25_standard\x18\x02 \x01(\rH\x01\x88\x01\x01\x12\x1b\n\x0epm100_standard\x18\x03 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12pm10_environmental\x18\x04 \x01(\rH\x03\x88\x01\x01\x12\x1f\n\x12pm25_environmental\x18\x05 \x01(\rH\x04\x88\x01\x01\x12 \n\x13pm100_environmental\x18\x06 \x01(\rH\x05\x88\x01\x01\x12\x1b\n\x0eparticles_03um\x18\x07 \x01(\rH\x06\x88\x01\x01\x12\x1b\n\x0eparticles_05um\x18\x08 \x01(\rH\x07\x88\x01\x01\x12\x1b\n\x0eparticles_10um\x18\t \x01(\rH\x08\x88\x01\x01\x12\x1b\n\x0eparticles_25um\x18\n \x01(\rH\t\x88\x01\x01\x12\x1b\n\x0eparticles_50um\x18\x0b \x01(\rH\n\x88\x01\x01\x12\x1c\n\x0fparticles_100um\x18\x0c \x01(\rH\x0b\x88\x01\x01\x42\x10\n\x0e_pm10_standardB\x10\n\x0e_pm25_standardB\x11\n\x0f_pm100_standardB\x15\n\x13_pm10_environmentalB\x15\n\x13_pm25_environmentalB\x16\n\x14_pm100_environmentalB\x11\n\x0f_particles_03umB\x11\n\x0f_particles_05umB\x11\n\x0f_particles_10umB\x11\n\x0f_particles_25umB\x11\n\x0f_particles_50umB\x12\n\x10_particles_100um\"\xd5\x01\n\nLocalStats\x12\x16\n\x0euptime_seconds\x18\x01 \x01(\r\x12\x1b\n\x13\x63hannel_utilization\x18\x02 \x01(\x02\x12\x13\n\x0b\x61ir_util_tx\x18\x03 \x01(\x02\x12\x16\n\x0enum_packets_tx\x18\x04 \x01(\r\x12\x16\n\x0enum_packets_rx\x18\x05 \x01(\r\x12\x1a\n\x12num_packets_rx_bad\x18\x06 \x01(\r\x12\x18\n\x10num_online_nodes\x18\x07 \x01(\r\x12\x17\n\x0fnum_total_nodes\x18\x08 \x01(\r\"\xb8\x02\n\tTelemetry\x12\x0c\n\x04time\x18\x01 \x01(\x07\x12\x33\n\x0e\x64\x65vice_metrics\x18\x02 \x01(\x0b\x32\x19.meshtastic.DeviceMetricsH\x00\x12=\n\x13\x65nvironment_metrics\x18\x03 \x01(\x0b\x32\x1e.meshtastic.EnvironmentMetricsH\x00\x12<\n\x13\x61ir_quality_metrics\x18\x04 \x01(\x0b\x32\x1d.meshtastic.AirQualityMetricsH\x00\x12\x31\n\rpower_metrics\x18\x05 \x01(\x0b\x32\x18.meshtastic.PowerMetricsH\x00\x12-\n\x0blocal_stats\x18\x06 \x01(\x0b\x32\x16.meshtastic.LocalStatsH\x00\x42\t\n\x07variant\">\n\rNau7802Config\x12\x12\n\nzeroOffset\x18\x01 \x01(\x05\x12\x19\n\x11\x63\x61librationFactor\x18\x02 \x01(\x02*\x92\x03\n\x13TelemetrySensorType\x12\x10\n\x0cSENSOR_UNSET\x10\x00\x12\n\n\x06\x42ME280\x10\x01\x12\n\n\x06\x42ME680\x10\x02\x12\x0b\n\x07MCP9808\x10\x03\x12\n\n\x06INA260\x10\x04\x12\n\n\x06INA219\x10\x05\x12\n\n\x06\x42MP280\x10\x06\x12\t\n\x05SHTC3\x10\x07\x12\t\n\x05LPS22\x10\x08\x12\x0b\n\x07QMC6310\x10\t\x12\x0b\n\x07QMI8658\x10\n\x12\x0c\n\x08QMC5883L\x10\x0b\x12\t\n\x05SHT31\x10\x0c\x12\x0c\n\x08PMSA003I\x10\r\x12\x0b\n\x07INA3221\x10\x0e\x12\n\n\x06\x42MP085\x10\x0f\x12\x0c\n\x08RCWL9620\x10\x10\x12\t\n\x05SHT4X\x10\x11\x12\x0c\n\x08VEML7700\x10\x12\x12\x0c\n\x08MLX90632\x10\x13\x12\x0b\n\x07OPT3001\x10\x14\x12\x0c\n\x08LTR390UV\x10\x15\x12\x0e\n\nTSL25911FN\x10\x16\x12\t\n\x05\x41HT10\x10\x17\x12\x10\n\x0c\x44\x46ROBOT_LARK\x10\x18\x12\x0b\n\x07NAU7802\x10\x19\x12\n\n\x06\x42MP3XX\x10\x1a\x12\x0c\n\x08ICM20948\x10\x1b\x12\x0c\n\x08MAX17048\x10\x1c\x42\x64\n\x13\x63om.geeksville.meshB\x0fTelemetryProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') - -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.telemetry_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\017TelemetryProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' - _TELEMETRYSENSORTYPE._serialized_start=2454 - _TELEMETRYSENSORTYPE._serialized_end=2856 - _DEVICEMETRICS._serialized_start=43 - _DEVICEMETRICS._serialized_end=286 - _ENVIRONMENTMETRICS._serialized_start=289 - _ENVIRONMENTMETRICS._serialized_end=965 - _POWERMETRICS._serialized_start=968 - _POWERMETRICS._serialized_end=1234 - _AIRQUALITYMETRICS._serialized_start=1237 - _AIRQUALITYMETRICS._serialized_end=1856 - _LOCALSTATS._serialized_start=1859 - _LOCALSTATS._serialized_end=2072 - _TELEMETRY._serialized_start=2075 - _TELEMETRY._serialized_end=2387 - _NAU7802CONFIG._serialized_start=2389 - _NAU7802CONFIG._serialized_end=2451 -# @@protoc_insertion_point(module_scope) diff --git a/meshtastic/xmodem_pb2.py b/meshtastic/xmodem_pb2.py deleted file mode 100644 index a352c637..00000000 --- a/meshtastic/xmodem_pb2.py +++ /dev/null @@ -1,28 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: meshtastic/xmodem.proto -"""Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder -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 -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - - - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x17meshtastic/xmodem.proto\x12\nmeshtastic\"\xb6\x01\n\x06XModem\x12+\n\x07\x63ontrol\x18\x01 \x01(\x0e\x32\x1a.meshtastic.XModem.Control\x12\x0b\n\x03seq\x18\x02 \x01(\r\x12\r\n\x05\x63rc16\x18\x03 \x01(\r\x12\x0e\n\x06\x62uffer\x18\x04 \x01(\x0c\"S\n\x07\x43ontrol\x12\x07\n\x03NUL\x10\x00\x12\x07\n\x03SOH\x10\x01\x12\x07\n\x03STX\x10\x02\x12\x07\n\x03\x45OT\x10\x04\x12\x07\n\x03\x41\x43K\x10\x06\x12\x07\n\x03NAK\x10\x15\x12\x07\n\x03\x43\x41N\x10\x18\x12\t\n\x05\x43TRLZ\x10\x1a\x42\x61\n\x13\x63om.geeksville.meshB\x0cXmodemProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') - -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.xmodem_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\014XmodemProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' - _XMODEM._serialized_start=40 - _XMODEM._serialized_end=222 - _XMODEM_CONTROL._serialized_start=139 - _XMODEM_CONTROL._serialized_end=222 -# @@protoc_insertion_point(module_scope) diff --git a/meshview/__version__.py b/meshview/__version__.py new file mode 100644 index 00000000..dac0cd93 --- /dev/null +++ b/meshview/__version__.py @@ -0,0 +1,57 @@ +"""Version information for MeshView.""" + +import subprocess +from pathlib import Path + +__version__ = "3.0.2" +__release_date__ = "2026-1-9" + + +def get_git_revision(): + """Get the current git revision hash.""" + try: + repo_dir = Path(__file__).parent.parent + result = subprocess.run( + ["git", "rev-parse", "HEAD"], + capture_output=True, + text=True, + check=True, + cwd=repo_dir, + ) + return result.stdout.strip() + except (subprocess.CalledProcessError, FileNotFoundError): + return "unknown" + + +def get_git_revision_short(): + """Get the short git revision hash.""" + try: + repo_dir = Path(__file__).parent.parent + result = subprocess.run( + ["git", "rev-parse", "--short", "HEAD"], + capture_output=True, + text=True, + check=True, + cwd=repo_dir, + ) + return result.stdout.strip() + except (subprocess.CalledProcessError, FileNotFoundError): + return "unknown" + + +def get_version_info(): + """Get complete version information.""" + return { + "version": __version__, + "release_date": __release_date__, + "git_revision": get_git_revision(), + "git_revision_short": get_git_revision_short(), + } + + +# Cache git info at import time for performance +_git_revision = get_git_revision() +_git_revision_short = get_git_revision_short() + +# Full version string for display +__version_string__ = f"{__version__} ~ {__release_date__}" diff --git a/meshview/config.py b/meshview/config.py new file mode 100644 index 00000000..e6d46003 --- /dev/null +++ b/meshview/config.py @@ -0,0 +1,16 @@ +import argparse +import configparser + +# Parse command-line arguments +parser = argparse.ArgumentParser(description="MeshView Configuration Loader") +parser.add_argument( + "--config", type=str, default="config.ini", help="Path to config.ini file (default: config.ini)" +) +args, _ = parser.parse_known_args() + +# Initialize config parser +config_parser = configparser.ConfigParser() +if not config_parser.read(args.config): + raise FileNotFoundError(f"Config file '{args.config}' not found! Ensure the file exists.") + +CONFIG = {section: dict(config_parser.items(section)) for section in config_parser.sections()} diff --git a/meshview/database.py b/meshview/database.py index 1d027eed..068adfa3 100644 --- a/meshview/database.py +++ b/meshview/database.py @@ -1,18 +1,33 @@ -from sqlalchemy.ext.asyncio import async_sessionmaker -from sqlalchemy.ext.asyncio import AsyncSession -from sqlalchemy.ext.asyncio import create_async_engine +from sqlalchemy.engine.url import make_url +from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine from meshview import models +engine = None +async_session = None -def init_database(database_connetion_string): + +def init_database(database_connection_string): global engine, async_session - kwargs = {} - if not database_connetion_string.startswith('sqlite'): - kwargs['pool_size'] = 20 - kwargs['max_overflow'] = 50 - engine = create_async_engine(database_connetion_string, echo=False, **kwargs) - async_session = async_sessionmaker(engine, expire_on_commit=False) + kwargs = {"echo": False} + url = make_url(database_connection_string) + connect_args = {} + + if url.drivername.startswith("sqlite"): + query = dict(url.query) + query.setdefault("mode", "ro") + url = url.set(query=query) + connect_args["uri"] = True + + if connect_args: + kwargs["connect_args"] = connect_args + + engine = create_async_engine(url, **kwargs) + async_session = async_sessionmaker( + bind=engine, + class_=AsyncSession, + expire_on_commit=False, + ) async def create_tables(): diff --git a/meshview/decode_payload.py b/meshview/decode_payload.py index 7c03b491..080a9353 100644 --- a/meshview/decode_payload.py +++ b/meshview/decode_payload.py @@ -1,15 +1,16 @@ -from meshtastic.portnums_pb2 import PortNum -from meshtastic.mesh_pb2 import ( - Position, +from google.protobuf.message import DecodeError + +from meshtastic.protobuf.mesh_pb2 import ( + MeshPacket, NeighborInfo, - NodeInfo, - User, + Position, RouteDiscovery, Routing, - MeshPacket, + User, ) -from meshtastic.telemetry_pb2 import Telemetry -from google.protobuf.message import DecodeError +from meshtastic.protobuf.mqtt_pb2 import MapReport +from meshtastic.protobuf.portnums_pb2 import PortNum +from meshtastic.protobuf.telemetry_pb2 import Telemetry def text_message(payload): @@ -24,6 +25,7 @@ def text_message(payload): PortNum.TRACEROUTE_APP: RouteDiscovery.FromString, PortNum.ROUTING_APP: Routing.FromString, PortNum.TEXT_MESSAGE_APP: text_message, + PortNum.MAP_REPORT_APP: MapReport.FromString, } diff --git a/meshview/lang/en.json b/meshview/lang/en.json new file mode 100644 index 00000000..0c0c2ac3 --- /dev/null +++ b/meshview/lang/en.json @@ -0,0 +1,212 @@ + { + "base": { + "chat": "Chat", + "nodes": "Nodes", + "everything": "See Everything", + "graphs": "Mesh Graphs", + "net": "Weekly Net", + "map": "Live Map", + "stats": "Stats", + "top": "Top Traffic Nodes", + "footer": "Visit Meshview on GitHub", + "node id": "Node id", + "go to node": "Go to Node", + "all": "All", + "portnum_options": { + "1": "Text Message", + "3": "Position", + "4": "Node Info", + "67": "Telemetry", + "70": "Traceroute", + "71": "Neighbor Info" + } + }, + "chat": { + "chat_title": "Chats:", + "replying_to": "Replying to:", + "view_packet_details": "View packet details" + }, + "nodelist": { + "search_placeholder": "Search by name or ID...", + "all_roles": "All Roles", + "all_channels": "All Channels", + "all_hw": "All HW Models", + "all_firmware": "All Firmware", + + "show_favorites": "⭐ Show Favorites", + "show_all": "⭐ Show All", + "export_csv": "Export CSV", + "clear_filters": "Clear Filters", + + "showing_nodes": "Showing", + "nodes_suffix": "nodes", + + "loading_nodes": "Loading nodes...", + "error_loading_nodes": "Error loading nodes", + "no_nodes_found": "No nodes found", + + "short_name": "Short", + "long_name": "Long Name", + "hw_model": "HW Model", + "firmware": "Firmware", + "role": "Role", + "last_lat": "Last Latitude", + "last_long": "Last Longitude", + "channel": "Channel", + "last_seen": "Last Seen", + "favorite": "Favorite", + + "time_just_now": "just now", + "time_min_ago": "min ago", + "time_hr_ago": "hr ago", + "time_day_ago": "day ago", + "time_days_ago": "days ago" + }, + + "net": { + "net_title": "Weekly Net:", + "total_messages": "Number of messages:", + "view_packet_details": "More details" + }, + + "map": { + "show_routers_only": "Show Routers Only", + "share_view": "Share This View", + "reset_filters": "Reset Filters To Defaults", + "channel_label": "Channel:", + "model_label": "Model:", + "role_label": "Role:", + "last_seen": "Last seen:", + "firmware": "Firmware:", + "link_copied": "Link Copied!", + "legend_traceroute": "Traceroute (with arrows)", + "legend_neighbor": "Neighbor" + + }, + + "stats": + { + "mesh_stats_summary": "Mesh Statistics - Summary (all available in Database)", + "total_nodes": "Total Nodes", + "total_packets": "Total Packets", + "total_packets_seen": "Total Packets Seen", + "packets_per_day_all": "Packets per Day - All Ports (Last 14 Days)", + "packets_per_day_text": "Packets per Day - Text Messages (Port 1, Last 14 Days)", + "packets_per_hour_all": "Packets per Hour - All Ports", + "packets_per_hour_text": "Packets per Hour - Text Messages (Port 1)", + "packet_types_last_24h": "Packet Types - Last 24 Hours", + "hardware_breakdown": "Hardware Breakdown", + "role_breakdown": "Role Breakdown", + "channel_breakdown": "Channel Breakdown", + "expand_chart": "Expand Chart", + "export_csv": "Export CSV", + "all_channels": "All Channels", + "node_id": "Node ID" + }, + "top": { + "top_traffic_nodes": "Top Nodes Traffic", + "channel": "Channel", + "search": "Search", + "search_placeholder": "Search nodes...", + "long_name": "Long Name", + "short_name": "Short Name", + "packets_sent": "Sent (24h)", + "times_seen": "Seen (24h)", + "avg_gateways": "Avg Gateways", + "showing_nodes": "Showing", + "nodes_suffix": "nodes" + }, + + "nodegraph": + { + "channel_label": "Channel:", + "search_node_placeholder": "Search node...", + "search_button": "Search", + "long_name_label": "Long Name:", + "short_name_label": "Short Name:", + "role_label": "Role:", + "hw_model_label": "Hardware Model:", + "node_not_found": "Node not found in current channel!" + }, + "firehose": + { + "live_feed": "📡 Live Feed", + "pause": "Pause", + "resume": "Resume", + "time": "Time", + "packet_id": "Packet ID", + "from": "From", + "to": "To", + "port": "Port", + "links": "Links", + "unknown_app": "UNKNOWN APP", + "text_message": "Text Message", + "position": "Position", + "node_info": "Node Info", + "routing": "Routing", + "administration": "Administration", + "waypoint": "Waypoint", + "store_forward": "Store Forward", + "telemetry": "Telemetry", + "trace_route": "Trace Route", + "neighbor_info": "Neighbor Info", + "direct_to_mqtt": "direct to MQTT", + "all": "All", + "map": "Map", + "graph": "Graph" + }, + "node": { + "specifications": "Specifications", + "node_id": "Node ID", + "long_name": "Long Name", + "short_name": "Short Name", + "hw_model": "Hardware Model", + "firmware": "Firmware", + "role": "Role", + "channel": "Channel", + "latitude": "Latitude", + "longitude": "Longitude", + "last_update": "Last Update", + "battery_voltage": "Battery & Voltage", + "air_channel": "Air & Channel Utilization", + "environment": "Environment Metrics", + "neighbors_chart": "Neighbors (Signal-to-Noise)", + "expand": "Expand", + "export_csv": "Export CSV", + "time": "Time", + "packet_id": "Packet ID", + "from": "From", + "to": "To", + "port": "Port", + "direct_to_mqtt": "Direct to MQTT", + "all_broadcast": "All", + "statistics": "Statistics", + "last_24h": "24h", + "packets_sent": "Packets sent", + "times_seen": "Times seen" + }, + "packet": { + "loading": "Loading packet information...", + "packet_id_label": "Packet ID", + "from_node": "From Node", + "to_node": "To Node", + "channel": "Channel", + "port": "Port", + "raw_payload": "Raw Payload", + "decoded_telemetry": "Decoded Telemetry", + "location": "Location", + "seen_by": "Seen By", + "gateway": "Gateway", + "rssi": "RSSI", + "snr": "SNR", + "hops": "Hop", + "time": "Time", + "packet_source": "Packet Source", + "distance": "Distance", + "node_id_short": "Node ID", + "all_broadcast": "All", + "direct_to_mqtt": "Direct to MQTT" + } + + + } \ No newline at end of file diff --git a/meshview/lang/es.json b/meshview/lang/es.json new file mode 100644 index 00000000..e6aee9fd --- /dev/null +++ b/meshview/lang/es.json @@ -0,0 +1,197 @@ +{ + "base": { + "chat": "Conversaciones", + "nodes": "Nodos", + "everything": "Mostrar todo", + "graphs": "Gráficos de la Malla", + "net": "Red Semanal", + "map": "Mapa en Vivo", + "stats": "Estadísticas", + "top": "Nodos con Mayor Tráfico", + "footer": "Visita Meshview en Github.", + "node_id": "ID de Nodo", + "go_to_node": "Ir al nodo", + "all": "Todos", + "portnum_options": { + "1": "Mensaje de Texto", + "3": "Ubicación", + "4": "Información del Nodo", + "67": "Telemetría", + "70": "Traceroute", + "71": "Información de Vecinos" + } + }, + + "chat": { + "chat_title": "Conversaciones:", + "replying_to": "Respondiendo a:", + "view_packet_details": "Ver detalles del paquete" + }, + + "nodelist": { + "search_placeholder": "Buscar por nombre o ID...", + "all_roles": "Todos los roles", + "all_channels": "Todos los canales", + "all_hw": "Todos los modelos", + "all_firmware": "Todo el firmware", + "show_favorites": "⭐ Mostrar favoritos", + "show_all": "⭐ Mostrar todos", + "export_csv": "Exportar CSV", + "clear_filters": "Limpiar filtros", + "showing_nodes": "Mostrando", + "nodes_suffix": "nodos", + "loading_nodes": "Cargando nodos...", + "error_loading_nodes": "Error al cargar nodos", + "no_nodes_found": "No se encontraron nodos", + "short_name": "Corto", + "long_name": "Nombre largo", + "hw_model": "Modelo HW", + "firmware": "Firmware", + "role": "Rol", + "last_lat": "Última latitud", + "last_long": "Última longitud", + "channel": "Canal", + "last_seen": "Última vez visto", + "favorite": "Favorito", + "time_just_now": "justo ahora", + "time_min_ago": "min atrás", + "time_hr_ago": "h atrás", + "time_day_ago": "día atrás", + "time_days_ago": "días atrás" + }, + + "net": { + "net_title": "Red Semanal:", + "total_messages": "Número de mensajes:", + "view_packet_details": "Más Detalles" + }, + + "map": { + "filter_routers_only": "Mostrar solo enrutadores", + "share_view": "Compartir esta vista", + "reset_filters": "Restablecer filtros", + "channel_label": "Canal:", + "model_label": "Modelo:", + "role_label": "Rol:", + "last_seen": "Visto por última vez:", + "firmware": "Firmware:", + "link_copied": "¡Enlace copiado!", + "legend_traceroute": "Ruta de traceroute (flechas de dirección)", + "legend_neighbor": "Vínculo de vecinos" + }, + + "stats": { + "mesh_stats_summary": "Estadísticas de la Malla - Resumen (completas en la base de datos)", + "total_nodes": "Nodos Totales", + "total_packets": "Paquetes Totales", + "total_packets_seen": "Paquetes Totales Vistos", + "packets_per_day_all": "Paquetes por Día - Todos los Puertos (Últimos 14 Días)", + "packets_per_day_text": "Paquetes por Día - Mensajes de Texto (Puerto 1, Últimos 14 Días)", + "packets_per_hour_all": "Paquetes por Hora - Todos los Puertos", + "packets_per_hour_text": "Paquetes por Hora - Mensajes de Texto (Puerto 1)", + "packet_types_last_24h": "Tipos de Paquetes - Últimas 24 Horas", + "hardware_breakdown": "Distribución de Hardware", + "role_breakdown": "Distribución de Roles", + "channel_breakdown": "Distribución de Canales", + "expand_chart": "Ampliar Gráfico", + "export_csv": "Exportar CSV", + "all_channels": "Todos los Canales" + }, + + "top": { + "top_traffic_nodes": "Tráfico de Nodos (24h)", + "channel": "Canal", + "search": "Buscar", + "search_placeholder": "Buscar nodos...", + "long_name": "Nombre Largo", + "short_name": "Nombre Corto", + "packets_sent": "Enviados (24h)", + "times_seen": "Visto (24h)", + "avg_gateways": "Promedio de Gateways", + "showing_nodes": "Mostrando", + "nodes_suffix": "nodos" + }, + + "nodegraph": { + "channel_label": "Canal:", + "search_placeholder": "Buscar nodo...", + "search_button": "Buscar", + "long_name_label": "Nombre completo:", + "short_name_label": "Nombre corto:", + "role_label": "Rol:", + "hw_model_label": "Modelo de hardware:", + "traceroute": "Traceroute", + "neighbor": "Vecino", + "other": "Otro", + "unknown": "Desconocido", + "node_not_found": "¡Nodo no encontrado en el canal actual!" + }, + + "firehose": { + "live_feed": "📡 Flujo en vivo", + "pause": "Pausar", + "resume": "Reanudar", + "time": "Hora", + "packet_id": "ID de paquete", + "from": "De", + "to": "A", + "port": "Puerto", + "direct_to_mqtt": "Directo a MQTT", + "all_broadcast": "Todos" + }, + + "node": { + "specifications": "Especificaciones", + "node_id": "ID de Nodo", + "long_name": "Nombre Largo", + "short_name": "Nombre Corto", + "hw_model": "Modelo de Hardware", + "firmware": "Firmware", + "role": "Rol", + "channel": "Canal", + "latitude": "Latitud", + "longitude": "Longitud", + "last_update": "Última Actualización", + "battery_voltage": "Batería y voltaje", + "air_channel": "Utilización del aire y del canal", + "environment": "Métricas Ambientales", + "neighbors_chart": "Vecinos (Relación Señal/Ruido)", + "expand": "Ampliar", + "export_csv": "Exportar CSV", + "time": "Hora", + "packet_id": "ID del Paquete", + "from": "De", + "to": "A", + "port": "Puerto", + "direct_to_mqtt": "Directo a MQTT", + "all_broadcast": "Todos", + "statistics": "Estadísticas", + "last_24h": "24h", + "packets_sent": "Paquetes enviados", + "times_seen": "Veces visto" + }, + + "packet": { + "loading": "Cargando información del paquete...", + "packet_id_label": "ID del Paquete", + "from_node": "De", + "to_node": "A", + "channel": "Canal", + "port": "Puerto", + "raw_payload": "Payload sin procesar", + "decoded_telemetry": "Telemetría Decodificada", + "location": "Ubicación", + "seen_by": "Visto por", + "gateway": "Gateway", + "rssi": "RSSI", + "snr": "SNR", + "hops": "Saltos", + "time": "Hora", + "packet_source": "Origen del Paquete", + "distance": "Distancia", + "node_id_short": "ID de Nodo", + "all_broadcast": "Todos", + "direct_to_mqtt": "Directo a MQTT", + "signal": "Señal" + } +} diff --git a/meshview/migrations.py b/meshview/migrations.py new file mode 100644 index 00000000..2ed5a504 --- /dev/null +++ b/meshview/migrations.py @@ -0,0 +1,248 @@ +""" +Database migration management for MeshView. + +This module provides utilities for: +- Running Alembic migrations programmatically +- Checking database schema versions +- Coordinating migrations between writer and reader apps +""" + +import asyncio +import logging +from pathlib import Path + +from alembic.config import Config +from alembic.runtime.migration import MigrationContext +from alembic.script import ScriptDirectory +from sqlalchemy import text +from sqlalchemy.ext.asyncio import AsyncEngine + +from alembic import command + +logger = logging.getLogger(__name__) + + +def get_alembic_config(database_url: str) -> Config: + """ + Get Alembic configuration with the database URL set. + + Args: + database_url: SQLAlchemy database connection string + + Returns: + Configured Alembic Config object + """ + # Get the alembic.ini path (in project root) + alembic_ini = Path(__file__).parent.parent / "alembic.ini" + + config = Config(str(alembic_ini)) + config.set_main_option("sqlalchemy.url", database_url) + + return config + + +async def get_current_revision(engine: AsyncEngine) -> str | None: + """ + Get the current database schema revision. + + Args: + engine: Async SQLAlchemy engine + + Returns: + Current revision string, or None if no migrations applied + """ + async with engine.connect() as connection: + + def _get_revision(conn): + context = MigrationContext.configure(conn) + return context.get_current_revision() + + revision = await connection.run_sync(_get_revision) + return revision + + +async def get_head_revision(database_url: str) -> str | None: + """ + Get the head (latest) revision from migration scripts. + + Args: + database_url: Database connection string + + Returns: + Head revision string, or None if no migrations exist + """ + config = get_alembic_config(database_url) + script_dir = ScriptDirectory.from_config(config) + + head = script_dir.get_current_head() + return head + + +async def is_database_up_to_date(engine: AsyncEngine, database_url: str) -> bool: + """ + Check if database is at the latest schema version. + + Args: + engine: Async SQLAlchemy engine + database_url: Database connection string + + Returns: + True if database is up to date, False otherwise + """ + current = await get_current_revision(engine) + head = await get_head_revision(database_url) + + # If there are no migrations yet, consider it up to date + if head is None: + return True + + return current == head + + +def run_migrations(database_url: str) -> None: + """ + Run all pending migrations to bring database up to date. + + This is a synchronous operation that runs Alembic migrations. + Should be called by the writer app on startup. + + Args: + database_url: Database connection string + """ + logger.info("Running database migrations...") + import sys + + sys.stdout.flush() + + config = get_alembic_config(database_url) + + try: + # Run migrations to head + logger.info("Calling alembic upgrade command...") + sys.stdout.flush() + command.upgrade(config, "head") + logger.info("Database migrations completed successfully") + sys.stdout.flush() + except Exception as e: + logger.error(f"Error running migrations: {e}") + raise + + +async def wait_for_migrations( + engine: AsyncEngine, database_url: str, max_retries: int = 30, retry_delay: int = 2 +) -> bool: + """ + Wait for database migrations to complete. + + This should be called by the reader app to wait until + the database schema is up to date before proceeding. + + Args: + engine: Async SQLAlchemy engine + database_url: Database connection string + max_retries: Maximum number of retry attempts + retry_delay: Seconds to wait between retries + + Returns: + True if database is up to date, False if max retries exceeded + """ + for attempt in range(max_retries): + try: + if await is_database_up_to_date(engine, database_url): + logger.info("Database schema is up to date") + return True + + current = await get_current_revision(engine) + head = await get_head_revision(database_url) + + logger.info( + f"Database schema not up to date (current: {current}, head: {head}). " + f"Waiting... (attempt {attempt + 1}/{max_retries})" + ) + + await asyncio.sleep(retry_delay) + + except Exception as e: + logger.warning( + f"Error checking database version (attempt {attempt + 1}/{max_retries}): {e}" + ) + await asyncio.sleep(retry_delay) + + logger.error(f"Database schema not up to date after {max_retries} attempts") + return False + + +async def create_migration_status_table(engine: AsyncEngine) -> None: + """ + Create a simple status table for migration coordination. + + This table can be used to signal when migrations are in progress. + + Args: + engine: Async SQLAlchemy engine + """ + async with engine.begin() as conn: + await conn.execute( + text(""" + CREATE TABLE IF NOT EXISTS migration_status ( + id INTEGER PRIMARY KEY CHECK (id = 1), + in_progress BOOLEAN NOT NULL DEFAULT FALSE, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + """) + ) + + result = await conn.execute( + text(""" + SELECT 1 FROM migration_status WHERE id = 1 + """) + ) + if result.first() is None: + await conn.execute( + text(""" + INSERT INTO migration_status (id, in_progress) + VALUES (1, FALSE) + """) + ) + + +async def set_migration_in_progress(engine: AsyncEngine, in_progress: bool) -> None: + """ + Set the migration in-progress flag. + + Args: + engine: Async SQLAlchemy engine + in_progress: True if migration is in progress, False otherwise + """ + async with engine.begin() as conn: + await conn.execute( + text(""" + UPDATE migration_status + SET in_progress = :in_progress, + updated_at = CURRENT_TIMESTAMP + WHERE id = 1 + """), + {"in_progress": in_progress}, + ) + + +async def is_migration_in_progress(engine: AsyncEngine) -> bool: + """ + Check if a migration is currently in progress. + + Args: + engine: Async SQLAlchemy engine + + Returns: + True if migration is in progress, False otherwise + """ + try: + async with engine.connect() as conn: + result = await conn.execute( + text("SELECT in_progress FROM migration_status WHERE id = 1") + ) + row = result.fetchone() + return bool(row[0]) if row else False + except Exception: + # If table doesn't exist or query fails, assume no migration in progress + return False diff --git a/meshview/models.py b/meshview/models.py index 63941a16..759d471d 100644 --- a/meshview/models.py +++ b/meshview/models.py @@ -1,41 +1,62 @@ -from datetime import datetime - -from sqlalchemy.orm import DeclarativeBase, foreign +from sqlalchemy import BigInteger, ForeignKey, Index, desc from sqlalchemy.ext.asyncio import AsyncAttrs -from sqlalchemy.orm import mapped_column, relationship, Mapped -from sqlalchemy import ForeignKey, BigInteger +from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship class Base(AsyncAttrs, DeclarativeBase): pass +# Node class Node(Base): __tablename__ = "node" id: Mapped[str] = mapped_column(primary_key=True) node_id: Mapped[int] = mapped_column(BigInteger, nullable=True, unique=True) - long_name: Mapped[str] - short_name: Mapped[str] - hw_model: Mapped[str] + long_name: Mapped[str] = mapped_column(nullable=True) + short_name: Mapped[str] = mapped_column(nullable=True) + hw_model: Mapped[str] = mapped_column(nullable=True) + firmware: Mapped[str] = mapped_column(nullable=True) role: Mapped[str] = mapped_column(nullable=True) last_lat: Mapped[int] = mapped_column(BigInteger, nullable=True) last_long: Mapped[int] = mapped_column(BigInteger, nullable=True) + channel: Mapped[str] = mapped_column(nullable=True) + first_seen_us: Mapped[int] = mapped_column(BigInteger, nullable=True) + last_seen_us: Mapped[int] = mapped_column(BigInteger, nullable=True) + + __table_args__ = ( + Index("idx_node_node_id", "node_id"), + Index("idx_node_first_seen_us", "first_seen_us"), + Index("idx_node_last_seen_us", "last_seen_us"), + ) + + def to_dict(self): + return {column.name: getattr(self, column.name) for column in self.__table__.columns} class Packet(Base): __tablename__ = "packet" id: Mapped[int] = mapped_column(BigInteger, primary_key=True) - portnum: Mapped[int] - from_node_id: Mapped[int] = mapped_column(BigInteger) + portnum: Mapped[int] = mapped_column(nullable=True) + from_node_id: Mapped[int] = mapped_column(BigInteger, nullable=True) from_node: Mapped["Node"] = relationship( primaryjoin="Packet.from_node_id == foreign(Node.node_id)", lazy="joined" ) - to_node_id: Mapped[int] = mapped_column(BigInteger) + to_node_id: Mapped[int] = mapped_column(BigInteger, nullable=True) to_node: Mapped["Node"] = relationship( - primaryjoin="Packet.to_node_id == foreign(Node.node_id)", lazy="joined" + primaryjoin="Packet.to_node_id == foreign(Node.node_id)", + lazy="joined", + overlaps="from_node", + ) + payload: Mapped[bytes] = mapped_column(nullable=True) + import_time_us: Mapped[int] = mapped_column(BigInteger, nullable=True) + channel: Mapped[str] = mapped_column(nullable=True) + + __table_args__ = ( + Index("idx_packet_from_node_id", "from_node_id"), + Index("idx_packet_to_node_id", "to_node_id"), + Index("idx_packet_import_time_us", desc("import_time_us")), + Index("idx_packet_from_node_time_us", "from_node_id", desc("import_time_us")), ) - payload: Mapped[bytes] - import_time: Mapped[datetime] class PacketSeen(Base): @@ -43,27 +64,39 @@ class PacketSeen(Base): packet_id = mapped_column(ForeignKey("packet.id"), primary_key=True) node_id: Mapped[int] = mapped_column(BigInteger, primary_key=True) node: Mapped["Node"] = relationship( - lazy="joined", primaryjoin="PacketSeen.node_id == foreign(Node.node_id)" + lazy="joined", + primaryjoin="PacketSeen.node_id == foreign(Node.node_id)", + overlaps="from_node,to_node", ) rx_time: Mapped[int] = mapped_column(BigInteger, primary_key=True) - hop_limit: Mapped[int] + hop_limit: Mapped[int] = mapped_column(nullable=True) hop_start: Mapped[int] = mapped_column(nullable=True) - channel: Mapped[str] + channel: Mapped[str] = mapped_column(nullable=True) rx_snr: Mapped[float] = mapped_column(nullable=True) rx_rssi: Mapped[int] = mapped_column(nullable=True) - topic: Mapped[str] - import_time: Mapped[datetime] + topic: Mapped[str] = mapped_column(nullable=True) + import_time_us: Mapped[int] = mapped_column(BigInteger, nullable=True) + + __table_args__ = ( + Index("idx_packet_seen_node_id", "node_id"), + # Index for /top endpoint performance - JOIN on packet_id + Index("idx_packet_seen_packet_id", "packet_id"), + Index("idx_packet_seen_import_time_us", "import_time_us"), + ) class Traceroute(Base): __tablename__ = "traceroute" + id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) packet_id = mapped_column(ForeignKey("packet.id")) packet: Mapped["Packet"] = relationship( primaryjoin="Traceroute.packet_id == foreign(Packet.id)", lazy="joined" ) - gateway_node_id: Mapped[int] = mapped_column(BigInteger) - done: Mapped[bool] - route: Mapped[bytes] - import_time: Mapped[datetime] + gateway_node_id: Mapped[int] = mapped_column(BigInteger, nullable=True) + done: Mapped[bool] = mapped_column(nullable=True) + route: Mapped[bytes] = mapped_column(nullable=True) + route_return: Mapped[bytes] = mapped_column(nullable=True) + import_time_us: Mapped[int] = mapped_column(BigInteger, nullable=True) + __table_args__ = (Index("idx_traceroute_import_time_us", "import_time_us"),) diff --git a/meshview/mqtt_database.py b/meshview/mqtt_database.py new file mode 100644 index 00000000..e74b9b09 --- /dev/null +++ b/meshview/mqtt_database.py @@ -0,0 +1,19 @@ +from sqlalchemy.engine.url import make_url +from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine + +from meshview import models + + +def init_database(database_connection_string): + global engine, async_session + url = make_url(database_connection_string) + kwargs = {"echo": False} + if url.drivername.startswith("sqlite"): + kwargs["connect_args"] = {"timeout": 900} + engine = create_async_engine(url, **kwargs) + async_session = async_sessionmaker(engine, expire_on_commit=False) + + +async def create_tables(): + async with engine.begin() as conn: + await conn.run_sync(models.Base.metadata.create_all) diff --git a/meshview/mqtt_reader.py b/meshview/mqtt_reader.py index 91d2490a..e443eea4 100644 --- a/meshview/mqtt_reader.py +++ b/meshview/mqtt_reader.py @@ -1,15 +1,25 @@ -import base64 import asyncio +import base64 +import logging import random +import time import aiomqtt -from google.protobuf.message import DecodeError from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes +from google.protobuf.message import DecodeError -from meshtastic.mqtt_pb2 import ServiceEnvelope +from meshtastic.protobuf.mqtt_pb2 import ServiceEnvelope KEY = base64.b64decode("1PG7OiApB1nwvP+rz05pAQ==") +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s %(filename)s:%(lineno)d [pid:%(process)d] %(levelname)s - %(message)s", + datefmt="%Y-%m-%d %H:%M:%S", +) + +logger = logging.getLogger(__name__) + def decrypt(packet): if packet.HasField("decoded"): @@ -27,23 +37,57 @@ def decrypt(packet): pass -async def get_topic_envelopes(mqtt_server, topics): +async def get_topic_envelopes(mqtt_server, mqtt_port, topics, mqtt_user, mqtt_passwd): identifier = str(random.getrandbits(16)) + msg_count = 0 + start_time = None while True: try: async with aiomqtt.Client( - mqtt_server, username="meshdev", password="large4cats" , identifier=identifier, + mqtt_server, + port=mqtt_port, + username=mqtt_user, + password=mqtt_passwd, + identifier=identifier, ) as client: + logger.info(f"Connected to MQTT broker at {mqtt_server}:{mqtt_port}") for topic in topics: + logger.info(f"Subscribing to: {topic}") await client.subscribe(topic) + + # Reset start time when connected + if start_time is None: + start_time = time.time() + async for msg in client.messages: try: envelope = ServiceEnvelope.FromString(msg.payload) except DecodeError: continue + decrypt(envelope.packet) + # print(envelope.packet.decoded) if not envelope.packet.decoded: continue + + # Skip packets from specific node + # FIXME: make this configurable as a list of node IDs to skip + if getattr(envelope.packet, "from", None) == 2144342101: + continue + + msg_count += 1 + # FIXME: make this interval configurable or time based + if ( + msg_count % 10000 == 0 + ): # Log notice every 10000 messages (approx every hour at 3/sec) + elapsed_time = time.time() - start_time + msg_rate = msg_count / elapsed_time if elapsed_time > 0 else 0 + logger.info( + f"Processed {msg_count} messages so far... ({msg_rate:.2f} msg/sec)" + ) + yield msg.topic.value, envelope + except aiomqtt.MqttError as e: + logger.error(f"MQTT error: {e}, reconnecting in 1s...") await asyncio.sleep(1) diff --git a/meshview/mqtt_store.py b/meshview/mqtt_store.py new file mode 100644 index 00000000..97018d4c --- /dev/null +++ b/meshview/mqtt_store.py @@ -0,0 +1,247 @@ +import datetime +import logging +import re +import time + +from sqlalchemy import select +from sqlalchemy.dialects.postgresql import insert as pg_insert +from sqlalchemy.dialects.sqlite import insert as sqlite_insert +from sqlalchemy.exc import IntegrityError + +from meshtastic.protobuf.config_pb2 import Config +from meshtastic.protobuf.mesh_pb2 import HardwareModel +from meshtastic.protobuf.portnums_pb2 import PortNum +from meshview import decode_payload, mqtt_database +from meshview.models import Node, Packet, PacketSeen, Traceroute + +logger = logging.getLogger(__name__) + + +async def process_envelope(topic, env): + # MAP_REPORT_APP + if env.packet.decoded.portnum == PortNum.MAP_REPORT_APP: + node_id = getattr(env.packet, "from") + user_id = f"!{node_id:0{8}x}" + + map_report = decode_payload.decode_payload( + PortNum.MAP_REPORT_APP, env.packet.decoded.payload + ) + + async with mqtt_database.async_session() as session: + try: + hw_model = ( + HardwareModel.Name(map_report.hw_model) + if hasattr(HardwareModel, "Name") + else "unknown" + ) + role = ( + Config.DeviceConfig.Role.Name(map_report.role) + if hasattr(Config.DeviceConfig.Role, "Name") + else "unknown" + ) + node = ( + await session.execute(select(Node).where(Node.node_id == node_id)) + ).scalar_one_or_none() + + now_us = int(time.time() * 1_000_000) + + if node: + node.node_id = node_id + node.long_name = map_report.long_name + node.short_name = map_report.short_name + node.hw_model = hw_model + node.role = role + node.channel = env.channel_id + node.last_lat = map_report.latitude_i + node.last_long = map_report.longitude_i + node.firmware = map_report.firmware_version + node.last_seen_us = now_us + if node.first_seen_us is None: + node.first_seen_us = now_us + else: + node = Node( + id=user_id, + node_id=node_id, + long_name=map_report.long_name, + short_name=map_report.short_name, + hw_model=hw_model, + role=role, + channel=env.channel_id, + firmware=map_report.firmware_version, + last_lat=map_report.latitude_i, + last_long=map_report.longitude_i, + first_seen_us=now_us, + last_seen_us=now_us, + ) + session.add(node) + except Exception as e: + print(f"Error processing MAP_REPORT_APP: {e}") + + await session.commit() + + if not env.packet.id: + return + + async with mqtt_database.async_session() as session: + # --- Packet insert with ON CONFLICT DO NOTHING + result = await session.execute(select(Packet).where(Packet.id == env.packet.id)) + packet = result.scalar_one_or_none() + if not packet: + now_us = int(time.time() * 1_000_000) + packet_values = { + "id": env.packet.id, + "portnum": env.packet.decoded.portnum, + "from_node_id": getattr(env.packet, "from"), + "to_node_id": env.packet.to, + "payload": env.packet.SerializeToString(), + "import_time_us": now_us, + "channel": env.channel_id, + } + utc_time = datetime.datetime.fromtimestamp(now_us / 1_000_000, datetime.UTC) + dialect = session.get_bind().dialect.name + stmt = None + if dialect == "sqlite": + stmt = ( + sqlite_insert(Packet) + .values(**packet_values) + .on_conflict_do_nothing(index_elements=["id"]) + ) + elif dialect == "postgresql": + stmt = ( + pg_insert(Packet) + .values(**packet_values) + .on_conflict_do_nothing(index_elements=["id"]) + ) + + if stmt is not None: + await session.execute(stmt) + else: + try: + async with session.begin_nested(): + session.add(Packet(**packet_values)) + await session.flush() + except IntegrityError: + pass + + # --- PacketSeen (no conflict handling here, normal insert) + + if not env.gateway_id: + print("WARNING: Missing gateway_id, skipping PacketSeen entry") + # Most likely a misconfiguration of a mqtt publisher? + return + else: + node_id = int(env.gateway_id[1:], 16) + + result = await session.execute( + select(PacketSeen).where( + PacketSeen.packet_id == env.packet.id, + PacketSeen.node_id == node_id, + PacketSeen.rx_time == env.packet.rx_time, + ) + ) + if not result.scalar_one_or_none(): + now_us = int(time.time() * 1_000_000) + seen = PacketSeen( + packet_id=env.packet.id, + node_id=int(env.gateway_id[1:], 16), + channel=env.channel_id, + rx_time=env.packet.rx_time, + rx_snr=env.packet.rx_snr, + rx_rssi=env.packet.rx_rssi, + hop_limit=env.packet.hop_limit, + hop_start=env.packet.hop_start, + topic=topic, + import_time_us=now_us, + ) + session.add(seen) + + # --- NODEINFO_APP handling + if env.packet.decoded.portnum == PortNum.NODEINFO_APP: + try: + user = decode_payload.decode_payload( + PortNum.NODEINFO_APP, env.packet.decoded.payload + ) + if user and user.id: + if user.id[0] == "!" and re.fullmatch(r"[0-9a-fA-F]+", user.id[1:]): + node_id = int(user.id[1:], 16) + else: + node_id = None + + hw_model = ( + HardwareModel.Name(user.hw_model) + if user.hw_model in HardwareModel.values() + else f"unknown({user.hw_model})" + ) + role = ( + Config.DeviceConfig.Role.Name(user.role) + if hasattr(Config.DeviceConfig.Role, "Name") + else "unknown" + ) + + node = ( + await session.execute(select(Node).where(Node.id == user.id)) + ).scalar_one_or_none() + + now_us = int(time.time() * 1_000_000) + + if node: + node.node_id = node_id + node.long_name = user.long_name + node.short_name = user.short_name + node.hw_model = hw_model + node.role = role + node.channel = env.channel_id + node.last_seen_us = now_us + if node.first_seen_us is None: + node.first_seen_us = now_us + else: + node = Node( + id=user.id, + node_id=node_id, + long_name=user.long_name, + short_name=user.short_name, + hw_model=hw_model, + role=role, + channel=env.channel_id, + first_seen_us=now_us, + last_seen_us=now_us, + ) + session.add(node) + except Exception as e: + print(f"Error processing NODEINFO_APP: {e}") + + # --- POSITION_APP handling + if env.packet.decoded.portnum == PortNum.POSITION_APP: + position = decode_payload.decode_payload( + PortNum.POSITION_APP, env.packet.decoded.payload + ) + if position and position.latitude_i and position.longitude_i: + from_node_id = getattr(env.packet, "from") + node = ( + await session.execute(select(Node).where(Node.node_id == from_node_id)) + ).scalar_one_or_none() + if node: + now_us = int(time.time() * 1_000_000) + node.last_lat = position.latitude_i + node.last_long = position.longitude_i + node.last_seen_us = now_us + if node.first_seen_us is None: + node.first_seen_us = now_us + session.add(node) + + # --- TRACEROUTE_APP (no conflict handling, normal insert) + if env.packet.decoded.portnum == PortNum.TRACEROUTE_APP: + packet_id = env.packet.id + if packet_id is not None: + now_us = int(time.time() * 1_000_000) + session.add( + Traceroute( + packet_id=packet_id, + route=env.packet.decoded.payload, + done=not env.packet.decoded.want_response, + gateway_node_id=int(env.gateway_id[1:], 16), + import_time_us=now_us, + ) + ) + + await session.commit() diff --git a/meshview/notify.py b/meshview/notify.py index d74b3830..49264137 100644 --- a/meshview/notify.py +++ b/meshview/notify.py @@ -1,6 +1,6 @@ +import asyncio import contextlib from collections import defaultdict -import asyncio waiting_node_ids_events = defaultdict(set) @@ -34,7 +34,6 @@ def create_event(node_id): def remove_event(node_event): - print("removing event") waiting_node_ids_events[node_event.node_id].remove(node_event) @@ -52,8 +51,15 @@ def notify_uplinked(node_id, packet): @contextlib.contextmanager def subscribe(node_id): + """ + Context manager for subscribing to events for a node_id. + Automatically manages event creation and cleanup. + """ event = create_event(node_id) try: yield event + except Exception as e: + print(f"Error during subscription for node_id={node_id}: {e}") + raise finally: remove_event(event) diff --git a/meshview/static/api-chat.html b/meshview/static/api-chat.html new file mode 100644 index 00000000..fe9dd81e --- /dev/null +++ b/meshview/static/api-chat.html @@ -0,0 +1,127 @@ + + + + + API Documentation - Chat + + + + +
+ + + + + diff --git a/meshview/static/api.html b/meshview/static/api.html new file mode 100644 index 00000000..4ff91480 --- /dev/null +++ b/meshview/static/api.html @@ -0,0 +1,273 @@ + + + + +Meshview API Documentation + + + + + +

Meshview API Documentation

+

This page describes all REST endpoints provided by Meshview.

+ + + +

/api/nodes

+ +
+ GET + /api/nodes +

Returns a list of mesh nodes.

+ +

Query Parameters

+ + + + + + +
ParameterDescription
roleFilter by node role
channelFilter by channel
hw_modelHardware model filter
days_activeOnly nodes seen within X days
+ +
+ Example:
+ /api/nodes?days_active=3 +
+
+ + + +

/api/packets

+ +
+ GET + /api/packets +

Fetch packets with many filters. Returns decoded packet data.

+ +

Query Parameters

+ + + + + + + + + + +
ParameterDescription
packet_idReturn exactly one packet
limitMax number of results (1–100)
sinceOnly packets newer than import_time_us
from_node_idFilter by sender node
to_node_idFilter by destination node
node_idLegacy: match either from or to
portnumFilter by port number
containsSubstring filter for payload
+ +
+ Example:
+ /api/packets?from_node_id=123&limit=100 +
+
+ + + +

/api/packets_seen/{packet_id}

+ +
+ GET + /api/packets_seen/<packet_id> +

Returns list of gateways that heard the packet (RSSI/SNR/hops).

+ +
+ Example:
+ /api/packets_seen/3314808102 +
+
+ + + +

/api/stats

+ +
+ GET + /api/stats +

Returns aggregated packet statistics for a node or globally.

+ +

Query Parameters

+ + + + + + + + + +
ParameterDescription
period_type"hour" or "day"
lengthHow many hours/days
nodeNode ID for combined sent+seen stats
from_nodeFilter by sender
to_nodeFilter by receiver
portnumFilter by port
channelFilter by channel
+ +
+ Example:
+ /api/stats?node=1128180332&period_type=day&length=1 +
+
+ + + +

/api/stats/count

+ +
+ GET + /api/stats/count +

+ Returns total packets and total packet_seen entries. + When no filters are provided, it returns global totals. + When filters are specified, they narrow the time, channel, + direction, or specific packet. +

+ +

Query Parameters

+ + + + + + + + +
ParameterDescription
period_type"hour" or "day"
lengthNumber of hours or days (depends on period_type)
channelFilter by channel
from_nodeOnly packets sent by this node
to_nodeOnly packets received by this node
packet_idFilter seen counts for specific packet_id
+ +
+ Example:
+ /api/stats/count?from_node=1128180332&period_type=day&length=1 +
+
+ + + + +

/api/edges

+ +
+ GET + /api/edges +

Returns traceroute and/or neighbor edges for graph rendering.

+ +

Query Parameters

+ + + +
ParameterDescription
type"traceroute", "neighbor", or omitted for both
+ +
+ Example:
+ /api/edges?type=neighbor +
+
+ + + +

/api/config

+ +
+ GET + /api/config +

Returns Meshview configuration (site, MQTT, cleanup, etc.).

+ +
+ /api/config +
+
+ + + +

/api/lang

+ +
+ GET + /api/lang +

Returns translated text for the UI.

+ +

Parameters

+ + + +
langLanguage code (e.g. "en")
sectionOptional UI section (firehose, map, top...)
+ +
+ /api/lang?lang=en§ion=firehose +
+
+ + + +

/health

+ +
+ GET + /health +

Returns API + database status.

+
+ + + +

/version

+ +
+ GET + /version +

Returns Meshview version and Git revision.

+
+ + +

+
+

Meshview API — generated documentation

+ + + diff --git a/meshview/static/favicon.ico b/meshview/static/favicon.ico new file mode 100644 index 00000000..6236c5bb Binary files /dev/null and b/meshview/static/favicon.ico differ diff --git a/meshview/static/heatmap.html b/meshview/static/heatmap.html new file mode 100644 index 00000000..c3135e2d --- /dev/null +++ b/meshview/static/heatmap.html @@ -0,0 +1,164 @@ + + + + +Mesh Nodes Population Heatmap + + + + +
+
+ + + + + + diff --git a/meshview/static/kiosk.html b/meshview/static/kiosk.html new file mode 100644 index 00000000..799950ad --- /dev/null +++ b/meshview/static/kiosk.html @@ -0,0 +1,244 @@ + + + + +Mesh Nodes Live Map + + + + + +
+ +
+
+
+ + + + + + + + diff --git a/meshview/static/portmaps.js b/meshview/static/portmaps.js new file mode 100644 index 00000000..4d303831 --- /dev/null +++ b/meshview/static/portmaps.js @@ -0,0 +1,36 @@ +// Shared port label/color definitions for UI pages. +window.PORT_LABEL_MAP = { + 0: "UNKNOWN", + 1: "Text", + 3: "Position", + 4: "Node Info", + 5: "Routing", + 6: "Admin", + 8: "Waypoint", + 35: "Store Forward++", + 65: "Store & Forward", + 67: "Telemetry", + 70: "Traceroute", + 71: "Neighbor", + 73: "Map Report", +}; + +window.PORT_COLOR_MAP = { + 0: "#6c757d", + 1: "#007bff", + 3: "#28a745", + 4: "#ffc107", + 5: "#dc3545", + 6: "#20c997", + 8: "#fd7e14", + 35: "#8bc34a", + 65: "#6610f2", + 67: "#17a2b8", + 70: "#ff4444", + 71: "#ff66cc", + 73: "#9999ff", +}; + +// Aliases for pages that expect different names. +window.PORT_MAP = window.PORT_LABEL_MAP; +window.PORT_COLORS = window.PORT_COLOR_MAP; diff --git a/meshview/store.py b/meshview/store.py index 9f84739c..8d7fac61 100644 --- a/meshview/store.py +++ b/meshview/store.py @@ -1,139 +1,13 @@ -import datetime +import logging +from datetime import datetime, timedelta, timezone -from sqlalchemy import select, func +from sqlalchemy import Text, and_, cast, func, or_, select from sqlalchemy.orm import lazyload -from meshtastic.config_pb2 import Config -from meshtastic.portnums_pb2 import PortNum -from meshtastic.mesh_pb2 import User, HardwareModel -from meshview import database -from meshview import decode_payload -from meshview.models import Packet, PacketSeen, Node, Traceroute -from meshview import notify +from meshview import database, models +from meshview.models import Node, Packet, PacketSeen, Traceroute - -async def process_envelope(topic, env): - if not env.packet.id: - return - - async with database.async_session() as session: - result = await session.execute(select(Packet).where(Packet.id == env.packet.id)) - new_packet = False - packet = result.scalar_one_or_none() - if not packet: - new_packet = True - packet = Packet( - id=env.packet.id, - portnum=env.packet.decoded.portnum, - from_node_id=getattr(env.packet, "from"), - to_node_id=env.packet.to, - payload=env.packet.SerializeToString(), - import_time=datetime.datetime.utcnow(), - ) - session.add(packet) - - result = await session.execute( - select(PacketSeen).where( - PacketSeen.packet_id == env.packet.id, - PacketSeen.node_id == int(env.gateway_id[1:], 16), - PacketSeen.rx_time == env.packet.rx_time, - ) - ) - seen = None - if not result.scalar_one_or_none(): - seen = PacketSeen( - packet_id=env.packet.id, - node_id=int(env.gateway_id[1:], 16), - channel=env.channel_id, - rx_time=env.packet.rx_time, - rx_snr=env.packet.rx_snr, - rx_rssi=env.packet.rx_rssi, - hop_limit=env.packet.hop_limit, - hop_start=env.packet.hop_start, - topic=topic, - import_time=datetime.datetime.utcnow(), - ) - session.add(seen) - - if env.packet.decoded.portnum == PortNum.NODEINFO_APP: - user = decode_payload.decode_payload( - PortNum.NODEINFO_APP, env.packet.decoded.payload - ) - if user: - result = await session.execute(select(Node).where(Node.id == user.id)) - if user.id and user.id[0] == "!": - try: - node_id = int(user.id[1:], 16) - except ValueError: - node_id = None - pass - else: - node_id = None - - try: - hw_model = HardwareModel.Name(user.hw_model) - except ValueError: - hw_model = "unknown" - try: - role = Config.DeviceConfig.Role.Name(user.role) - except ValueError: - role = "unknown" - - if node := result.scalar_one_or_none(): - node.node_id = node_id - node.long_name = user.long_name - node.short_name = user.short_name - node.hw_model = hw_model - node.role = role - else: - node = Node( - id=user.id, - node_id=node_id, - long_name=user.long_name, - short_name=user.short_name, - hw_model=hw_model, - role=role, - ) - session.add(node) - - if env.packet.decoded.portnum == PortNum.POSITION_APP: - position = decode_payload.decode_payload( - PortNum.POSITION_APP, env.packet.decoded.payload - ) - if position and position.latitude_i and position.longitude_i: - from_node_id = getattr(env.packet, 'from') - node = (await session.execute(select(Node).where(Node.node_id == from_node_id))).scalar_one_or_none() - if node: - node.last_lat = position.latitude_i - node.last_long = position.longitude_i - session.add(node) - - if env.packet.decoded.portnum == PortNum.TRACEROUTE_APP: - packet_id = None - if env.packet.decoded.want_response: - packet_id = env.packet.id - else: - result = await session.execute(select(Packet).where(Packet.id == env.packet.decoded.request_id)) - if result.scalar_one_or_none(): - packet_id = env.packet.decoded.request_id - if packet_id is not None: - session.add(Traceroute( - packet_id=packet_id, - route=env.packet.decoded.payload, - done=not env.packet.decoded.want_response, - gateway_node_id=int(env.gateway_id[1:], 16), - import_time=datetime.datetime.utcnow(), - )) - - await session.commit() - if new_packet: - await packet.awaitable_attrs.to_node - await packet.awaitable_attrs.from_node - notify.notify_packet(packet.to_node_id, packet) - notify.notify_packet(packet.from_node_id, packet) - notify.notify_packet(None, packet) - if seen: - notify.notify_uplinked(seen.node_id, packet) +logger = logging.getLogger(__name__) async def get_node(node_id): @@ -153,27 +27,63 @@ async def get_fuzzy_nodes(query): return result.scalars() -async def get_packets(node_id=None, portnum=None, since=None, limit=500, before=None, after=None): +async def get_packets( + from_node_id=None, + to_node_id=None, + node_id=None, # legacy + portnum=None, + after=None, + contains=None, # substring search + limit=50, +): async with database.async_session() as session: - q = select(Packet) - - if node_id: - q = q.where( - (Packet.from_node_id == node_id) | (Packet.to_node_id == node_id) + stmt = select(models.Packet) + conditions = [] + + # Strict FROM filter + if from_node_id is not None: + conditions.append(models.Packet.from_node_id == from_node_id) + + # Strict TO filter + if to_node_id is not None: + conditions.append(models.Packet.to_node_id == to_node_id) + + # Legacy node_id (either direction) + if node_id is not None: + conditions.append( + or_( + models.Packet.from_node_id == node_id, + models.Packet.to_node_id == node_id, + ) ) - if portnum: - q = q.where(Packet.portnum == portnum) - if since: - q = q.where(Packet.import_time > (datetime.datetime.utcnow() - since)) - if before: - q = q.where(Packet.import_time < before) - if after: - q = q.where(Packet.import_time > after) - if limit is not None: - q = q.limit(limit) - - result = await session.execute(q.order_by(Packet.import_time.desc())) - return result.scalars() + + # Port filter + if portnum is not None: + conditions.append(models.Packet.portnum == portnum) + + # Timestamp filter using microseconds + if after is not None: + conditions.append(models.Packet.import_time_us > after) + + # Case-insensitive substring search on payload (BLOB → TEXT) + if contains: + contains_lower = f"%{contains.lower()}%" + payload_text = cast(models.Packet.payload, Text) + conditions.append(func.lower(payload_text).like(contains_lower)) + + # Apply WHERE conditions + if conditions: + stmt = stmt.where(and_(*conditions)) + + # Order by newest first + stmt = stmt.order_by(models.Packet.import_time_us.desc()) + + # Limit + stmt = stmt.limit(limit) + + # Run query + result = await session.execute(stmt) + return result.scalars().all() async def get_packets_from(node_id=None, portnum=None, since=None, limit=500): @@ -181,14 +91,14 @@ async def get_packets_from(node_id=None, portnum=None, since=None, limit=500): q = select(Packet) if node_id: - q = q.where( - Packet.from_node_id == node_id - ) + q = q.where(Packet.from_node_id == node_id) if portnum: q = q.where(Packet.portnum == portnum) if since: - q = q.where(Packet.import_time > (datetime.datetime.utcnow() - since)) - result = await session.execute(q.limit(limit).order_by(Packet.import_time.desc())) + now_us = int(datetime.now().timestamp() * 1_000_000) + start_us = now_us - int(since.total_seconds() * 1_000_000) + q = q.where(Packet.import_time_us > start_us) + result = await session.execute(q.limit(limit).order_by(Packet.import_time_us.desc())) return result.scalars() @@ -199,21 +109,12 @@ async def get_packet(packet_id): return result.scalar_one_or_none() -async def get_uplinked_packets(node_id, portnum=None): - async with database.async_session() as session: - q = select(Packet).join(PacketSeen).where(PacketSeen.node_id == node_id).order_by(Packet.import_time.desc()).limit(500) - if portnum: - q = q.where(Packet.portnum == portnum) - result = await session.execute(q) - return result.scalars() - - async def get_packets_seen(packet_id): async with database.async_session() as session: result = await session.execute( select(PacketSeen) .where(PacketSeen.packet_id == packet_id) - .order_by(PacketSeen.import_time.desc()) + .order_by(PacketSeen.import_time_us.desc()) ) return result.scalars() @@ -221,41 +122,51 @@ async def get_packets_seen(packet_id): async def has_packets(node_id, portnum): async with database.async_session() as session: return bool( - (await session.execute( + ( + await session.execute( select(Packet.id).where(Packet.from_node_id == node_id).limit(1) - )).scalar() + ) + ).scalar() ) async def get_traceroute(packet_id): async with database.async_session() as session: result = await session.execute( - select(Traceroute) - .where(Traceroute.packet_id == packet_id) - .order_by(Traceroute.import_time) + select(Traceroute) + .where(Traceroute.packet_id == packet_id) + .order_by(Traceroute.import_time_us) ) return result.scalars() async def get_traceroutes(since): + if isinstance(since, datetime): + since_us = int(since.timestamp() * 1_000_000) + else: + since_us = int(since) async with database.async_session() as session: - result = await session.execute( - select(Traceroute) - .join(Packet) - .where(Traceroute.import_time > (datetime.datetime.utcnow() - since)) - .order_by(Traceroute.import_time) + stmt = ( + select(Traceroute) + .where(Traceroute.import_time_us > since_us) + .order_by(Traceroute.import_time_us) ) - return result.scalars() + stream = await session.stream_scalars(stmt) + async for tr in stream: + yield tr async def get_mqtt_neighbors(since): + now_us = int(datetime.now().timestamp() * 1_000_000) + start_us = now_us - int(since.total_seconds() * 1_000_000) async with database.async_session() as session: - result = await session.execute(select(PacketSeen, Packet) + result = await session.execute( + select(PacketSeen, Packet) .join(Packet) .where( (PacketSeen.hop_limit == PacketSeen.hop_start) & (PacketSeen.hop_start != 0) - & (PacketSeen.import_time > (datetime.datetime.utcnow() - since)) + & (PacketSeen.import_time_us > start_us) ) .options( lazyload(Packet.from_node), @@ -264,3 +175,376 @@ async def get_mqtt_neighbors(since): ) return result + +async def get_total_node_count(channel: str = None) -> int: + try: + async with database.async_session() as session: + now_us = int(datetime.now(timezone.utc).timestamp() * 1_000_000) + cutoff_us = now_us - 86400 * 1_000_000 + q = select(func.count(Node.id)).where(Node.last_seen_us > cutoff_us) + + if channel: + q = q.where(Node.channel == channel) + + result = await session.execute(q) + return result.scalar() + except Exception as e: + print(f"An error occurred: {e}") + return 0 + + +async def get_top_traffic_nodes(): + try: + async with database.async_session() as session: + now_us = int(datetime.now(timezone.utc).timestamp() * 1_000_000) + cutoff_us = now_us - 86400 * 1_000_000 + total_packets_sent = func.count(func.distinct(Packet.id)).label("total_packets_sent") + total_times_seen = func.count(PacketSeen.packet_id).label("total_times_seen") + + stmt = ( + select( + Node.node_id, + Node.long_name, + Node.short_name, + Node.channel, + total_packets_sent, + total_times_seen, + ) + .select_from(Node) + .outerjoin( + Packet, + (Packet.from_node_id == Node.node_id) & (Packet.import_time_us >= cutoff_us), + ) + .outerjoin(PacketSeen, PacketSeen.packet_id == Packet.id) + .group_by(Node.node_id, Node.long_name, Node.short_name, Node.channel) + .having(total_packets_sent > 0) + .order_by(total_times_seen.desc()) + ) + + rows = (await session.execute(stmt)).all() + + nodes = [ + { + 'node_id': row[0], + 'long_name': row[1], + 'short_name': row[2], + 'channel': row[3], + 'total_packets_sent': row[4], + 'total_times_seen': row[5], + } + for row in rows + ] + return nodes + + except Exception as e: + print(f"Error retrieving top traffic nodes: {str(e)}") + return [] + + +async def get_node_traffic(node_id: int): + try: + async with database.async_session() as session: + now_us = int(datetime.now(timezone.utc).timestamp() * 1_000_000) + cutoff_us = now_us - 86400 * 1_000_000 + packet_count = func.count().label("packet_count") + + stmt = ( + select(Node.long_name, Packet.portnum, packet_count) + .select_from(Packet) + .join(Node, Packet.from_node_id == Node.node_id) + .where(Node.node_id == node_id) + .where(Packet.import_time_us >= cutoff_us) + .group_by(Node.long_name, Packet.portnum) + .order_by(packet_count.desc()) + ) + + result = await session.execute(stmt) + return [ + { + "long_name": row.long_name, + "portnum": row.portnum, + "packet_count": row.packet_count, + } + for row in result.all() + ] + + except Exception as e: + # Log the error or handle it as needed + print(f"Error fetching node traffic: {str(e)}") + return [] + + +async def get_nodes(node_id=None, role=None, channel=None, hw_model=None, days_active=None): + """ + Fetches nodes from the database based on optional filtering criteria. + + Parameters: + node_id + role (str, optional): The role of the node (converted to uppercase for consistency). + channel (str, optional): The communication channel associated with the node. + hw_model (str, optional): The hardware model of the node. + + Returns: + list: A list of Node objects that match the given criteria. + """ + try: + async with database.async_session() as session: + # print(channel) # Debugging output (consider replacing with logging) + + # Start with a base query selecting all nodes + query = select(Node) + + # Apply filters based on provided parameters + if node_id is not None: + try: + node_id_int = int(node_id) + except (TypeError, ValueError): + node_id_int = node_id + query = query.where(Node.node_id == node_id_int) + if role is not None: + query = query.where(Node.role == role.upper()) # Ensure role is uppercase + if channel is not None: + query = query.where(Node.channel == channel) + if hw_model is not None: + query = query.where(Node.hw_model == hw_model) + + if days_active is not None: + now_us = int(datetime.now(timezone.utc).timestamp() * 1_000_000) + cutoff_us = now_us - int(timedelta(days_active).total_seconds() * 1_000_000) + query = query.where(Node.last_seen_us > cutoff_us) + + # Exclude nodes with missing last_seen_us + query = query.where(Node.last_seen_us.is_not(None)) + + # Order results by long_name in ascending order + query = query.order_by(Node.short_name.asc()) + + # Execute the query and retrieve results + result = await session.execute(query) + nodes = result.scalars().all() + return nodes # Return the list of nodes + + except Exception: + logger.exception("error reading DB") + return [] # Return an empty list in case of failure + + +async def get_packet_stats( + period_type: str = "day", + length: int = 14, + channel: str | None = None, + portnum: int | None = None, + to_node: int | None = None, + from_node: int | None = None, +): + now = datetime.now(timezone.utc) + + if period_type == "hour": + start_time = now - timedelta(hours=length) + time_format_sqlite = "%Y-%m-%d %H:00" + time_format_pg = "YYYY-MM-DD HH24:00" + elif period_type == "day": + start_time = now - timedelta(days=length) + time_format_sqlite = "%Y-%m-%d" + time_format_pg = "YYYY-MM-DD" + else: + raise ValueError("period_type must be 'hour' or 'day'") + + async with database.async_session() as session: + dialect = session.get_bind().dialect.name + if dialect == "postgresql": + period_expr = func.to_char( + func.to_timestamp(Packet.import_time_us / 1_000_000.0), + time_format_pg, + ) + else: + period_expr = func.strftime( + time_format_sqlite, + func.datetime(Packet.import_time_us / 1_000_000, "unixepoch"), + ) + + q = select( + period_expr.label("period"), + func.count().label("count"), + ).where(Packet.import_time_us >= int(start_time.timestamp() * 1_000_000)) + + # Filters + if channel: + q = q.where(func.lower(Packet.channel) == channel.lower()) + if portnum is not None: + q = q.where(Packet.portnum == portnum) + if to_node is not None: + q = q.where(Packet.to_node_id == to_node) + if from_node is not None: + q = q.where(Packet.from_node_id == from_node) + + q = q.group_by('period').order_by('period') + + result = await session.execute(q) + data = [{"period": row.period, "count": row.count} for row in result] + + return { + "period_type": period_type, + "length": length, + "channel": channel, + "portnum": portnum, + "to_node": to_node, + "from_node": from_node, + "data": data, + } + + +async def get_channels_in_period(period_type: str = "hour", length: int = 24): + """ + Returns a sorted list of distinct channels used in packets over a given period. + period_type: "hour" or "day" + length: number of hours or days to look back + """ + now_us = int(datetime.utcnow().timestamp() * 1_000_000) + + if period_type == "hour": + delta_us = length * 3600 * 1_000_000 + elif period_type == "day": + delta_us = length * 86400 * 1_000_000 + else: + raise ValueError("period_type must be 'hour' or 'day'") + + start_us = now_us - delta_us + + async with database.async_session() as session: + stmt = ( + select(Packet.channel) + .where(Packet.import_time_us >= start_us) + .distinct() + .order_by(Packet.channel) + ) + + result = await session.execute(stmt) + + channels = [ch for ch in result.scalars().all() if ch is not None] + + return channels + + +async def get_total_packet_count( + period_type: str | None = None, + length: int | None = None, + channel: str | None = None, + from_node: int | None = None, + to_node: int | None = None, +): + """ + Count total packets, with ALL filters optional. + If no filters -> return ALL packets ever. + Uses import_time_us (microseconds). + """ + + # CASE 1: no filters -> count everything + if ( + period_type is None + and length is None + and channel is None + and from_node is None + and to_node is None + ): + async with database.async_session() as session: + q = select(func.count(Packet.id)) + res = await session.execute(q) + return res.scalar() or 0 + + # CASE 2: filtered mode -> compute time window using import_time_us + now_us = int(datetime.now().timestamp() * 1_000_000) + + if period_type is None: + period_type = "day" + if length is None: + length = 1 + + if period_type == "hour": + start_time_us = now_us - (length * 3600 * 1_000_000) + elif period_type == "day": + start_time_us = now_us - (length * 86400 * 1_000_000) + else: + raise ValueError("period_type must be 'hour' or 'day'") + + async with database.async_session() as session: + q = select(func.count(Packet.id)).where(Packet.import_time_us >= start_time_us) + + if channel: + q = q.where(func.lower(Packet.channel) == channel.lower()) + if from_node: + q = q.where(Packet.from_node_id == from_node) + if to_node: + q = q.where(Packet.to_node_id == to_node) + + res = await session.execute(q) + return res.scalar() or 0 + + +async def get_total_packet_seen_count( + packet_id: int | None = None, + period_type: str | None = None, + length: int | None = None, + channel: str | None = None, + from_node: int | None = None, + to_node: int | None = None, +): + """ + Count total PacketSeen rows. + - If packet_id is provided -> count only that packet's seen entries. + - Otherwise match EXACT SAME FILTERS as get_total_packet_count. + Uses import_time_us for time window. + """ + + # SPECIAL CASE: direct packet_id lookup + if packet_id is not None: + async with database.async_session() as session: + q = select(func.count(PacketSeen.packet_id)).where(PacketSeen.packet_id == packet_id) + res = await session.execute(q) + return res.scalar() or 0 + + # No filters -> return ALL seen entries + if ( + period_type is None + and length is None + and channel is None + and from_node is None + and to_node is None + ): + async with database.async_session() as session: + q = select(func.count(PacketSeen.packet_id)) + res = await session.execute(q) + return res.scalar() or 0 + + # Compute time window + now_us = int(datetime.now().timestamp() * 1_000_000) + + if period_type is None: + period_type = "day" + if length is None: + length = 1 + + if period_type == "hour": + start_time_us = now_us - (length * 3600 * 1_000_000) + elif period_type == "day": + start_time_us = now_us - (length * 86400 * 1_000_000) + else: + raise ValueError("period_type must be 'hour' or 'day'") + + # JOIN Packet so we can apply identical filters + async with database.async_session() as session: + q = ( + select(func.count(PacketSeen.packet_id)) + .join(Packet, Packet.id == PacketSeen.packet_id) + .where(Packet.import_time_us >= start_time_us) + ) + + if channel: + q = q.where(func.lower(Packet.channel) == channel.lower()) + if from_node: + q = q.where(Packet.from_node_id == from_node) + if to_node: + q = q.where(Packet.to_node_id == to_node) + + res = await session.execute(q) + return res.scalar() or 0 diff --git a/meshview/templates/base.html b/meshview/templates/base.html index 763811f3..44f3d0d4 100644 --- a/meshview/templates/base.html +++ b/meshview/templates/base.html @@ -1,42 +1,202 @@ - - - MeshView {% if node and node.short_name %}-- {{node.short_name}}{% endif %} - - - - - - - - - {% block head %} - {% endblock %} - - + + + +
+ +
+ + +
+ +{% block body %}{% endblock %} + +
+ +
ver. unknown
+
+ + + + diff --git a/meshview/templates/buttons.html b/meshview/templates/buttons.html deleted file mode 100644 index 9959bff3..00000000 --- a/meshview/templates/buttons.html +++ /dev/null @@ -1,16 +0,0 @@ - diff --git a/meshview/templates/chat.html b/meshview/templates/chat.html index b40e7be1..bd719fb6 100644 --- a/meshview/templates/chat.html +++ b/meshview/templates/chat.html @@ -2,23 +2,243 @@ {% block css %} .timestamp { - min-width:10em; + min-width: 10em; + color: #ccc; } -.chat-packet:nth-of-type(odd){ - background-color:#CCC; + +.chat-packet:nth-of-type(odd) { background-color: #3a3a3a; } +.chat-packet { + border-bottom: 1px solid #555; + padding: 3px 6px; + border-radius: 6px; + margin: 0; } -.chat-packet:nth-of-type(even){ - background-color:#EEE; + +/* Same column spacing as before */ +.chat-packet > [class^="col-"] { + padding-left: 10px !important; + padding-right: 10px !important; + padding-top: 1px !important; + padding-bottom: 1px !important; +} + +.chat-packet:nth-of-type(even) { background-color: #333333; } + +.channel { + font-style: italic; + color: #bbb; +} +.channel a { + font-style: normal; + color: #999; } -{% endblock %} +@keyframes flash { + 0% { background-color: #ffe066; } + 100% { background-color: inherit; } +} +.chat-packet.flash { animation: flash 3.5s ease-out; } + +.replying-to { + font-size: 0.8em; + color: #aaa; + margin-top: 2px; + padding-left: 10px; +} +.replying-to .reply-preview { color: #aaa; } +{% endblock %} {% block body %} -
- {% for packet in packets %} - {% include 'chat_packet.html' %} - {% else %} - No packets found. - {% endfor %} +
+ + +
+

+ 💬 + +

+ +
+
+ + + {% endblock %} diff --git a/meshview/templates/chat_packet.html b/meshview/templates/chat_packet.html deleted file mode 100644 index 1c95df46..00000000 --- a/meshview/templates/chat_packet.html +++ /dev/null @@ -1,5 +0,0 @@ -
- {{packet.import_time | format_timestamp}} ✉️ - {{packet.from_node.long_name or (packet.from_node_id | node_id_to_hex) }} - {{packet.payload}} -
diff --git a/meshview/templates/datalist.html b/meshview/templates/datalist.html deleted file mode 100644 index 5c65c4bb..00000000 --- a/meshview/templates/datalist.html +++ /dev/null @@ -1,7 +0,0 @@ - - {% for option in node_options %} - - {% endfor %} - diff --git a/meshview/templates/error.html b/meshview/templates/error.html new file mode 100644 index 00000000..a847e550 --- /dev/null +++ b/meshview/templates/error.html @@ -0,0 +1,19 @@ +{% extends "base.html" %} + +{% block body %} +
+

Error

+

{{ error_message }}

+ + {% if error_details %} +
+ Error Details + +
{{error_details}}
+
+
+ {% endif %} + + +
+{% endblock %} diff --git a/meshview/templates/firehose.html b/meshview/templates/firehose.html index fcfe27fe..790b4d69 100644 --- a/meshview/templates/firehose.html +++ b/meshview/templates/firehose.html @@ -1,39 +1,404 @@ {% extends "base.html" %} +{% block css %} +.container { + margin: 0 auto; + padding: 10px; +} +#pause-button { + white-space: nowrap; + padding: 4px 10px; + font-size: 0.9rem; + border-radius: 6px; +} + +.port-tag { + display: inline-block; + padding: 2px 6px; + border-radius: 6px; + font-size: 0.75rem; + font-weight: 500; + color: #fff; +} + +/* Packet table */ +.packet-table { + width: 100%; + border-collapse: collapse; + font-size: 0.85rem; + color: #e4e9ee; +} + +.packet-table th, .packet-table td { + border: 1px solid #3a3f44; + padding: 6px 10px; + text-align: left; +} +.packet-table th { + background-color: #1f2226; + font-weight: bold; +} +.packet-table tr:nth-of-type(odd) { background-color: #272b2f; } +.packet-table tr:nth-of-type(even) { background-color: #212529; } + +/* Port tag */ +.port-tag { + display: inline-block; + padding: 1px 6px; + border-radius: 6px; + font-size: 0.75rem; + font-weight: 500; + color: #fff; +} + +.to-mqtt { font-style: italic; color: #aaa; } + +/* Payload rows */ +.payload-row { display: none; background-color: #1b1e22; } +.payload-cell { + padding: 8px 12px; + font-family: monospace; + white-space: pre-wrap; + color: #b0bec5; +} +.packet-table tr.expanded + .payload-row { + display: table-row; +} + +/* Toggle arrow */ +.toggle-btn { + cursor: pointer; + color: #aaa; + margin-right: 6px; + font-weight: bold; +} +.toggle-btn:hover { color: #fff; } + +/* Inline link next to port tag */ +.inline-link { + margin-left: 6px; + font-weight: bold; + text-decoration: none; + color: #9fd4ff; +} +.inline-link:hover { + color: #c7e6ff; +} +{% endblock %} + {% block body %} -
-
- {% set options = { - 1: "Text Message", - 3: "Position", - 4: "Node Info", - 67: "Telemetry", - 71: "Neighbor Info", - } - %} - - +
+ + +

📡 Live Feed

+ + -
-
- {% for packet in packets %} - {% include 'packet.html' %} - {% else %} - No packets found. - {% endfor %} -
-
-
+ + + + + + + + + + + + +
TimePacket IDFromToPort
+
+ + + + {% endblock %} diff --git a/meshview/templates/index.html b/meshview/templates/index.html index a8c5223b..b26bf0b8 100644 --- a/meshview/templates/index.html +++ b/meshview/templates/index.html @@ -2,7 +2,4 @@ {% block body %} {% include "search_form.html" %} -

See a realtime graph of the network

-

See what people are saying

-

See everything

-{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/meshview/templates/map.html b/meshview/templates/map.html new file mode 100644 index 00000000..4ca22ccb --- /dev/null +++ b/meshview/templates/map.html @@ -0,0 +1,560 @@ +{% extends "base.html" %} + +{% block css %} + + +{% endblock %} + +{% block body %} + +
+ +
+
+ + Traceroute Path (arrowed) +
+ +
+ + Neighbor Link +
+
+ +
+ + Show Routers Only +
+ +
+ + +
+ + + + + + + +{% endblock %} diff --git a/meshview/templates/net.html b/meshview/templates/net.html index e0288f0e..794b9133 100644 --- a/meshview/templates/net.html +++ b/meshview/templates/net.html @@ -1,27 +1,233 @@ {% extends "base.html" %} {% block css %} - #packet_details{ - height: 95vh; - overflow: scroll; - } +.timestamp { min-width: 10em; color: #ccc; } + +.chat-packet:nth-of-type(odd) { background-color: #3a3a3a; } +.chat-packet { + border-bottom: 1px solid #555; + padding: 3px 6px; + border-radius: 6px; + margin: 0; +} + +.chat-packet > [class^="col-"] { + padding-left: 10px !important; + padding-right: 10px !important; + padding-top: 1px !important; + padding-bottom: 1px !important; +} + +.chat-packet:nth-of-type(even) { background-color: #333333; } + +.channel { font-style: italic; color: #bbb; } +.channel a { font-style: normal; color: #999; } + +#weekly-message { margin: 15px 0; font-weight: bold; color: #ffeb3b; } +#total-count { margin-bottom: 10px; font-style: italic; color: #ccc; } {% endblock %} {% block body %} -
-
-
- {% for packet in net_packets %} - {% include 'net_packet.html' %} - {% endfor %} -
-
-
+
+ +
+

+ 💬 + +

+
+ + +
+ + + +
+ Total messages: + 0 +
+ +
+
-{% endblock body %} + + +{% endblock %} diff --git a/meshview/templates/net_packet.html b/meshview/templates/net_packet.html deleted file mode 100644 index 7582a1f2..00000000 --- a/meshview/templates/net_packet.html +++ /dev/null @@ -1,12 +0,0 @@ -
-
- {{packet.from_node.long_name}} - 🔎 -
-
-
-
{{packet.import_time | format_timestamp}}
-
{{packet.payload}}
-
-
-
diff --git a/meshview/templates/node.html b/meshview/templates/node.html index d5cb0127..ea75602f 100644 --- a/meshview/templates/node.html +++ b/meshview/templates/node.html @@ -1,95 +1,1442 @@ {% extends "base.html" %} {% block css %} - #node_info { - height:100%; - } - #map{ - height:100%; - min-height: 400px; - } - #packet_details{ - height: 95vh; - overflow: scroll; - top: 3em; - } +{{ super() }} + +/* --- Map --- */ +#map { + width: 100%; + height: 400px; + margin-bottom: 20px; + border-radius: 8px; + display: block; +} +.leaflet-container { + background: #1a1a1a; + z-index: 1; +} + +/* --- Node Info --- */ +.node-info { + background-color: #1f2226; + border: 1px solid #3a3f44; + color: #ddd; + font-size: 0.88rem; + padding: 12px 14px; + margin-bottom: 14px; + border-radius: 8px; + + display: grid; + grid-template-columns: repeat(3, minmax(120px, 1fr)); + grid-column-gap: 14px; + grid-row-gap: 6px; +} + +.node-info div { padding: 2px 0; } +.node-info strong { + color: #9fd4ff; + font-weight: 600; +} + +/* --- Charts --- */ +.chart-container { + width: 100%; + height: 380px; + margin-bottom: 25px; + border: 1px solid #3a3f44; + border-radius: 8px; + overflow: hidden; + background-color: #16191d; +} +.chart-header { + display: flex; + justify-content: space-between; + align-items: center; + background: #1f2226; + padding: 6px 12px; + font-weight: bold; + border-bottom: 1px solid #333; + font-size: 1rem; +} +.chart-actions button { + background: rgba(255,255,255,0.05); + border: 1px solid #555; + border-radius: 4px; + color: #ccc; + font-size: 0.8rem; + padding: 2px 6px; + cursor: pointer; +} +.chart-actions button:hover { + color: #fff; + background: rgba(255,255,255,0.15); + border-color: #888; +} + +/* --- Packet Table --- */ +.packet-table { + width: 100%; + border-collapse: collapse; + font-size: 0.85rem; + color: #e4e9ee; +} +.packet-table th, .packet-table td { + border: 1px solid #3a3f44; + padding: 6px 10px; + text-align: left; +} +.packet-table th { + background-color: #1f2226; + font-weight: bold; +} +.packet-table tr:nth-of-type(odd) { background-color: #272b2f; } +.packet-table tr:nth-of-type(even) { background-color: #212529; } + +.port-tag { + padding: 2px 6px; + border-radius: 6px; + font-size: 0.75rem; + color: #fff; +} + +.to-mqtt { font-style: italic; color: #aaa; } + +.payload-row { display: none; background-color: #1b1e22; } +.payload-cell { + padding: 8px 12px; + font-family: monospace; + white-space: pre-wrap; + color: #b0bec5; +} +.packet-table tr.expanded + .payload-row { display: table-row; } +.toggle-btn { cursor: pointer; color: #aaa; margin-right: 6px; } +.toggle-btn:hover { color: #fff; } + +/* --- Chart Modal --- */ +#chartModal { + display:none; position:fixed; top:0; left:0; width:100%; height:100%; + background:rgba(0,0,0,0.9); z-index:9999; + align-items:center; justify-content:center; +} +#chartModal > div { + background:#1b1e22; border-radius:8px; + width:90%; height:85%; padding:10px; +} + +/* Inline link */ +.inline-link { + margin-left: 6px; + font-weight: bold; + text-decoration: none; + color: #9fd4ff; +} +.inline-link:hover { color: #c7e6ff; } {% endblock %} {% block body %} +
-{% include "search_form.html" %} - -
-
-
-
-
- {% if node %} -
- {{node.long_name}} ({{node.node_id|node_id_to_hex}}) -
-
-
-
short name
-
{{node.short_name}}
-
hw model
-
{{node.hw_model}}
-
role
-
{{node.role}}
-
- {% include "node_graphs.html" %} -
- {% else %} -
- A NodeInfo has not been seen. -
- {% endif %} -
-
-
-
+
+ 📡 Specifications:: + +
+ + +
+
Node ID:
+
Hex ID:
+
Long Name:
+
Short Name:
+ +
Hardware Model:
+
Firmware:
+
Role:
+ +
Channel:
+
Latitude:
+
Longitude:
+ +
Last Update:
+
+ Statistics: + +
+ + +
+ + +
+ + +
+
+ 🔋 Battery & Voltage +
+ +
+
+
-
-
- {% include "buttons.html" %} + +
+
+ 📶 Air & Channel Utilization +
+ +
+
+
-
- {% include 'packet_list.html' %} -
+ + + + + +
+
+ 📡 Neighbors (SNR Over Time) +
+ + +
+
-{% if trace %} + +
+
+ 📊 Packets per Day (Last 7 Days) +
+ + +
+
+
+
+ + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + +
TimePacket IDFromToPortSize
+ +
+ + +
+
+
+ +
+
+
+
+ + + + -{% endif %} +} + +function hideMap(){ + const mapDiv = document.getElementById("map"); + if (mapDiv) { + mapDiv.style.display = "none"; + } +} + +function addMarker(id, lat, lon, color = "red", node = null) { + if (!map) return; + if (isNaN(lat) || isNaN(lon)) return; + + nodePositions[id] = [lat, lon]; + + if (!node) { + node = nodeCache[id] || null; + } + + const popupHtml = node ? makeNodePopup(node) : `${id}`; + + const m = L.circleMarker([lat, lon], { + radius: 6, + color, + fillColor: color, + fillOpacity: 1 + }).addTo(map).bindPopup(popupHtml); + + markers[id] = m; + m.bringToFront(); +} + +async function drawNeighbors(src, nids) { + if (!map) return; + + // Ensure source node position exists + const srcNode = await fetchNodeFromApi(src); + if (!srcNode || !srcNode.last_lat || !srcNode.last_long) return; + + const srcLat = srcNode.last_lat / 1e7; + const srcLon = srcNode.last_long / 1e7; + nodePositions[src] = [srcLat, srcLon]; + + for (const nid of nids) { + const neighbor = await fetchNodeFromApi(nid); + if (!neighbor || !neighbor.last_lat || !neighbor.last_long) continue; + + const lat = neighbor.last_lat / 1e7; + const lon = neighbor.last_long / 1e7; + + nodePositions[nid] = [lat, lon]; + + // Marker + addMarker(nid, lat, lon, "blue", neighbor); + + // Link line + L.polyline( + [[srcLat, srcLon], [lat, lon]], + { color: "gray", weight: 1 } + ).addTo(map); + } + + ensureMapVisible(); +} + + +function ensureMapVisible(){ + if (!map) return; + requestAnimationFrame(() => { + map.invalidateSize(); + const group = L.featureGroup(Object.values(markers)); + if (group.getLayers().length > 0) { + map.fitBounds(group.getBounds(), { + padding: [20, 20], + maxZoom: 11 + }); + } + }); +} + +/* ====================================================== + POSITION TRACK (portnum=3) + ====================================================== */ + +async function loadTrack(){ + try { + const url = new URL("/api/packets", window.location.origin); + url.searchParams.set("portnum", 3); + url.searchParams.set("from_node_id", fromNodeId); + url.searchParams.set("limit", 50); + + const res = await fetch(url); + if (!res.ok) { + hideMap(); + return; + } + + const data = await res.json(); + const packets = data.packets || []; + const points = []; + + for (const pkt of packets) { + if (!pkt.payload) continue; + const latMatch = pkt.payload.match(/latitude_i:\s*(-?\d+)/); + const lonMatch = pkt.payload.match(/longitude_i:\s*(-?\d+)/); + if (!latMatch || !lonMatch) continue; + + const lat = parseInt(latMatch[1], 10) / 1e7; + const lon = parseInt(lonMatch[1], 10) / 1e7; + if (isNaN(lat) || isNaN(lon)) continue; + + points.push({ + lat, + lon, + time: pkt.import_time_us + }); + } + + if (!points.length) { + hideMap(); + return; + } + + // Sort chronologically (oldest -> newest) + points.sort((a, b) => a.time - b.time); + + // Track node's last known position + const latest = points[points.length - 1]; + nodePositions[fromNodeId] = [latest.lat, latest.lon]; + + if (!map) { + initMap(); + } + + const latlngs = points.map(p => [p.lat, p.lon]); + const trackLine = L.polyline(latlngs, { + color: '#052152', + weight: 2 + }).addTo(map); + + const first = points[0]; + const last = points[points.length - 1]; + + const node = currentNode || nodeCache[fromNodeId] || null; + + const startMarker = L.circleMarker([first.lat, first.lon], { + radius: 6, + color: 'green', + fillColor: 'green', + fillOpacity: 1 + }).addTo(map).bindPopup(node ? makeNodePopup(node) : "Start"); + + const endMarker = L.circleMarker([last.lat, last.lon], { + radius: 6, + color: 'red', + fillColor: 'red', + fillOpacity: 1 + }).addTo(map).bindPopup(node ? makeNodePopup(node) : "Latest"); + + markers["__track_start"] = startMarker; + markers["__track_end"] = endMarker; + + map.fitBounds(trackLine.getBounds(), { padding:[20,20] }); + + } catch (err) { + console.error("Failed to load track:", err); + hideMap(); + } +} + +/* ====================================================== + PACKETS TABLE + NEIGHBOR OVERLAY + ====================================================== */ + +async function loadPackets(filters = {}) { + const list = document.getElementById("packet_list"); + list.innerHTML = ""; + + const url = new URL("/api/packets", window.location.origin); + url.searchParams.set("node_id", fromNodeId); + url.searchParams.set("limit", 1000); + + if (filters.since) { + url.searchParams.set("since", filters.since); + } + if (filters.portnum) { + url.searchParams.set("portnum", filters.portnum); + } + + const res = await fetch(url); + if (!res.ok) return; + + const data = await res.json(); + const packets = data.packets || []; + currentPacketRows = packets; + + for (const pkt of packets.reverse()) { + + // ================================ + // TABLE ROW + // ================================ + const safePayload = (pkt.payload || "") + .replace(/[<>]/g, m => (m === "<" ? "<" : ">")); + + const localTime = formatLocalTime(pkt.import_time_us); + const fromCell = nodeLink(pkt.from_node_id, pkt.long_name); + const toCell = nodeLink(pkt.to_node_id, pkt.to_long_name); + + let inlineLinks = ""; + + if (pkt.portnum === 3 && pkt.payload) { + const latMatch = pkt.payload.match(/latitude_i:\s*(-?\d+)/); + const lonMatch = pkt.payload.match(/longitude_i:\s*(-?\d+)/); + if (latMatch && lonMatch) { + const lat = parseFloat(latMatch[1]) / 1e7; + const lon = parseFloat(lonMatch[1]) / 1e7; + inlineLinks += + ` 📍`; + } + } + + if (pkt.portnum === 70) { + let traceId = pkt.id; + const match = pkt.payload?.match(/ID:\s*(\d+)/i); + if (match) traceId = match[1]; + inlineLinks += + ` `; + } + + const sizeBytes = packetSizeBytes(pkt); + + list.insertAdjacentHTML("afterbegin", ` + + ${localTime} + + + ${pkt.id} + + + ${fromCell} + ${toCell} + ${portLabel(pkt.portnum)}${inlineLinks} + ${sizeBytes.toLocaleString()} B + + + ${safePayload} + `); + + } +} + + + +/* ====================================================== + TELEMETRY CHARTS (portnum=67) + ====================================================== */ + +async function loadTelemetryCharts(){ + const url = `/api/packets?portnum=67&from_node_id=${fromNodeId}`; + const res = await fetch(url); + if (!res.ok) return; + + const data = await res.json(); + const packets = data.packets || []; + chartData = { + times: [], + battery: [], voltage: [], + airUtil: [], chanUtil: [], + temperature: [], humidity: [], pressure: [] + }; + + for (const pkt of packets.reverse()) { + const pl = pkt.payload || ""; + const t = new Date(pkt.import_time_us / 1000); + chartData.times.push( + t.toLocaleString([], { month:"2-digit", day:"2-digit", hour:"2-digit", minute:"2-digit" }) + ); + + // Matches your payload exactly (inside device_metrics {}) + chartData.battery.push( + parseFloat(pl.match(/battery_level:\s*([\d.]+)/)?.[1] || NaN) + ); + chartData.voltage.push( + parseFloat(pl.match(/voltage:\s*([\d.]+)/)?.[1] || NaN) + ); + chartData.airUtil.push( + parseFloat(pl.match(/air_util_tx:\s*([\d.]+)/)?.[1] || NaN) + ); + chartData.chanUtil.push( + parseFloat(pl.match(/channel_utilization:\s*([\d.]+)/)?.[1] || NaN) + ); + chartData.temperature.push( + parseFloat(pl.match(/temperature:\s*([\d.]+)/)?.[1] || NaN) + ); + chartData.humidity.push( + parseFloat(pl.match(/relative_humidity:\s*([\d.]+)/)?.[1] || NaN) + ); + chartData.pressure.push( + parseFloat(pl.match(/barometric_pressure:\s*([\d.]+)/)?.[1] || NaN) + ); + } + + const hasBattery = chartData.battery.some(v => !isNaN(v)); + const hasVoltage = chartData.voltage.some(v => !isNaN(v)); + const hasAir = chartData.airUtil.some(v => !isNaN(v)); + const hasChan = chartData.chanUtil.some(v => !isNaN(v)); + const hasEnv = + chartData.temperature.some(v => !isNaN(v)) || + chartData.humidity.some(v => !isNaN(v)) || + chartData.pressure.some(v => !isNaN(v)); + + const batteryContainer = document.getElementById("battery_voltage_container"); + const airContainer = document.getElementById("air_channel_container"); + const envContainer = document.getElementById("env_chart_container"); + + const makeLine = (name, color, data, yAxisIndex = 0) => ({ + name, + type: 'line', + smooth: true, + connectNulls: true, + yAxisIndex, + showSymbol: true, + symbol: 'circle', + symbolSize: 8, + lineStyle: { + width: 2, + color, + shadowColor: color.replace('1)', '0.4)'), + shadowBlur: 8, + shadowOffsetY: 3 + }, + itemStyle: { + color, + borderColor: '#000', + borderWidth: 1 + }, + areaStyle: { + color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ + { offset: 0, color: color.replace('1)', '0.65)') }, + { offset: 0.5, color: color.replace('1)', '0.35)') }, + { offset: 1, color: 'rgba(0,0,0,0)' } + ]) + }, + data: data.map(v => isNaN(v) ? null : v) + }); + + let chart1 = null, chart2 = null, chart3 = null; + + // Battery / Voltage chart + if (hasBattery || hasVoltage) { + batteryContainer.style.display = "block"; + chart1 = echarts.init(document.getElementById('chart_battery_voltage')); + chart1.setOption({ + tooltip: { trigger:'axis' }, + legend: { data:['Battery Level','Voltage'], textStyle:{ color:'#ccc' } }, + xAxis: { type:'category', data:chartData.times, axisLabel:{ color:'#ccc' } }, + yAxis: [ + { type:'value', name:'Battery (%)', axisLabel:{ color:'#ccc' } }, + { type:'value', name:'Voltage (V)', axisLabel:{ color:'#ccc' } } + ], + series: [ + makeLine('Battery Level', 'rgba(255,214,82,1)', chartData.battery), + makeLine('Voltage', 'rgba(79,155,255,1)', chartData.voltage, 1) + ] + }); + } else { + batteryContainer.style.display = "none"; + } + + // Air / Channel chart + if (hasAir || hasChan) { + airContainer.style.display = "block"; + chart2 = echarts.init(document.getElementById('chart_air_channel')); + chart2.setOption({ + tooltip: { trigger:'axis' }, + legend: { data:['Air Util Tx','Channel Utilization'], textStyle:{ color:'#ccc' } }, + xAxis: { type:'category', data:chartData.times, axisLabel:{ color:'#ccc' } }, + yAxis: { type:'value', name:'%', axisLabel:{ color:'#ccc' } }, + series: [ + makeLine('Air Util Tx', 'rgba(138,255,108,1)', chartData.airUtil), + makeLine('Channel Utilization', 'rgba(255,102,204,1)', chartData.chanUtil) + ] + }); + } else { + airContainer.style.display = "none"; + } + + // Environment chart + if (hasEnv) { + envContainer.style.display = "block"; + chart3 = echarts.init(document.getElementById('chart_environment')); + chart3.setOption({ + tooltip: { trigger:'axis' }, + legend: { data:['Temperature (°C)','Humidity (%)','Pressure (hPa)'], textStyle:{ color:'#ccc' } }, + xAxis: { type:'category', data:chartData.times, axisLabel:{ color:'#ccc' } }, + yAxis: [ + { type:'value', name:'°C / %', axisLabel:{ color:'#ccc' } }, + { type:'value', name:'hPa', axisLabel:{ color:'#ccc' } } + ], + series: [ + makeLine('Temperature (°C)', 'rgba(255,138,82,1)', chartData.temperature), + makeLine('Humidity (%)', 'rgba(138,255,108,1)', chartData.humidity), + makeLine('Pressure (hPa)', 'rgba(79,155,255,1)', chartData.pressure, 1) + ] + }); + } else { + envContainer.style.display = "none"; + } + + // Resize charts that exist + window.addEventListener("resize", () => { + [chart1, chart2, chart3].forEach(c => { if (c) c.resize(); }); + }); +} + + +async function loadLatestNeighborIds() { + const url = new URL("/api/packets", window.location.origin); + url.searchParams.set("from_node_id", fromNodeId); + url.searchParams.set("portnum", 71); + url.searchParams.set("limit", 1); // ✅ ONLY the latest packet + + const res = await fetch(url); + if (!res.ok) return []; + + const data = await res.json(); + const pkt = data.packets?.[0]; + if (!pkt || !pkt.payload) return []; + + const ids = []; + const re = /neighbors\s*\{([^}]+)\}/g; + let m; + + while ((m = re.exec(pkt.payload)) !== null) { + const id = m[1].match(/node_id:\s*(\d+)/); + if (id) ids.push(parseInt(id[1], 10)); + } + + return ids; +} + +/* ====================================================== + NEIGHBOR CHART (portnum=71) + ====================================================== */ + +async function loadNeighborTimeSeries() { + const container = document.getElementById("neighbor_chart_container"); + const chartEl = document.getElementById("chart_neighbors"); + + const url = `/api/packets?portnum=71&from_node_id=${fromNodeId}&limit=500`; + const res = await fetch(url); + + if (!res.ok) { + container.style.display = "none"; + return; + } + + const data = await res.json(); + const packets = data.packets || []; + + if (!packets.length) { + container.style.display = "none"; + return; + } + + // Sort packets chronologically (microseconds) + packets.sort((a, b) => (a.import_time_us || 0) - (b.import_time_us || 0)); + + const neighborHistory = {}; // node_id -> { name, times[], snr[] } + + for (const pkt of packets) { + if (!pkt.import_time_us || !pkt.payload) continue; + + const ts = pkt.import_time_us; // KEEP NUMERIC TIMESTAMP + + const blockRe = /neighbors\s*\{([^}]+)\}/g; + let m; + + while ((m = blockRe.exec(pkt.payload)) !== null) { + const block = m[1]; + + const idMatch = block.match(/node_id:\s*(\d+)/); + const snrMatch = block.match(/snr:\s*(-?\d+(?:\.\d+)?)/); + + if (!idMatch || !snrMatch) continue; + + const nid = parseInt(idMatch[1], 10); + const snr = parseFloat(snrMatch[1]); + + // Fetch neighbor metadata once + const neighbor = await fetchNodeFromApi(nid); + + if (!neighborHistory[nid]) { + neighborHistory[nid] = { + name: neighbor?.short_name || + neighbor?.long_name || + `Node ${nid}`, + times: [], + snr: [] + }; + } + + neighborHistory[nid].times.push(ts); + neighborHistory[nid].snr.push(snr); + } + } + + // Collect ALL timestamps across neighbors + const allTimes = new Set(); + Object.values(neighborHistory).forEach(entry => { + entry.times.forEach(t => allTimes.add(t)); + }); + + // Sort timestamps numerically + const xTimes = Array.from(allTimes).sort((a, b) => a - b); + + const legend = []; + const series = []; + + for (const entry of Object.values(neighborHistory)) { + legend.push(entry.name); + + series.push({ + name: entry.name, + type: "line", + smooth: true, + connectNulls: true, + showSymbol: false, + data: xTimes.map(t => { + const idx = entry.times.indexOf(t); + return idx >= 0 ? entry.snr[idx] : null; + }) + }); + } + + const chart = echarts.init(chartEl); + + chart.setOption({ + tooltip: { + trigger: "axis", + axisPointer: { type: "line" } + }, + legend: { + data: legend, + textStyle: { color: "#ccc" } + }, + xAxis: { + type: "category", + data: xTimes, + axisLabel: { + color: "#ccc", + formatter: value => + new Date(value / 1000).toLocaleString([], { + year: "2-digit", + month: "2-digit", + day: "2-digit", + hour: "2-digit", + minute: "2-digit" + }) + } + }, + yAxis: { + type: "value", + name: "SNR", + axisLabel: { color: "#ccc" } + }, + series + }); + + window.addEventListener("resize", () => chart.resize()); +} + + + +async function loadPacketHistogram() { + const DAYS = 7; + const now = new Date(); + + const dayKeys = []; + const dayLabels = []; + + for (let i = DAYS - 1; i >= 0; i--) { + const d = new Date(now); + d.setDate(d.getDate() - i); + dayKeys.push(d.toISOString().slice(0, 10)); + dayLabels.push( + d.toLocaleDateString([], { month: "short", day: "numeric" }) + ); + } + + const url = new URL("/api/packets", window.location.origin); + url.searchParams.set("node_id", fromNodeId); + + // last 7 days only (microseconds) + const sinceUs = Date.now() * 1000 - (7 * 24 * 60 * 60 * 1_000_000); + url.searchParams.set("since", sinceUs); + + // modest safety limit (still applies after server-side filter) + url.searchParams.set("limit", 2000); + + + const res = await fetch(url); + if (!res.ok) return; + + const packets = (await res.json()).packets || []; + + const counts = {}; // { port: { day: count } } + const ports = new Set(); + + for (const pkt of packets) { + if (!pkt.import_time_us) continue; + + const day = new Date(pkt.import_time_us / 1000) + .toISOString() + .slice(0, 10); + + if (!dayKeys.includes(day)) continue; + + const port = pkt.portnum ?? 0; + ports.add(port); + + counts[port] ??= {}; + counts[port][day] = (counts[port][day] || 0) + 1; + } + + if (!ports.size) { + document.getElementById("packet_histogram_container").style.display = "none"; + return; + } + + const series = Array.from(ports) + .sort((a, b) => a - b) + .map(port => ({ + name: PORT_LABEL_MAP[port] || `Port ${port}`, + type: "bar", + stack: "total", + barMaxWidth: 42, + itemStyle: { + color: PORT_COLOR_MAP[port] || "#888" + }, + data: dayKeys.map(d => counts[port]?.[d] || 0) + })); + + const chart = echarts.init( + document.getElementById("chart_packet_histogram") + ); + + chart.setOption({ + animation: false, + tooltip: { trigger: "axis" }, + legend: { textStyle: { color: "#ccc" } }, + xAxis: { + type: "category", + data: dayLabels, + axisLabel: { color: "#ccc" } + }, + yAxis: { + type: "value", + axisLabel: { color: "#ccc" } + }, + series + }); + + window.addEventListener("resize", () => chart.resize()); +} + + + +/* ====================================================== + EXPAND / EXPORT BUTTONS + ====================================================== */ + +function expandChart(type){ + const srcEl = document.getElementById(`chart_${type}`); + if (!srcEl) return; + const sourceChart = echarts.getInstanceByDom(srcEl); + if (!sourceChart) return; + + const modal = document.getElementById('chartModal'); + const modalChart = echarts.init(document.getElementById('modalChart')); + modal.style.display = "flex"; + modalChart.setOption(sourceChart.getOption()); + modalChart.resize(); +} +function closeModal(){ + document.getElementById('chartModal').style.display = "none"; +} + +function exportCSV(type){ + const rows = [["Time"]]; + + if (type === "battery_voltage") { + rows[0].push("Battery Level", "Voltage"); + for (let i = 0; i < chartData.times.length; i++) + rows.push([chartData.times[i], chartData.battery[i], chartData.voltage[i]]); + } + else if (type === "air_channel") { + rows[0].push("Air Util Tx", "Channel Utilization"); + for (let i = 0; i < chartData.times.length; i++) + rows.push([chartData.times[i], chartData.airUtil[i], chartData.chanUtil[i]]); + } + else if (type === "environment") { + rows[0].push("Temperature", "Humidity", "Pressure"); + for (let i = 0; i < chartData.times.length; i++) + rows.push([ + chartData.times[i], + chartData.temperature[i], + chartData.humidity[i], + chartData.pressure[i] + ]); + } + else if (type === "neighbors") { + rows[0] = ["Neighbor Node ID", "Neighbor Name", "SNR (dB)"]; + for (let i = 0; i < neighborData.ids.length; i++) { + rows.push([ + neighborData.ids[i], + neighborData.names[i], + neighborData.snrs[i] + ]); + } + } + + const csv = rows.map(r => r.join(",")).join("\n"); + const blob = new Blob([csv], { type:"text/csv" }); + const link = document.createElement("a"); + link.href = URL.createObjectURL(blob); + link.download = `${type}_${fromNodeId}.csv`; + link.click(); +} + +/* ====================================================== + EXPAND PAYLOAD ROWS + ====================================================== */ + +document.addEventListener("click", e => { + const btn = e.target.closest(".toggle-btn"); + if (!btn) return; + const row = btn.closest(".packet-row"); + row.classList.toggle("expanded"); + btn.textContent = row.classList.contains("expanded") ? "▼" : "▶"; +}); + +/* ====================================================== + INIT + ====================================================== */ + +document.addEventListener("DOMContentLoaded", async () => { + await loadTranslationsNode(); + + requestAnimationFrame(async () => { + await loadNodeInfo(); + + // ✅ MAP MUST EXIST FIRST + if (!map) initMap(); + + // ✅ DRAW LATEST NEIGHBORS ONCE + const neighborIds = await loadLatestNeighborIds(); + if (neighborIds.length) { + await drawNeighbors(fromNodeId, neighborIds); + } + + // ⚠️ Track may add to map, but must not hide it + await loadTrack(); + + await loadPackets(); + initPacketPortFilter(); + await loadTelemetryCharts(); + await loadNeighborTimeSeries(); + await loadPacketHistogram(); + + ensureMapVisible(); + setTimeout(ensureMapVisible, 1000); + window.addEventListener("resize", ensureMapVisible); + window.addEventListener("focus", ensureMapVisible); + }); +}); + + + +function packetSizeBytes(pkt) { + if (!pkt) return 0; + + // Prefer raw payload length + if (pkt.payload) { + return new TextEncoder().encode(pkt.payload).length; + } + + // Fallbacks (if you later add protobuf/base64) + if (pkt.raw_payload) { + return atob(pkt.raw_payload).length; + } + + return 0; +} + + +async function loadNodeStats(nodeId) { + try { + const res = await fetch( + `/api/stats/count?from_node=${nodeId}&period_type=day&length=1` + ); + + if (!res.ok) { + throw new Error(`HTTP ${res.status}`); + } + + const data = await res.json(); + + const packets = data?.total_packets ?? 0; + const seen = data?.total_seen ?? 0; + + document.getElementById("info-stats").textContent = + `24h · Packets sent: ${packets.toLocaleString()} · Times seen: ${seen.toLocaleString()} `; + } catch (err) { + console.error("Failed to load node stats:", err); + document.getElementById("info-stats").textContent = "—"; + } +} + + function reloadPackets() { + const sinceSel = document.getElementById("packet_since").value; + const portSel = document.getElementById("packet_port").value; + + const filters = {}; + + if (sinceSel) { + const sinceUs = Date.now() * 1000 - (parseInt(sinceSel, 10) * 1_000_000); + filters.since = sinceUs; + } + + if (portSel) { + filters.portnum = portSel; + } + + loadPackets(filters); +} + + function exportPacketsCSV() { + if (!currentPacketRows.length) { + alert("No packets to export."); + return; + } + + const rows = [ + ["Time", "Packet ID", "From Node", "To Node", "Port", "Port Name", "Payload"] + ]; + + for (const pkt of currentPacketRows) { + const time = pkt.import_time_us + ? new Date(pkt.import_time_us / 1000).toISOString() + : ""; + + const portName = PORT_LABEL_MAP[pkt.portnum] || `Port ${pkt.portnum}`; + + // Escape quotes + line breaks for CSV safety + const payload = (pkt.payload || "") + .replace(/"/g, '""') + .replace(/\r?\n/g, " "); + + rows.push([ + time, + pkt.id, + pkt.from_node_id, + pkt.to_node_id, + pkt.portnum, + portName, + `"${payload}"` + ]); + } + + const csv = rows.map(r => r.join(",")).join("\n"); + const blob = new Blob([csv], { type: "text/csv" }); + + const link = document.createElement("a"); + link.href = URL.createObjectURL(blob); + link.download = `packets_${fromNodeId}_${Date.now()}.csv`; + link.click(); +} + + + + {% endblock %} diff --git a/meshview/templates/node_graphs.html b/meshview/templates/node_graphs.html deleted file mode 100644 index 7d27985c..00000000 --- a/meshview/templates/node_graphs.html +++ /dev/null @@ -1,39 +0,0 @@ -{% macro graph(name) %} - -{% endmacro %} - - - - -
-
- {{ graph("power") }} - {{ graph("chutil") }} -
-
- -
-
- {{ graph("temperature") }} - {{ graph("humidity") }} - {{ graph("wind_speed") }} - {{ graph("wind_direction") }} -
-
- {{ graph("power_metrics") }} -
-
- diff --git a/meshview/templates/nodegraph.html b/meshview/templates/nodegraph.html new file mode 100644 index 00000000..fe515b17 --- /dev/null +++ b/meshview/templates/nodegraph.html @@ -0,0 +1,383 @@ +{% extends "base.html" %} + +{% block head %} + +{% endblock %} + +{% block css %} +#mynetwork { + width: 100%; + height: 100vh; + border: 1px solid #ddd; + background-color: #f8f9fa; + border-radius: 10px; + box-shadow: 0 4px 8px rgba(0,0,0,0.1); +} + +/* Search UI */ +.search-container { + position: absolute; + bottom: 100px; + left: 10px; + z-index: 10; + display: flex; + flex-direction: column; + gap: 5px; +} +.search-container input, +.search-container select, +.search-container button { + padding: 8px; + border-radius: 8px; + border: 1px solid #ccc; +} +.search-container button { + background-color: #007bff; + color: white; + cursor: pointer; +} +.search-container button:hover { + background-color: #0056b3; +} + +/* Node info box */ +#node-info { + position: absolute; + bottom: 10px; + right: 10px; + background-color: rgba(255,255,255,0.95); + padding: 12px; + border-radius: 8px; + border: 1px solid #ccc; + box-shadow: 0 2px 6px rgba(0,0,0,0.1); + font-size: 14px; + color: #333; + width: 260px; + max-height: 250px; + overflow-y: auto; +} + +/* Legend */ +#legend { + position: absolute; + bottom: 10px; + left: 10px; + background: rgba(255,255,255,0.9); + padding: 10px; + border-radius: 5px; + border: 1px solid #ccc; + box-shadow: 0 2px 4px rgba(0,0,0,0.1); + font-size: 14px; + display: flex; + color: #333; +} +.legend-category { + margin-right: 10px; +} +.legend-box { + display: inline-block; + width: 12px; + height: 12px; + margin-right: 5px; + border-radius: 3px; +} +.circle { border-radius: 6px; } +{% endblock %} + +{% block body %} +
+ + +
+ + + + + +
+ + +
+ Long Name:
+ Short Name:
+ Role:
+ Hardware Model: +
+ + +
+
+
Traceroute
+
Neighbor
+
+
+
ROUTER
+
ROUTER_LATE
+
+
+
CLIENT
+
CLIENT_MUTE
+
+
+
CLIENT_BASE
+
Other
+
+
+
Unknown
+
+
+ + + +{% endblock %} diff --git a/meshview/templates/nodelist.html b/meshview/templates/nodelist.html new file mode 100644 index 00000000..70c8e9a6 --- /dev/null +++ b/meshview/templates/nodelist.html @@ -0,0 +1,773 @@ +{% extends "base.html" %} + +{% block css %} + +{% endblock %} + +{% block body %} +
+ + + + + + + + + + + + + + + + +
+ +
+ Showing + 0 + nodes + +
+ + +
+ + + + + + + + + + + + + + + + + + + + +
Short Long Name HW Model Firmware Role Last Latitude Last Longitude Channel Last Seen
+ Loading nodes... +
+
+ + + + + +{% endblock %} diff --git a/meshview/templates/packet.html b/meshview/templates/packet.html index e430d32c..acea412c 100644 --- a/meshview/templates/packet.html +++ b/meshview/templates/packet.html @@ -1,68 +1,550 @@ -
-
- {% set from_me = packet.from_node_id == node_id %} - {% set to_me = packet.to_node_id == node_id %} - - {{packet.from_node.long_name}}( - {%- if not from_me -%} - - {%- endif -%} - {{packet.from_node_id|node_id_to_hex}} - {%- if not from_me -%} - - {%- endif -%} - ) - - -> - - {{packet.to_node.long_name}}( - {%- if not to_me -%} - - {%- endif -%} - {{packet.to_node_id|node_id_to_hex}} - {%- if not to_me -%} - - {%- endif -%} - ) - +{% extends "base.html" %} + +{% block title %}Packet Details{% endblock %} + +{% block css %} +{{ super() }} + +{% endblock %} + +{% block body %} +
+ +
Loading packet information...
+
+ +
+ +
+
+ 📡 Seen By + +
+ +
+ + + + + + + + + + + + +
GatewayRSSISNRHopsChannelTime
+
-
-
- {{packet.id}} - 🔎 - 🔗 +
+ + + +{% endblock %} diff --git a/meshview/templates/packet_details.html b/meshview/templates/packet_details.html deleted file mode 100644 index 3de8ebc0..00000000 --- a/meshview/templates/packet_details.html +++ /dev/null @@ -1,60 +0,0 @@ -
- -{% for seen in packets_seen %} -
-
- {{seen.node.long_name}}( - - {{seen.node_id|node_id_to_hex}} - - ) -
-
-
-
-
import_time
-
{{seen.import_time|format_timestamp}}
-
rx_time
-
{{seen.rx_time|format_timestamp}}
-
hop_limit
-
{{seen.hop_limit}}
-
hop_start
-
{{seen.hop_start}}
-
channel
-
{{seen.channel}}
-
rx_snr
-
{{seen.rx_snr}}
-
rx_rssi
-
{{seen.rx_rssi}}
-
topic
-
{{seen.topic}}
-
-
-
-
-{% endfor %} - -{% if map_center %} - -{% endif %} diff --git a/meshview/templates/packet_index.html b/meshview/templates/packet_index.html deleted file mode 100644 index 0882c737..00000000 --- a/meshview/templates/packet_index.html +++ /dev/null @@ -1,18 +0,0 @@ -{% extends "base.html" %} - -{% block body %} -
-
-
- {% include 'packet.html' %} -
-
-
-
-
-{% endblock %} diff --git a/meshview/templates/packet_list.html b/meshview/templates/packet_list.html deleted file mode 100644 index 42d1757c..00000000 --- a/meshview/templates/packet_list.html +++ /dev/null @@ -1,7 +0,0 @@ -
- {% for packet in packets %} - {% include 'packet.html' %} - {% else %} - No packets found. - {% endfor %} -
diff --git a/meshview/templates/search.html b/meshview/templates/search.html deleted file mode 100644 index 8c9d2e71..00000000 --- a/meshview/templates/search.html +++ /dev/null @@ -1,21 +0,0 @@ -{% extends "base.html" %} - - -{% block body %} - -{% include "search_form.html" %} - - - -{% endblock %} diff --git a/meshview/templates/search_form.html b/meshview/templates/search_form.html deleted file mode 100644 index dc1d0704..00000000 --- a/meshview/templates/search_form.html +++ /dev/null @@ -1,45 +0,0 @@ -
-
- - {% include "datalist.html" %} - {% set options = { - 1: "Text Message", - 3: "Position", - 4: "Node Info", - 67: "Telemetry", - 70: "Traceroute", - 71: "Neighbor Info", - } - %} - - - -
-
diff --git a/meshview/templates/stats.html b/meshview/templates/stats.html new file mode 100644 index 00000000..29287fc0 --- /dev/null +++ b/meshview/templates/stats.html @@ -0,0 +1,578 @@ +{% extends "base.html" %} + +{% block css %} +#packet_details { + height: 95vh; + overflow: auto; +} + +.main-container, .container { + max-width: 900px; + margin: 0 auto; + text-align: center; +} + +.card-section { + background-color: #272b2f; + border: 1px solid #474b4e; + padding: 15px 20px; + margin-bottom: 20px; + border-radius: 10px; + transition: background-color 0.2s ease; +} + +.card-section:hover { + background-color: #2f3338; +} + +.section-header { + font-size: 16px; + margin: 0 0 6px 0; + font-weight: 500; + color: #fff; +} + +.main-header { + font-size: 22px; + margin-bottom: 20px; + font-weight: 600; +} + +.chart { + height: 400px; + margin-top: 10px; +} + +.total-count { + color: #ccc; + font-size: 14px; + margin-bottom: 8px; +} + +.expand-btn, .export-btn { + margin-bottom: 8px; + padding: 4px 8px; + cursor: pointer; + background-color: #444; + color: #fff; + border: none; + border-radius: 4px; + font-size: 12px; +} + +.expand-btn:hover { background-color: #666; } +.export-btn:hover { background-color: #777; } + +/* Summary cards at top */ +.summary-card { + background-color: #1f2124; + border: 1px solid #474b4e; + padding: 10px 15px; + margin-bottom: 15px; + border-radius: 8px; +} +.summary-count { + font-size: 18px; + color: #66bb6a; + font-weight: bold; +} + +#channelSelect { + margin-bottom: 8px; + padding: 4px 6px; + background:#444; + color:#fff; + border:none; + border-radius:4px; +} +{% endblock %} + +{% block head %} + +{% endblock %} + +{% block body %} +
+

+ Mesh Statistics - Summary (all available in Database) +

+ + +
+
+

Total Nodes

+
0
+
+
+

Total Packets

+
0
+
+
+

Total Packets Seen

+
0
+
+
+ + +
+

+ Packets per Day - All Ports (Last 14 Days) +

+
Total: 0
+ + +
+
+ + +
+

+ Packet Types - Last 24 Hours +

+ + + +
+
+ +
+

+ Packets per Day - Text Messages (Port 1, Last 14 Days) +

+
Total: 0
+ + +
+
+ + +
+

+ Packets per Hour - All Ports +

+
Total: 0
+ + +
+
+ +
+

+ Packets per Hour - Text Messages (Port 1) +

+
Total: 0
+ + +
+
+ + +
+

Hardware Breakdown

+ + +
+
+ +
+

Role Breakdown

+ + +
+
+ +
+

Channel Breakdown

+ + +
+
+
+ + + + + +{% endblock %} diff --git a/meshview/templates/top.html b/meshview/templates/top.html new file mode 100644 index 00000000..a033a052 --- /dev/null +++ b/meshview/templates/top.html @@ -0,0 +1,241 @@ +{% extends "base.html" %} + +{% block css %} + +{% endblock %} + +{% block body %} + +

Top Nodes Traffic

+ +
+ +
+
+ + +
+
+ +
+ Showing + 0 + nodes +
+ + + + + + + + + + + + + +
Long NameShort NameChannelSent (24h)Seen (24h)Avg Gateways
+ + + +
+ + + +{% endblock %} diff --git a/meshview/web.py b/meshview/web.py index 6d20e6e2..bb841481 100644 --- a/meshview/web.py +++ b/meshview/web.py @@ -1,116 +1,55 @@ +"""Main web server routes and page rendering for Meshview.""" + import asyncio -import io -from collections import Counter -from dataclasses import dataclass import datetime -from aiohttp_sse import sse_response -import ssl -import re +import logging import os +import pathlib +import re +import ssl +from dataclasses import dataclass import pydot -from pandas import DataFrame -import plotly.express as px -import seaborn as sns -import matplotlib.pyplot as plt -import matplotlib.ticker as ticker from aiohttp import web -from markupsafe import Markup -from jinja2 import Environment, PackageLoader, select_autoescape from google.protobuf import text_format from google.protobuf.message import Message +from jinja2 import Environment, PackageLoader, Undefined, select_autoescape +from markupsafe import Markup -from meshtastic.portnums_pb2 import PortNum -from meshview import store -from meshview import models -from meshview import decode_payload -from meshview import notify - +from meshtastic.protobuf.portnums_pb2 import PortNum +from meshview import config, database, decode_payload, migrations, models, store +from meshview.__version__ import ( + __version_string__, +) +from meshview.web_api import api -with open(os.path.join(os.path.dirname(__file__), '1x1.png'), 'rb') as png: - empty_png = png.read() +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s %(filename)s:%(lineno)d [pid:%(process)d] %(levelname)s - %(message)s", + datefmt="%Y-%m-%d %H:%M:%S", +) +logger = logging.getLogger(__name__) +SEQ_REGEX = re.compile(r"seq \d+") +SOFTWARE_RELEASE = __version_string__ # Keep for backward compatibility +CONFIG = config.CONFIG env = Environment(loader=PackageLoader("meshview"), autoescape=select_autoescape()) +# Start Database +database.init_database(CONFIG["database"]["connection_string"]) -async def build_trace(node_id): - trace = [] - for raw_p in await store.get_packets_from(node_id, PortNum.POSITION_APP, since=datetime.timedelta(hours=24)): - p = Packet.from_model(raw_p) - if not p.raw_payload or not p.raw_payload.latitude_i or not p.raw_payload.longitude_i: - continue - trace.append((p.raw_payload.latitude_i * 1e-7, p.raw_payload.longitude_i * 1e-7)) - if not trace: - for raw_p in await store.get_packets_from(node_id, PortNum.POSITION_APP): - p = Packet.from_model(raw_p) - if not p.raw_payload or not p.raw_payload.latitude_i or not p.raw_payload.longitude_i: - continue - trace.append((p.raw_payload.latitude_i * 1e-7, p.raw_payload.longitude_i * 1e-7)) - break - return trace - +BASE_DIR = os.path.dirname(__file__) +LANG_DIR = os.path.join(BASE_DIR, "lang") -async def build_neighbors(node_id): - packet = (await store.get_packets_from(node_id, PortNum.NEIGHBORINFO_APP, limit=1)).first() - if not packet: - return [] - if packet.import_time < datetime.datetime.utcnow() - datetime.timedelta(days=1): - return [] - _, payload = decode_payload.decode(packet) - neighbors = [] - results = {} - async with asyncio.TaskGroup() as tg: - for n in payload.neighbors: - results[n.node_id] = { - 'node_id': n.node_id, - 'snr': n.snr, - } - neighbors.append((n.node_id, tg.create_task(store.get_node(n.node_id)))) - - for node_id, node in neighbors: - node = await node - if node and node.last_lat and node.last_long: - results[node_id]['short_name'] = node.short_name - results[node_id]['long_name'] = node.long_name - results[node_id]['location'] = (node.last_lat * 1e-7, node.last_long * 1e-7) - else: - del results[node_id] - - return list(results.values()) - - -def node_id_to_hex(node_id): - if node_id == 4294967295: - return "^all" - else: - return f"!{hex(node_id)[2:]}" - - -def format_timestamp(timestamp): - if isinstance(timestamp, int): - timestamp = datetime.datetime.fromtimestamp(timestamp, datetime.timezone.utc) - return timestamp.isoformat(timespec="milliseconds") - - -env.filters["node_id_to_hex"] = node_id_to_hex -env.filters["format_timestamp"] = format_timestamp - - -routes = web.RouteTableDef() - - -@routes.get("/") -async def index(request): - template = env.get_template("index.html") - return web.Response( - text=template.render(is_hx_request="HX-Request" in request.headers, node=None), - content_type="text/html", - ) +with open(os.path.join(os.path.dirname(__file__), '1x1.png'), 'rb') as png: + empty_png = png.read() @dataclass class Packet: + """UI-friendly packet wrapper for templates and API payloads.""" + id: int from_node_id: int from_node: models.Node @@ -122,12 +61,12 @@ class Packet: raw_payload: object payload: str pretty_payload: Markup - import_time: datetime.datetime + import_time_us: int @classmethod def from_model(cls, packet): + """Convert a Packet ORM model into a presentation-friendly Packet.""" mesh_packet, payload = decode_payload.decode(packet) - pretty_payload = None if mesh_packet: @@ -140,19 +79,18 @@ def from_model(cls, packet): text_payload = "Did not decode" elif isinstance(payload, Message): text_payload = text_format.MessageToString(payload) - elif ( - packet.portnum == PortNum.TEXT_MESSAGE_APP - and packet.to_node_id != 0xFFFFFFFF - ): + elif packet.portnum == PortNum.TEXT_MESSAGE_APP and packet.to_node_id != 0xFFFFFFFF: text_payload = "" + elif isinstance(payload, bytes): + text_payload = payload.decode("utf-8", errors="replace") # decode bytes safely else: - text_payload = payload + text_payload = str(payload) if payload: if ( packet.portnum == PortNum.POSITION_APP - and payload.latitude_i - and payload.longitude_i + and getattr(payload, "latitude_i", None) + and getattr(payload, "longitude_i", None) ): pretty_payload = Markup( f'map' @@ -166,604 +104,209 @@ def from_model(cls, packet): to_node_id=packet.to_node_id, portnum=packet.portnum, data=text_mesh_packet, - payload=text_payload, + payload=text_payload, # now always a string pretty_payload=pretty_payload, - import_time=packet.import_time, + import_time_us=packet.import_time_us, # <-- include microseconds raw_mesh_packet=mesh_packet, raw_payload=payload, ) -def generate_responce(request, body, raw_node_id="", node=None): - if "HX-Request" in request.headers: - return web.Response(text=body, content_type="text/html") +async def build_trace(node_id): + """Build a recent GPS trace list for a node using position packets.""" + trace = [] + for raw_p in await store.get_packets_from( + node_id, PortNum.POSITION_APP, since=datetime.timedelta(hours=24) + ): + p = Packet.from_model(raw_p) + if not p.raw_payload or not p.raw_payload.latitude_i or not p.raw_payload.longitude_i: + continue + trace.append((p.raw_payload.latitude_i * 1e-7, p.raw_payload.longitude_i * 1e-7)) - template = env.get_template("index.html") - return web.Response( - text=template.render( - is_hx_request="HX-Request" in request.headers, - raw_node_id=raw_node_id, - node_html=Markup(body), - node=node, - ), - content_type="text/html", - ) + if not trace: + for raw_p in await store.get_packets_from(node_id, PortNum.POSITION_APP): + p = Packet.from_model(raw_p) + if not p.raw_payload or not p.raw_payload.latitude_i or not p.raw_payload.longitude_i: + continue + trace.append((p.raw_payload.latitude_i * 1e-7, p.raw_payload.longitude_i * 1e-7)) + break + return trace -@routes.get("/node_search") -async def node_search(request): - if not "q" in request.query or not request.query["q"]: - return web.Response(text="Bad node id") - portnum = request.query.get("portnum") - if portnum: - portnum = int(portnum) - raw_node_id = request.query["q"] - - node_id = None - if raw_node_id == "^all": - node_id = 0xFFFFFFFF - elif raw_node_id[0] == "!": - try: - node_id = int(raw_node_id[1:], 16) - except ValueError: - pass - else: - try: - node_id = int(raw_node_id) - except ValueError: - pass - if node_id is None: - fuzzy_nodes = list(await store.get_fuzzy_nodes(raw_node_id)) - if len(fuzzy_nodes) == 1: - node_id = fuzzy_nodes[0].node_id +async def build_neighbors(node_id): + """Return neighbor node metadata for the given node ID.""" + packets = await store.get_packets_from(node_id, PortNum.NEIGHBORINFO_APP, limit=1) + packet = packets.first() - if node_id: - return web.Response( - status=307, - headers={'Location': f'/packet_list/{node_id}?{request.query_string}'}, - ) + if not packet: + return [] - template = env.get_template("search.html") - return web.Response( - text=template.render( - nodes=fuzzy_nodes, - query_string=request.query_string, - ), - content_type="text/html", - ) + _, payload = decode_payload.decode(packet) + neighbors = {} + # Gather node information asynchronously + tasks = {n.node_id: store.get_node(n.node_id) for n in payload.neighbors} + results = await asyncio.gather(*tasks.values(), return_exceptions=True) -@routes.get("/node_match") -async def node_match(request): - if not "q" in request.query or not request.query["q"]: - return web.Response(text="Bad node id") - raw_node_id = request.query["q"] - node_options = await store.get_fuzzy_nodes(raw_node_id) + for neighbor, node in zip(payload.neighbors, results, strict=False): + if isinstance(node, Exception): + continue + if node and node.last_lat and node.last_long: + neighbors[neighbor.node_id] = { + 'node_id': neighbor.node_id, + 'snr': neighbor.snr, # Fix dictionary keying issue + 'short_name': node.short_name, + 'long_name': node.long_name, + 'location': (node.last_lat * 1e-7, node.last_long * 1e-7), + } - template = env.get_template("datalist.html") - return web.Response( - text=template.render( - node_options=node_options, - ), - content_type="text/html", - ) + return list(neighbors.values()) # Return a list of dictionaries -@routes.get("/packet_list/{node_id}") -async def packet_list(request): - node_id = int(request.match_info["node_id"]) - if portnum := request.query.get("portnum"): - portnum = int(portnum) +def node_id_to_hex(node_id): + """Format a node_id in Meshtastic hex notation.""" + if node_id is None or isinstance(node_id, Undefined): + return "Invalid node_id" # i... have no clue + if node_id == 4294967295: + return "^all" else: - portnum = None - return await _packet_list(request, store.get_packets(node_id, portnum), 'packet') + return f"!{hex(node_id)[2:].zfill(8)}" -@routes.get("/uplinked_list/{node_id}") -async def uplinked_list(request): - node_id = int(request.match_info["node_id"]) - if portnum := request.query.get("portnum"): - portnum = int(portnum) - else: - portnum = None - return await _packet_list(request, store.get_uplinked_packets(node_id, portnum), 'uplinked') +def format_timestamp(timestamp): + """Normalize timestamps to ISO 8601 strings.""" + if isinstance(timestamp, int): + timestamp = datetime.datetime.fromtimestamp(timestamp, datetime.UTC) + return timestamp.isoformat(timespec="milliseconds") -async def _packet_list(request, raw_packets, packet_event): - node_id = int(request.match_info["node_id"]) - if portnum := request.query.get("portnum"): - portnum = int(portnum) - else: - portnum = None +env.filters["node_id_to_hex"] = node_id_to_hex +env.filters["format_timestamp"] = format_timestamp - async with asyncio.TaskGroup() as tg: - raw_packets = tg.create_task(raw_packets) - node = tg.create_task(store.get_node(node_id)) - trace = tg.create_task(build_trace(node_id)) - neighbors = tg.create_task(build_neighbors(node_id)) - has_telemetry = tg.create_task(store.has_packets(node_id, PortNum.TELEMETRY_APP)) +# Initialize API module with dependencies +api.init_api_module(Packet, SEQ_REGEX, LANG_DIR) - packets = (Packet.from_model(p) for p in await raw_packets) +# Create main routes table +routes = web.RouteTableDef() - template = env.get_template("node.html") + +@routes.get("/") +async def index(request): + """Redirect root URL to configured starting page.""" + """ + Redirect root URL '/' to the page specified in CONFIG['site']['starting']. + Defaults to '/map' if not set. + """ + # Get the starting page from config + starting_url = CONFIG["site"].get("starting", "/map") # default to /map if not set + raise web.HTTPFound(location=starting_url) + + +# Generic static HTML route +@routes.get("/{page}") +async def serve_page(request): + """Serve static HTML pages from meshview/static.""" + page = request.match_info["page"] + + # default to index.html if no extension + if not page.endswith(".html"): + page = f"{page}.html" + + html_file = pathlib.Path(__file__).parent / "static" / page + if not html_file.exists(): + raise web.HTTPNotFound(text=f"Page '{page}' not found") + + content = html_file.read_text(encoding="utf-8") + return web.Response(text=content, content_type="text/html") + + +@routes.get("/net") +async def net(request): return web.Response( - text=template.render( - raw_node_id=node_id_to_hex(node_id), - node_id=node_id, - node=await node, - portnum=portnum, - packets=packets, - packet_event=packet_event, - trace=await trace, - neighbors=await neighbors, - has_telemetry=await has_telemetry, - query_string=request.query_string, - ), + text=env.get_template("net.html").render(), content_type="text/html", ) -@routes.get("/chat_events") -async def chat_events(request): - chat_packet = env.get_template("chat_packet.html") - - with notify.subscribe(node_id=0xFFFFFFFF) as event: - async with sse_response(request) as resp: - while resp.is_connected(): - try: - async with asyncio.timeout(10): - await event.wait() - except TimeoutError: - continue - if event.is_set(): - packets = [ - p - for p in event.packets - if PortNum.TEXT_MESSAGE_APP == p.portnum - ] - event.clear() - try: - for packet in packets: - ui_packet = Packet.from_model(packet) - if not re.match(r"seq \d+$", ui_packet.payload): - await resp.send( - chat_packet.render( - packet=ui_packet, - ), - event="chat_packet", - ) - except ConnectionResetError: - return - - -@routes.get("/events") -async def events(request): - node_id = request.query.get("node_id") - if node_id: - node_id = int(node_id) - portnum = request.query.get("portnum") - if portnum: - portnum = int(portnum) - - packet_template = env.get_template("packet.html") - net_packet_template = env.get_template("net_packet.html") - with notify.subscribe(node_id) as event: - async with sse_response(request) as resp: - while resp.is_connected(): - try: - async with asyncio.timeout(10): - await event.wait() - except TimeoutError: - continue - if event.is_set(): - packets = [ - p - for p in event.packets - if portnum is None or portnum == p.portnum - ] - uplinked = [ - u - for u in event.uplinked - if portnum is None or portnum == u.portnum - ] - event.clear() - try: - for packet in packets: - ui_packet = Packet.from_model(packet) - await resp.send( - packet_template.render( - is_hx_request="HX-Request" in request.headers, - node_id=node_id, - packet=ui_packet, - ), - event="packet", - ) - if ui_packet.portnum == PortNum.TEXT_MESSAGE_APP and '#baymeshnet' in ui_packet.payload.lower(): - await resp.send( - net_packet_template.render(packet=ui_packet), - event="net_packet", - ) - - for packet in uplinked: - await resp.send( - packet_template.render( - is_hx_request="HX-Request" in request.headers, - node_id=node_id, - packet=Packet.from_model(packet), - ), - event="uplinked", - ) - except ConnectionResetError: - return - -@dataclass -class UplinkedNode: - lat: float - long: float - long_name: str - short_name: str - hops: int - snr: float - rssi: float - - -@routes.get("/packet_details/{packet_id}") -async def packet_details(request): - packet_id = int(request.match_info["packet_id"]) - packets_seen = list(await store.get_packets_seen(packet_id)) - packet = await store.get_packet(packet_id) - - from_node_cord = None - if packet.from_node and packet.from_node.last_lat: - from_node_cord = [packet.from_node.last_lat * 1e-7 , packet.from_node.last_long * 1e-7] - - uplinked_nodes = [] - for p in packets_seen: - if p.node and p.node.last_lat: - if p.topic.startswith('mqtt-meshtastic-org'): - hops = 666 - else: - hops = p.hop_start - p.hop_limit - uplinked_nodes.append( - UplinkedNode( - lat=p.node.last_lat * 1e-7, - long=p.node.last_long * 1e-7, - long_name=p.node.long_name, - short_name=p.node.short_name, - hops=hops, - snr=p.rx_snr, - rssi=p.rx_rssi, - ) - ) +@routes.get("/map") +async def map(request): + template = env.get_template("map.html") + return web.Response(text=template.render(), content_type="text/html") - map_center = None - if from_node_cord: - map_center = from_node_cord - elif uplinked_nodes: - map_center = [uplinked_nodes[0].lat, uplinked_nodes[0].long] - template = env.get_template("packet_details.html") +@routes.get("/nodelist") +async def nodelist(request): + template = env.get_template("nodelist.html") return web.Response( - text=template.render( - packets_seen=packets_seen, - map_center=map_center, - from_node_cord=from_node_cord, - uplinked_nodes=uplinked_nodes, - ), + text=template.render(), content_type="text/html", ) @routes.get("/firehose") -async def packet_details(request): - portnum = request.query.get("portnum") - if portnum: - portnum = int(portnum) - packets = await store.get_packets(portnum=portnum) - template = env.get_template("firehose.html") +async def firehose(request): return web.Response( - text=template.render( - packets=(Packet.from_model(p) for p in packets), - portnum=portnum, - ), + text=env.get_template("firehose.html").render(), content_type="text/html", ) @routes.get("/chat") async def chat(request): - packets = await store.get_packets( - node_id=0xFFFFFFFF, portnum=PortNum.TEXT_MESSAGE_APP - ) template = env.get_template("chat.html") - ui_packets = (Packet.from_model(p) for p in packets) return web.Response( - text=template.render( - packets=(p for p in ui_packets if not re.match(r"seq \d+$", p.payload)), - ), + text=template.render(), content_type="text/html", ) @routes.get("/packet/{packet_id}") -async def packet(request): - packet = await store.get_packet(int(request.match_info["packet_id"])) - if not packet: - return web.Response( - status=404, - ) - template = env.get_template("packet_index.html") +async def new_packet(request): + template = env.get_template("packet.html") return web.Response( - text=template.render(packet=Packet.from_model(packet)), + text=template.render(), content_type="text/html", ) -async def graph_telemetry(node_id, payload_type, graph_config): - data = {'date': []} - fields = [] - for c in graph_config: - fields.extend(c['fields']) - - for field in fields: - data[field] = [] - - for p in await store.get_packets_from(node_id, PortNum.TELEMETRY_APP): - _, payload = decode_payload.decode(p) - if not payload: - continue - if not payload.HasField(payload_type): - continue - data_field = getattr(payload, payload_type) - timestamp = p.import_time - data['date'].append(timestamp) - for field in fields: - data[field].append(getattr(data_field, field)) - - if not data['date']: - return web.Response( - body=empty_png, - status=404, - content_type="image/png", - ) - - max_time = datetime.timedelta(days=4) - newest = data['date'][0] - for i, d in enumerate(data['date']): - if d < newest - max_time: - break - - fig, ax = plt.subplots(figsize=(10, 10)) - fig.autofmt_xdate() - ax.set_xlabel('time') - axes = {0: ax} - - date = data.pop('date') - df = DataFrame(data, index=date) - - for i, ax_config in enumerate(graph_config): - args = {} - if 'color' in ax_config: - args['color'] = 'tab:' + ax_config['color'] - if i: - ax = ax.twinx() - ax.set_ylabel(ax_config['label'], **args) - ax_df = df[ax_config['fields']] - args = {} - if 'palette' in ax_config: - args['palette'] = ax_config['palette'] - sns.lineplot(data=ax_df, ax=ax, **args) - - png = io.BytesIO() - plt.savefig(png, dpi=100) - plt.close() - +@routes.get("/node/{from_node_id}") +async def firehose_node(request): + template = env.get_template("node.html") return web.Response( - body=png.getvalue(), - content_type="image/png", - ) - - -@routes.get("/graph/power/{node_id}") -async def graph_power(request): - return await graph_telemetry( - int(request.match_info['node_id']), - 'device_metrics', - [ - { - 'label': 'battery level', - 'fields': ['battery_level'], - }, - { - 'label': 'voltage', - 'fields': ['voltage'], - 'palette': 'Set2', - }, - ], - ) - - -@routes.get("/graph/chutil/{node_id}") -async def graph_chutil(request): - return await graph_telemetry( - int(request.match_info['node_id']), - 'device_metrics', - [ - { - 'label': 'utilization', - 'fields': ['channel_utilization', 'air_util_tx'], - }, - ], - ) - - - - -@routes.get("/graph/wind_speed/{node_id}") -async def graph_wind_speed(request): - return await graph_telemetry( - int(request.match_info['node_id']), - 'environment_metrics', - [ - { - 'label': 'wind speed m/s', - 'fields': ['wind_speed'], - }, - ], - ) - - -@routes.get("/graph/wind_direction/{node_id}") -async def graph_wind_direction(request): - return await graph_telemetry( - int(request.match_info['node_id']), - 'environment_metrics', - [ - { - 'label': 'wind direction', - 'fields': ['wind_direction'], - }, - ], - ) - -@routes.get("/graph/temperature/{node_id}") -async def graph_temperature(request): - return await graph_telemetry( - int(request.match_info['node_id']), - 'environment_metrics', - [ - { - 'label': 'temperature C', - 'fields': ['temperature'], - }, - ], - ) - - -@routes.get("/graph/humidity/{node_id}") -async def graph_humidity(request): - return await graph_telemetry( - int(request.match_info['node_id']), - 'environment_metrics', - [ - { - 'label': 'humidity', - 'fields': ['relative_humidity'], - }, - ], + text=template.render(), + content_type="text/html", ) -@routes.get("/graph/power_metrics/{node_id}") -async def graph_power_metrics(request): - return await graph_telemetry( - int(request.match_info['node_id']), - 'power_metrics', - [ - { - 'label': 'voltage', - 'fields': ['ch1_voltage', 'ch2_voltage', 'ch3_voltage'], - }, - { - 'label': 'current', - 'fields': ['ch1_current', 'ch2_current', 'ch3_current'], - 'palette': 'Set2', - }, - ], +@routes.get("/nodegraph") +async def nodegraph(request): + template = env.get_template("nodegraph.html") + return web.Response( + text=template.render(), + content_type="text/html", ) -@routes.get("/graph/neighbors/{node_id}") -async def graph_neighbors(request): - oldest = datetime.datetime.utcnow() - datetime.timedelta(days=4) - - data = {} - dates =[] - for p in await store.get_packets_from(int(request.match_info['node_id']), PortNum.NEIGHBORINFO_APP): - _, payload = decode_payload.decode(p) - if not payload: - continue - if p.import_time < oldest: - break - - dates.append(p.import_time) - for v in data.values(): - v.append(None) - - for n in payload.neighbors: - data.setdefault(n.node_id, [None] * len(dates))[-1] = n.snr - - nodes = {} - async with asyncio.TaskGroup() as tg: - for node_id in data: - nodes[node_id] = tg.create_task(store.get_node(node_id)) - - data_by_short_name = {} - for node_id, data in data.items(): - node = await nodes[node_id] - if node: - data_by_short_name[node.short_name] = data - else: - data_by_short_name[node_id_to_hex(node_id)] = data - - fig, ax1 = plt.subplots(figsize=(5, 5)) - ax1.set_xlabel('time') - ax1.set_ylabel('SNR') - df = DataFrame(data_by_short_name, index=dates) - sns.lineplot(data=df) - - png = io.BytesIO() - plt.savefig(png, dpi=100) - plt.close() - +@routes.get("/top") +async def top(request): + template = env.get_template("top.html") return web.Response( - body=png.getvalue(), - content_type="image/png", + text=template.render(), + content_type="text/html", ) -@routes.get("/graph/neighbors2/{node_id}") -async def graph_neighbors2(request): - oldest = datetime.datetime.utcnow() - datetime.timedelta(days=30) - data = [] - node_ids = set() - for p in await store.get_packets_from(int(request.match_info['node_id']), PortNum.NEIGHBORINFO_APP): - _, payload = decode_payload.decode(p) - if not payload: - continue - if p.import_time < oldest: - break - - for n in payload.neighbors: - node_ids.add(n.node_id) - data.append({ - 'time': p.import_time, - 'snr': n.snr, - 'node_id': n.node_id, - }) - - nodes = {} - async with asyncio.TaskGroup() as tg: - for node_id in node_ids: - nodes[node_id] = tg.create_task(store.get_node(node_id)) - - for d in data: - node = await nodes[d['node_id']] - if node: - d['node_name'] = node.short_name - else: - d['node_name'] = node_id_to_hex(node_id) - - df = DataFrame(data) - fig = px.line(df, x="time", y="snr", color="node_name", markers=True) - html = fig.to_html(full_html=True, include_plotlyjs='cdn') +@routes.get("/stats") +async def stats(request): + template = env.get_template("stats.html") return web.Response( - text=html, + text=template.render(), content_type="text/html", ) +# Keep !! @routes.get("/graph/traceroute/{packet_id}") async def graph_traceroute(request): packet_id = int(request.match_info['packet_id']) @@ -812,8 +355,8 @@ async def graph_traceroute(request): # It seems some nodes add them self to the list before uplinking path.append(tr.gateway_node_id) - if not tr.done and tr.gateway_node_id not in node_seen_time and tr.import_time: - node_seen_time[path[-1]] = tr.import_time + if not tr.done and tr.gateway_node_id not in node_seen_time and tr.import_time_us: + node_seen_time[path[-1]] = tr.import_time_us mqtt_nodes.add(tr.gateway_node_id) node_color[path[-1]] = '#' + hex(hash(tuple(path)))[3:9] @@ -823,7 +366,7 @@ async def graph_traceroute(request): for path in paths: used_nodes.update(path) - import_times = [tr.import_time for tr in traceroutes if tr.import_time] + import_times = [tr.import_time_us for tr in traceroutes if tr.import_time_us] if import_times: first_time = min(import_times) else: @@ -834,9 +377,11 @@ async def graph_traceroute(request): if not node: node_name = node_id_to_hex(node_id) else: - node_name = f'[{node.short_name}] {node.long_name}\n{node_id_to_hex(node_id)}\n{node.role}' + node_name = ( + f'[{node.short_name}] {node.long_name}\n{node_id_to_hex(node_id)}\n{node.role}' + ) if node_id in node_seen_time: - ms = (node_seen_time[node_id] - first_time).total_seconds() * 1000 + ms = (node_seen_time[node_id] - first_time) / 1000 node_name += f'\n {ms:.2f}ms' style = 'dashed' if node_id == dest: @@ -847,18 +392,20 @@ async def graph_traceroute(request): if node_id in saw_reply: style += ', diagonals' - graph.add_node(pydot.Node( - str(node_id), - label=node_name, - shape='box', - color=node_color.get(node_id, 'black'), - style=style, - href=f"/packet_list/{node_id}", - )) + graph.add_node( + pydot.Node( + str(node_id), + label=node_name, + shape='box', + color=node_color.get(node_id, 'black'), + style=style, + href=f"/node/{node_id}", + ) + ) for path in paths: color = '#' + hex(hash(tuple(path)))[3:9] - for src, dest in zip(path, path[1:]): + for src, dest in zip(path, path[1:], strict=False): graph.add_edge(pydot.Edge(src, dest, color=color)) return web.Response( @@ -866,208 +413,49 @@ async def graph_traceroute(request): content_type="image/svg+xml", ) -@routes.get("/graph/network") -async def graph_network(request): - root = request.query.get("root") - depth = int(request.query.get("depth", 5)) - hours = int(request.query.get("hours", 24)) - minutes = int(request.query.get("minutes", 0)) - since = datetime.timedelta(hours=hours, minutes=minutes) - - nodes = {} - node_ids = set() - - traceroutes = [] - for tr in await store.get_traceroutes(since): - node_ids.add(tr.gateway_node_id) - node_ids.add(tr.packet.from_node_id) - node_ids.add(tr.packet.to_node_id) - route = decode_payload.decode_payload(PortNum.TRACEROUTE_APP, tr.route) - node_ids.update(route.route) - - path = [tr.packet.from_node_id] - path.extend(route.route) - if tr.done: - path.append(tr.packet.to_node_id) - else: - if path[-1] != tr.gateway_node_id: - # It seems some nodes add them self to the list before uplinking - path.append(tr.gateway_node_id) - traceroutes.append((tr, path)) - - edges = Counter() - edge_type = {} - used_nodes = set() - - for ps, p in await store.get_mqtt_neighbors(since): - node_ids.add(ps.node_id) - node_ids.add(p.from_node_id) - used_nodes.add(ps.node_id) - used_nodes.add(p.from_node_id) - edges[(p.from_node_id, ps.node_id)] += 1 - edge_type[(p.from_node_id, ps.node_id)] = 'sni' - - for packet in await store.get_packets( - portnum=PortNum.NEIGHBORINFO_APP, - since=since, - ): - _, neighbor_info = decode_payload.decode(packet) - node_ids.add(packet.from_node_id) - used_nodes.add(packet.from_node_id) - for node in neighbor_info.neighbors: - node_ids.add(node.node_id) - used_nodes.add(node.node_id) - edges[(node.node_id, packet.from_node_id)] += 1 - edge_type[(node.node_id, packet.from_node_id)] = 'ni' - - async with asyncio.TaskGroup() as tg: - for node_id in node_ids: - nodes[node_id] = tg.create_task(store.get_node(node_id)) - - tr_done = set() - for tr, path in traceroutes: - if tr.done: - if tr.packet_id in tr_done: - continue - else: - tr_done.add(tr.packet_id) - - for src, dest in zip(path, path[1:]): - used_nodes.add(src) - used_nodes.add(dest) - edges[(src, dest)] += 1 - edge_type[(src, dest)] = 'tr' - - async def get_node_name(node_id): - node = await nodes[node_id] - if not node: - node_name = node_id_to_hex(node_id) - else: - node_name = f'[{node.short_name}] {node.long_name}\n{node_id_to_hex(node_id)}' - return node_name - - if root: - new_used_nodes = set() - new_edges = Counter() - edge_map = {} - for src, dest in edges: - edge_map.setdefault(dest, []).append(src) - - queue = [int(root)] - for i in range(depth): - next_queue = [] - for node in queue: - new_used_nodes.add(node) - for dest in edge_map.get(node, []): - new_used_nodes.add(dest) - new_edges[(dest, node)] += 1 - next_queue.append(dest) - queue = next_queue - - used_nodes = new_used_nodes - edges = new_edges - - #graph = pydot.Dot('network', graph_type="digraph", layout="sfdp", overlap="prism", quadtree="2", repulsiveforce="1.5", k="1", overlap_scaling="1.5", concentrate=True) - #graph = pydot.Dot('network', graph_type="digraph", layout="sfdp", overlap="prism1000", overlap_scaling="-4", sep="1000", pack="true") - graph = pydot.Dot('network', graph_type="digraph", layout="neato", overlap="false", model='subset', esep="+5") - for node_id in used_nodes: - node = await nodes[node_id] - color = '#000000' - node_name = await get_node_name(node_id) - if node and node.role in ('ROUTER', 'ROUTER_CLIENT', 'REPEATER'): - color = '#0000FF' - elif node and node.role == 'CLIENT_MUTE': - color = '#00FF00' - graph.add_node(pydot.Node( - str(node_id), - label=node_name, - shape='box', - color=color, - href=f"/graph/network?root={node_id}&depth={depth-1}", - )) - - if edges: - max_edge_count = edges.most_common(1)[0][1] - else: - max_edge_count = 1 - - size_ratio = 2. / max_edge_count - - edge_added = set() +async def run_server(): + """Start the aiohttp web server after migrations are complete.""" + # Wait for database migrations to complete before starting web server + logger.info("Checking database schema status...") + database_url = CONFIG["database"]["connection_string"] - for (src, dest), edge_count in edges.items(): - size = max(size_ratio * edge_count, .25) - arrowsize = max(size_ratio * edge_count, .5) - if edge_type[(src, dest)] in ('ni'): - color = '#FF0000' - elif edge_type[(src, dest)] in ('sni'): - color = '#00FF00' - else: - color = '#000000' - edge_dir = "forward" - if (dest, src) in edges and edge_type[(src, dest)] == edge_type[(dest, src)]: - edge_dir = "both" - edge_added.add((dest, src)) - - if (src, dest) not in edge_added: - edge_added.add((src, dest)) - graph.add_edge(pydot.Edge( - str(src), - str(dest), - color=color, - tooltip=f'{await get_node_name(src)} -> {await get_node_name(dest)}', - penwidth=1.85, - dir=edge_dir, - )) - - return web.Response( - body=graph.create_svg(), - content_type="image/svg+xml", + # Wait for migrations to complete (writer app responsibility) + migration_ready = await migrations.wait_for_migrations( + database.engine, database_url, max_retries=30, retry_delay=2 ) + if not migration_ready: + logger.error("Database schema is not up to date. Cannot start web server.") + raise RuntimeError("Database schema version mismatch - migrations not complete") -@routes.get("/net") -async def net(request): - if "date" in request.query: - start_date = datetime.date.fromisoformat(request.query["date"]) - else: - start_date = datetime.date.today() - while start_date.weekday() != 2: - start_date = start_date - datetime.timedelta(days=1) - - start_time = datetime.datetime.combine(start_date, datetime.time(0,0)) - - text_packets = [ - Packet.from_model(p) - for p in await store.get_packets( - portnum=PortNum.TEXT_MESSAGE_APP, - after=start_time, - before=start_time + datetime.timedelta(hours=1), - ) - ] - net_packets = [p for p in text_packets if '#baymeshnet' in p.payload.lower()] + logger.info("Database schema verified - starting web server") - template = env.get_template("net.html") - return web.Response( - text=template.render(net_packets=net_packets), - content_type="text/html", - ) + app = web.Application() + app.router.add_static("/static/", pathlib.Path(__file__).parent / "static") + app.add_routes(api.routes) # Add API routes + app.add_routes(routes) # Add main web routes + # Check if access logging should be disabled + enable_access_log = CONFIG.get("logging", {}).get("access_log", "False").lower() == "true" + access_log_handler = None if not enable_access_log else logging.getLogger("aiohttp.access") -async def run_server(bind, port, tls_cert): - app = web.Application() - app.add_routes(routes) - runner = web.AppRunner(app) + runner = web.AppRunner(app, access_log=access_log_handler) await runner.setup() - if tls_cert: + if CONFIG["server"]["tls_cert"]: ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) - ssl_context.load_cert_chain(tls_cert) + ssl_context.load_cert_chain(CONFIG["server"]["tls_cert"]) + logger.info(f"TLS enabled with certificate: {CONFIG['server']['tls_cert']}") else: ssl_context = None - for host in bind: + logger.info("TLS disabled") + if host := CONFIG["server"]["bind"]: + port = CONFIG["server"]["port"] + protocol = "https" if ssl_context else "http" site = web.TCPSite(runner, host, port, ssl_context=ssl_context) await site.start() - + # Display localhost instead of wildcard addresses for usability + display_host = "localhost" if host in ("0.0.0.0", "*", "::") else host + logger.info(f"Web server started at {protocol}://{display_host}:{port}") while True: await asyncio.sleep(3600) # sleep forever diff --git a/meshview/web_api/__init__.py b/meshview/web_api/__init__.py new file mode 100644 index 00000000..da348400 --- /dev/null +++ b/meshview/web_api/__init__.py @@ -0,0 +1 @@ +"""Web submodule for MeshView API endpoints.""" diff --git a/meshview/web_api/api.py b/meshview/web_api/api.py new file mode 100644 index 00000000..517cc735 --- /dev/null +++ b/meshview/web_api/api.py @@ -0,0 +1,882 @@ +"""API endpoints for MeshView.""" + +import datetime +import json +import logging +import os + +from aiohttp import web +from sqlalchemy import func, select, text + +from meshtastic.protobuf.portnums_pb2 import PortNum +from meshview import database, decode_payload, store +from meshview.__version__ import __version__, _git_revision_short, get_version_info +from meshview.config import CONFIG +from meshview.models import Node +from meshview.models import Packet as PacketModel +from meshview.models import PacketSeen as PacketSeenModel + +logger = logging.getLogger(__name__) + +# Will be set by web.py during initialization +Packet = None +SEQ_REGEX = None +LANG_DIR = None + +# Create dedicated route table for API endpoints +routes = web.RouteTableDef() + + +def init_api_module(packet_class, seq_regex, lang_dir): + """Initialize API module with dependencies from main web module.""" + global Packet, SEQ_REGEX, LANG_DIR + Packet = packet_class + SEQ_REGEX = seq_regex + LANG_DIR = lang_dir + + +@routes.get("/api/channels") +async def api_channels(request: web.Request): + period_type = request.query.get("period_type", "hour") + length = int(request.query.get("length", 24)) + + try: + channels = await store.get_channels_in_period(period_type, length) + return web.json_response({"channels": channels}) + except Exception as e: + return web.json_response({"channels": [], "error": str(e)}) + + +@routes.get("/api/nodes") +async def api_nodes(request): + try: + # Optional query parameters + node_id = request.query.get("node_id") + role = request.query.get("role") + channel = request.query.get("channel") + hw_model = request.query.get("hw_model") + days_active = request.query.get("days_active") + + if days_active: + try: + days_active = int(days_active) + except ValueError: + days_active = None + + # Fetch nodes from database + nodes = await store.get_nodes( + node_id=node_id, role=role, channel=channel, hw_model=hw_model, days_active=days_active + ) + + # Prepare the JSON response + nodes_data = [] + for n in nodes: + nodes_data.append( + { + "id": getattr(n, "id", None), + "node_id": n.node_id, + "long_name": n.long_name, + "short_name": n.short_name, + "hw_model": n.hw_model, + "firmware": n.firmware, + "role": n.role, + "last_lat": getattr(n, "last_lat", None), + "last_long": getattr(n, "last_long", None), + "channel": n.channel, + # "last_update": n.last_update.isoformat(), + "last_seen_us": n.last_seen_us, + } + ) + + return web.json_response({"nodes": nodes_data}) + + except Exception as e: + logger.error(f"Error in /api/nodes: {e}") + return web.json_response({"error": "Failed to fetch nodes"}, status=500) + + +@routes.get("/api/packets") +async def api_packets(request): + try: + # --- Parse query parameters --- + packet_id_str = request.query.get("packet_id") + limit_str = request.query.get("limit", "50") + since_str = request.query.get("since") + portnum_str = request.query.get("portnum") + contains = request.query.get("contains") + + # NEW — explicit filters + from_node_id_str = request.query.get("from_node_id") + to_node_id_str = request.query.get("to_node_id") + node_id_str = request.query.get("node_id") # legacy: match either from/to + + # --- If a packet_id is provided, return only that packet --- + if packet_id_str: + try: + packet_id = int(packet_id_str) + except ValueError: + return web.json_response({"error": "Invalid packet_id format"}, status=400) + + packet = await store.get_packet(packet_id) + if not packet: + return web.json_response({"packets": []}) + + p = Packet.from_model(packet) + data = { + "id": p.id, + "from_node_id": p.from_node_id, + "to_node_id": p.to_node_id, + "portnum": int(p.portnum) if p.portnum is not None else None, + "payload": (p.payload or "").strip(), + "import_time_us": p.import_time_us, + "channel": getattr(p.from_node, "channel", ""), + "long_name": getattr(p.from_node, "long_name", ""), + } + return web.json_response({"packets": [data]}) + + # --- Parse limit --- + try: + limit = min(max(int(limit_str), 1), 1000) + except ValueError: + limit = 50 + + # --- Parse since timestamp --- + since = None + if since_str: + try: + since = int(since_str) + except ValueError: + logger.warning(f"Invalid 'since' value (expected microseconds): {since_str}") + + # --- Parse portnum --- + portnum = None + if portnum_str: + try: + portnum = int(portnum_str) + except ValueError: + logger.warning(f"Invalid portnum: {portnum_str}") + + # --- Parse node filters --- + from_node_id = None + to_node_id = None + node_id = None # legacy: match either from/to + + if from_node_id_str: + try: + from_node_id = int(from_node_id_str, 0) + except ValueError: + logger.warning(f"Invalid from_node_id: {from_node_id_str}") + + if to_node_id_str: + try: + to_node_id = int(to_node_id_str, 0) + except ValueError: + logger.warning(f"Invalid to_node_id: {to_node_id_str}") + + if node_id_str: + try: + node_id = int(node_id_str, 0) + except ValueError: + logger.warning(f"Invalid node_id: {node_id_str}") + + # --- Fetch packets using explicit filters --- + packets = await store.get_packets( + from_node_id=from_node_id, + to_node_id=to_node_id, + node_id=node_id, + portnum=portnum, + after=since, + contains=contains, + limit=limit, + ) + + ui_packets = [Packet.from_model(p) for p in packets] + + # --- Text message filtering --- + if portnum == PortNum.TEXT_MESSAGE_APP: + ui_packets = [p for p in ui_packets if p.payload and not SEQ_REGEX.fullmatch(p.payload)] + if contains: + ui_packets = [p for p in ui_packets if contains.lower() in p.payload.lower()] + + # --- Sort descending by import_time_us --- + ui_packets.sort( + key=lambda p: (p.import_time_us is not None, p.import_time_us or 0), reverse=True + ) + ui_packets = ui_packets[:limit] + + # --- Build JSON output --- + packets_data = [] + for p in ui_packets: + packet_dict = { + "id": p.id, + "import_time_us": p.import_time_us, + "channel": getattr(p.from_node, "channel", ""), + "from_node_id": p.from_node_id, + "to_node_id": p.to_node_id, + "portnum": int(p.portnum), + "long_name": getattr(p.from_node, "long_name", ""), + "payload": (p.payload or "").strip(), + "to_long_name": getattr(p.to_node, "long_name", ""), + } + + reply_id = getattr( + getattr(getattr(p, "raw_mesh_packet", None), "decoded", None), + "reply_id", + None, + ) + if reply_id: + packet_dict["reply_id"] = reply_id + + packets_data.append(packet_dict) + + # --- Latest import_time_us for incremental fetch --- + latest_import_time = None + if packets_data: + for p in packets_data: + if p.get("import_time_us") and p["import_time_us"] > 0: + latest_import_time = max(latest_import_time or 0, p["import_time_us"]) + + response = {"packets": packets_data} + if latest_import_time is not None: + response["latest_import_time"] = latest_import_time + + return web.json_response(response) + + except Exception as e: + logger.error(f"Error in /api/packets: {e}") + return web.json_response({"error": "Failed to fetch packets"}, status=500) + + +@routes.get("/api/stats") +async def api_stats(request): + """ + Enhanced stats endpoint: + - Supports global stats (existing behavior) + - Supports per-node stats using ?node= + returning both sent AND seen counts in the specified period + """ + allowed_periods = {"hour", "day"} + + period_type = request.query.get("period_type", "hour").lower() + if period_type not in allowed_periods: + return web.json_response( + {"error": f"Invalid period_type. Must be one of {allowed_periods}"}, + status=400, + ) + + try: + length = int(request.query.get("length", 24)) + except ValueError: + return web.json_response({"error": "length must be an integer"}, status=400) + + # NEW: optional combined node stats + node_str = request.query.get("node") + if node_str: + try: + node_id = int(node_str) + except ValueError: + return web.json_response({"error": "node must be an integer"}, status=400) + + # Fetch sent packets + sent = await store.get_packet_stats( + period_type=period_type, + length=length, + from_node=node_id, + ) + + # Fetch seen packets + seen = await store.get_packet_stats( + period_type=period_type, + length=length, + to_node=node_id, + ) + + return web.json_response( + { + "node_id": node_id, + "period_type": period_type, + "length": length, + "sent": sent.get("total", 0), + "seen": seen.get("total", 0), + } + ) + + # ---- Existing full stats mode (unchanged) ---- + channel = request.query.get("channel") + + def parse_int_param(name): + value = request.query.get(name) + if value is not None: + try: + return int(value) + except ValueError: + raise web.HTTPBadRequest( + text=json.dumps({"error": f"{name} must be an integer"}), + content_type="application/json", + ) from None + return None + + portnum = parse_int_param("portnum") + to_node = parse_int_param("to_node") + from_node = parse_int_param("from_node") + + stats = await store.get_packet_stats( + period_type=period_type, + length=length, + channel=channel, + portnum=portnum, + to_node=to_node, + from_node=from_node, + ) + + return web.json_response(stats) + + +@routes.get("/api/stats/count") +async def api_stats_count(request): + """ + Returns packet and packet_seen totals. + Behavior: + • If no filters → total packets ever + total seen ever + • If filters → apply window/channel/from/to + packet_id + """ + + # -------- Parse request parameters -------- + packet_id_str = request.query.get("packet_id") + packet_id = None + if packet_id_str: + try: + packet_id = int(packet_id_str) + except ValueError: + return web.json_response({"error": "packet_id must be integer"}, status=400) + + period_type = request.query.get("period_type") + length_str = request.query.get("length") + length = None + if length_str: + try: + length = int(length_str) + except ValueError: + return web.json_response({"error": "length must be integer"}, status=400) + + channel = request.query.get("channel") + + def parse_int(name): + value = request.query.get(name) + if value is None: + return None + try: + return int(value) + except ValueError: + raise web.HTTPBadRequest( + text=json.dumps({"error": f"{name} must be integer"}), + content_type="application/json", + ) from None + + from_node = parse_int("from_node") + to_node = parse_int("to_node") + + # -------- Case 1: NO FILTERS → return global totals -------- + no_filters = ( + period_type is None + and length is None + and channel is None + and from_node is None + and to_node is None + and packet_id is None + ) + + if no_filters: + total_packets = await store.get_total_packet_count() + total_seen = await store.get_total_packet_seen_count() + return web.json_response({"total_packets": total_packets, "total_seen": total_seen}) + + # -------- Case 2: Apply filters → compute totals -------- + total_packets = await store.get_total_packet_count( + period_type=period_type, + length=length, + channel=channel, + from_node=from_node, + to_node=to_node, + ) + + total_seen = await store.get_total_packet_seen_count( + packet_id=packet_id, + period_type=period_type, + length=length, + channel=channel, + from_node=from_node, + to_node=to_node, + ) + + return web.json_response({"total_packets": total_packets, "total_seen": total_seen}) + + +@routes.get("/api/edges") +async def api_edges(request): + since = datetime.datetime.now() - datetime.timedelta(hours=48) + filter_type = request.query.get("type") + + # NEW → optional single-node filter + node_filter_str = request.query.get("node_id") + node_filter = None + if node_filter_str: + try: + node_filter = int(node_filter_str) + except ValueError: + return web.json_response({"error": "node_id must be integer"}, status=400) + + edges = {} + traceroute_count = 0 + edges_added_tr = 0 + edges_added_neighbor = 0 + + # --- Traceroute edges --- + if filter_type in (None, "traceroute"): + async for tr in store.get_traceroutes(since): + traceroute_count += 1 + + try: + route = decode_payload.decode_payload(PortNum.TRACEROUTE_APP, tr.route) + except Exception: + continue + + path = [tr.packet.from_node_id] + list(route.route) + path.append(tr.packet.to_node_id if tr.done else tr.gateway_node_id) + + for a, b in zip(path, path[1:], strict=False): + if (a, b) not in edges: + edges[(a, b)] = "traceroute" + edges_added_tr += 1 + + # --- Neighbor edges --- + if filter_type in (None, "neighbor"): + packets = await store.get_packets(portnum=71) + for packet in packets: + try: + _, neighbor_info = decode_payload.decode(packet) + except Exception: + continue + + for node in neighbor_info.neighbors: + edge = (node.node_id, packet.from_node_id) + if edge not in edges: + edges[edge] = "neighbor" + edges_added_neighbor += 1 + + # Convert to list + edges_list = [ + {"from": frm, "to": to, "type": edge_type} for (frm, to), edge_type in edges.items() + ] + + # NEW → apply node_id filtering + if node_filter is not None: + edges_list = [e for e in edges_list if e["from"] == node_filter or e["to"] == node_filter] + + return web.json_response({"edges": edges_list}) + + +@routes.get("/api/config") +async def api_config(request): + try: + # ------------------ Helpers ------------------ + def get(section, key, default=None): + """Safe getter for both dict and ConfigParser.""" + if isinstance(section, dict): + return section.get(key, default) + return section.get(key, fallback=default) + + def get_bool(section, key, default=False): + val = get(section, key, default) + if isinstance(val, bool): + return "true" if val else "false" + if isinstance(val, str): + return "true" if val.lower() in ("1", "true", "yes", "on") else "false" + return "true" if bool(val) else "false" + + def get_float(section, key, default=0.0): + try: + return float(get(section, key, default)) + except Exception: + return float(default) + + def get_int(section, key, default=0): + try: + return int(get(section, key, default)) + except Exception: + return default + + def get_str(section, key, default=""): + val = get(section, key, default) + return str(val) if val is not None else str(default) + + # ------------------ SITE ------------------ + site = CONFIG.get("site", {}) + safe_site = { + "domain": get_str(site, "domain", ""), + "language": get_str(site, "language", "en"), + "title": get_str(site, "title", ""), + "message": get_str(site, "message", ""), + "starting": get_str(site, "starting", "/chat"), + "nodes": get_bool(site, "nodes", True), + "chat": get_bool(site, "chat", True), + "everything": get_bool(site, "everything", True), + "graphs": get_bool(site, "graphs", True), + "stats": get_bool(site, "stats", True), + "net": get_bool(site, "net", True), + "map": get_bool(site, "map", True), + "top": get_bool(site, "top", True), + "map_top_left_lat": get_float(site, "map_top_left_lat", 39.0), + "map_top_left_lon": get_float(site, "map_top_left_lon", -123.0), + "map_bottom_right_lat": get_float(site, "map_bottom_right_lat", 36.0), + "map_bottom_right_lon": get_float(site, "map_bottom_right_lon", -121.0), + "map_interval": get_int(site, "map_interval", 3), + "firehose_interval": get_int(site, "firehose_interval", 3), + "weekly_net_message": get_str( + site, "weekly_net_message", "Weekly Mesh check-in message." + ), + "net_tag": get_str(site, "net_tag", "#BayMeshNet"), + "version": str(__version__), + } + + # ------------------ MQTT ------------------ + mqtt = CONFIG.get("mqtt", {}) + topics_raw = get(mqtt, "topics", []) + + if isinstance(topics_raw, str): + try: + topics = json.loads(topics_raw) + except Exception: + topics = [topics_raw] + elif isinstance(topics_raw, list): + topics = topics_raw + else: + topics = [] + + safe_mqtt = { + "server": get_str(mqtt, "server", ""), + "topics": topics, + } + + # ------------------ CLEANUP ------------------ + cleanup = CONFIG.get("cleanup", {}) + safe_cleanup = { + "enabled": get_bool(cleanup, "enabled", False), + "days_to_keep": get_str(cleanup, "days_to_keep", "14"), + "hour": get_str(cleanup, "hour", "2"), + "minute": get_str(cleanup, "minute", "0"), + "vacuum": get_bool(cleanup, "vacuum", False), + } + + safe_config = { + "site": safe_site, + "mqtt": safe_mqtt, + "cleanup": safe_cleanup, + } + + return web.json_response(safe_config) + except Exception as e: + return web.json_response({"error": str(e)}, status=500) + + +@routes.get("/api/lang") +async def api_lang(request): + # Language from ?lang=xx, fallback to config, then to "en" + lang_code = request.query.get("lang") or CONFIG.get("site", {}).get("language", "en") + section = request.query.get("section") + + lang_file = os.path.join(LANG_DIR, f"{lang_code}.json") + if not os.path.exists(lang_file): + lang_file = os.path.join(LANG_DIR, "en.json") + + # Load JSON translations + with open(lang_file, encoding="utf-8") as f: + translations = json.load(f) + + if section: + section = section.lower() + if section in translations: + return web.json_response(translations[section]) + else: + return web.json_response( + {"error": f"Section '{section}' not found in {lang_code}"}, status=404 + ) + + # if no section requested → return full translation file + return web.json_response(translations) + + +@routes.get("/health") +async def health_check(request): + """Health check endpoint for monitoring and load balancers.""" + health_status = { + "status": "healthy", + "timestamp": datetime.datetime.now(datetime.UTC).isoformat(), + "version": __version__, + "git_revision": _git_revision_short, + } + + # Check database connectivity + try: + async with database.async_session() as session: + await session.execute(text("SELECT 1")) + health_status["database"] = "connected" + except Exception as e: + logger.error(f"Database health check failed: {e}") + health_status["database"] = "disconnected" + health_status["status"] = "unhealthy" + return web.json_response(health_status, status=503) + + # Get database file size + try: + db_url = CONFIG.get("database", {}).get("connection_string", "") + # Extract file path from SQLite connection string (e.g., "sqlite+aiosqlite:///packets.db") + if "sqlite" in db_url.lower(): + db_path = db_url.split("///")[-1].split("?")[0] + if os.path.exists(db_path): + db_size_bytes = os.path.getsize(db_path) + # Convert to human-readable format + if db_size_bytes < 1024: + health_status["database_size"] = f"{db_size_bytes} B" + elif db_size_bytes < 1024 * 1024: + health_status["database_size"] = f"{db_size_bytes / 1024:.2f} KB" + elif db_size_bytes < 1024 * 1024 * 1024: + health_status["database_size"] = f"{db_size_bytes / (1024 * 1024):.2f} MB" + else: + health_status["database_size"] = ( + f"{db_size_bytes / (1024 * 1024 * 1024):.2f} GB" + ) + health_status["database_size_bytes"] = db_size_bytes + except Exception as e: + logger.warning(f"Failed to get database size: {e}") + # Don't fail health check if we can't get size + + return web.json_response(health_status) + + +@routes.get("/version") +async def version_endpoint(request): + """Return version information including semver and git revision.""" + try: + version_info = get_version_info() + return web.json_response(version_info) + except Exception as e: + logger.error(f"Error in /version: {e}") + return web.json_response({"error": "Failed to fetch version info"}, status=500) + + +@routes.get("/api/packets_seen/{packet_id}") +async def api_packets_seen(request): + try: + # --- Validate packet_id --- + try: + packet_id = int(request.match_info["packet_id"]) + except (KeyError, ValueError): + return web.json_response( + {"error": "Invalid or missing packet_id"}, + status=400, + ) + + # --- Fetch list using your helper --- + rows = await store.get_packets_seen(packet_id) + + items = [] + for row in rows: # <-- FIX: normal for-loop + items.append( + { + "packet_id": row.packet_id, + "node_id": row.node_id, + "rx_time": row.rx_time, + "hop_limit": row.hop_limit, + "hop_start": row.hop_start, + "channel": row.channel, + "rx_snr": row.rx_snr, + "rx_rssi": row.rx_rssi, + "topic": row.topic, + "import_time_us": row.import_time_us, + } + ) + + return web.json_response({"seen": items}) + + except Exception: + logger.exception("Error in /api/packets_seen") + return web.json_response( + {"error": "Internal server error"}, + status=500, + ) + + +@routes.get("/api/traceroute/{packet_id}") +async def api_traceroute(request): + packet_id = int(request.match_info['packet_id']) + + traceroutes = list(await store.get_traceroute(packet_id)) + packet = await store.get_packet(packet_id) + + if not packet: + return web.json_response({"error": "Packet not found"}, status=404) + + tr_groups = [] + + # -------------------------------------------- + # Decode each traceroute entry + # -------------------------------------------- + for idx, tr in enumerate(traceroutes): + route = decode_payload.decode_payload(PortNum.TRACEROUTE_APP, tr.route) + + forward_list = list(route.route) + reverse_list = list(route.route_back) + + tr_groups.append( + { + "index": idx, + "gateway_node_id": tr.gateway_node_id, + "done": tr.done, + "forward_hops": forward_list, + "reverse_hops": reverse_list, + } + ) + + # -------------------------------------------- + # Compute UNIQUE paths + counts + winning path + # -------------------------------------------- + from collections import Counter + + forward_paths = [] + reverse_paths = [] + winning_paths = [] + + for tr in tr_groups: + f = tuple(tr["forward_hops"]) + r = tuple(tr["reverse_hops"]) + + if tr["forward_hops"]: + forward_paths.append(f) + + if tr["reverse_hops"]: + reverse_paths.append(r) + + if tr["done"]: + winning_paths.append(f) + + # Deduplicate + unique_forward_paths = sorted(set(forward_paths)) + unique_reverse_paths = sorted(set(reverse_paths)) + + # Count occurrences + forward_counts = Counter(forward_paths) + + # Convert for JSON output + unique_forward_paths_json = [ + {"path": list(p), "count": forward_counts[p]} for p in unique_forward_paths + ] + + unique_reverse_paths_json = [list(p) for p in unique_reverse_paths] + + winning_paths_json = [list(p) for p in set(winning_paths)] + + # -------------------------------------------- + # Final API output + # -------------------------------------------- + return web.json_response( + { + "packet": { + "id": packet.id, + "from": packet.from_node_id, + "to": packet.to_node_id, + "channel": packet.channel, + }, + "traceroute_packets": tr_groups, + "unique_forward_paths": unique_forward_paths_json, + "unique_reverse_paths": unique_reverse_paths_json, + "winning_paths": winning_paths_json, + } + ) + + +@routes.get("/api/stats/top") +async def api_stats_top(request): + """ + Returns nodes sorted by SEEN (high → low) with pagination. + """ + + period_type = request.query.get("period_type", "day") + length = int(request.query.get("length", 1)) + channel = request.query.get("channel") + + limit = min(int(request.query.get("limit", 20)), 100) + offset = int(request.query.get("offset", 0)) + + multiplier = 3600 if period_type == "hour" else 86400 + window_us = length * multiplier * 1_000_000 + + max_packet_import = select(func.max(PacketModel.import_time_us)).scalar_subquery() + max_seen_import = select(func.max(PacketSeenModel.import_time_us)).scalar_subquery() + + sent_cte = ( + select(PacketModel.from_node_id.label("node_id"), func.count().label("sent")) + .where(PacketModel.import_time_us >= max_packet_import - window_us) + .group_by(PacketModel.from_node_id) + .cte("sent") + ) + + seen_cte = ( + select(PacketModel.from_node_id.label("node_id"), func.count().label("seen")) + .select_from(PacketSeenModel) + .join(PacketModel, PacketModel.id == PacketSeenModel.packet_id) + .where(PacketSeenModel.import_time_us >= max_seen_import - window_us) + .group_by(PacketModel.from_node_id) + .cte("seen") + ) + + query = ( + select( + Node.node_id, + Node.long_name, + Node.short_name, + Node.channel, + func.coalesce(sent_cte.c.sent, 0).label("sent"), + func.coalesce(seen_cte.c.seen, 0).label("seen"), + ) + .select_from(Node) + .outerjoin(sent_cte, sent_cte.c.node_id == Node.node_id) + .outerjoin(seen_cte, seen_cte.c.node_id == Node.node_id) + .order_by(func.coalesce(seen_cte.c.seen, 0).desc()) + .limit(limit) + .offset(offset) + ) + + count_query = select(func.count()).select_from(Node) + + if channel: + query = query.where(Node.channel == channel) + count_query = count_query.where(Node.channel == channel) + + async with database.async_session() as session: + rows = (await session.execute(query)).all() + total = (await session.execute(count_query)).scalar() or 0 + + nodes = [] + for r in rows: + avg = r.seen / max(r.sent, 1) + nodes.append( + { + "node_id": r.node_id, + "long_name": r.long_name, + "short_name": r.short_name, + "channel": r.channel, + "sent": r.sent, + "seen": r.seen, + "avg": round(avg, 2), + } + ) + + return web.json_response( + { + "total": total, + "limit": limit, + "offset": offset, + "nodes": nodes, + } + ) diff --git a/mvrun.py b/mvrun.py new file mode 100644 index 00000000..469d9f4b --- /dev/null +++ b/mvrun.py @@ -0,0 +1,139 @@ +import argparse +import logging +import os +import signal +import subprocess +import sys +import threading + +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s %(filename)s:%(lineno)d [pid:%(process)d] %(levelname)s - %(message)s", + datefmt="%Y-%m-%d %H:%M:%S", +) + +logger = logging.getLogger(__name__) + +# Global list to track running processes +running_processes = [] +pid_files = [] + + +def cleanup_pid_file(pid_file): + """Remove a PID file if it exists""" + if os.path.exists(pid_file): + try: + os.remove(pid_file) + logger.info(f"Removed PID file {pid_file}") + except Exception as e: + logger.error(f"Error removing PID file {pid_file}: {e}") + + +def signal_handler(sig, frame): + """Handle Ctrl-C gracefully""" + logger.info("Received interrupt signal (Ctrl-C), shutting down gracefully...") + + # Terminate all running processes + for process in running_processes: + if process and process.poll() is None: # Process is still running + try: + logger.info(f"Terminating process PID {process.pid}") + process.terminate() + # Give it a moment to terminate gracefully + try: + process.wait(timeout=5) + logger.info(f"Process PID {process.pid} terminated successfully") + except subprocess.TimeoutExpired: + logger.warning(f"Process PID {process.pid} did not terminate, forcing kill") + process.kill() + process.wait() + except Exception as e: + logger.error(f"Error terminating process PID {process.pid}: {e}") + + # Clean up PID files + for pid_file in pid_files: + cleanup_pid_file(pid_file) + + logger.info("Shutdown complete") + sys.exit(0) + + +# Run python in subprocess +def run_script(python_executable, script_name, pid_file, *args): + process = None + try: + # Combine the script name and arguments + command = [python_executable, '-u', script_name] + list(args) + + # Run the subprocess (output goes directly to console for real-time viewing) + process = subprocess.Popen(command) + + # Track the process globally + running_processes.append(process) + + # Write PID to file + with open(pid_file, 'w') as f: + f.write(str(process.pid)) + logger.info(f"Started {script_name} with PID {process.pid}, written to {pid_file}") + + # Wait for the process to complete + process.wait() + + except Exception as e: + logger.error(f"Error running {script_name}: {e}") + finally: + # Clean up PID file when process exits + cleanup_pid_file(pid_file) + + +# Parse runtime argument (--config) and start subprocess threads +def main(): + # Register signal handler for Ctrl-C + signal.signal(signal.SIGINT, signal_handler) + signal.signal(signal.SIGTERM, signal_handler) + + parser = argparse.ArgumentParser( + description="Helper script to run the database and web frontend in separate threads." + ) + + # Add --config runtime argument + parser.add_argument('--config', help="Path to the configuration file.", default='config.ini') + parser.add_argument('--pid_dir', help="PID files path.", default='.') + parser.add_argument('--py_exec', help="Path to the Python executable.", default=sys.executable) + args = parser.parse_args() + + # PID file paths + db_pid_file = os.path.join(args.pid_dir, 'meshview-db.pid') + web_pid_file = os.path.join(args.pid_dir, 'meshview-web.pid') + + # Track PID files globally for cleanup + pid_files.append(db_pid_file) + pid_files.append(web_pid_file) + + # Database Thread + dbthrd = threading.Thread( + target=run_script, args=(args.py_exec, 'startdb.py', db_pid_file, '--config', args.config) + ) + + # Web server thread + webthrd = threading.Thread( + target=run_script, args=(args.py_exec, 'main.py', web_pid_file, '--config', args.config) + ) + + # Start Meshview subprocess threads + logger.info(f"Starting Meshview with config: {args.config}") + logger.info("Starting database thread...") + dbthrd.start() + logger.info("Starting web server thread...") + webthrd.start() + + try: + dbthrd.join() + webthrd.join() + except KeyboardInterrupt: + # This shouldn't be reached due to signal handler, but just in case + signal_handler(signal.SIGINT, None) + + +if __name__ == '__main__': + main() diff --git a/proto_def/LICENSE b/proto_def/LICENSE deleted file mode 100644 index f288702d..00000000 --- a/proto_def/LICENSE +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/proto_def/meshtastic/admin.options b/proto_def/meshtastic/admin.options deleted file mode 100644 index 022c4fd0..00000000 --- a/proto_def/meshtastic/admin.options +++ /dev/null @@ -1,14 +0,0 @@ -*AdminMessage.payload_variant anonymous_oneof:true - -*AdminMessage.session_passkey max_size:8 - -*AdminMessage.set_canned_message_module_messages max_size:201 -*AdminMessage.get_canned_message_module_messages_response max_size:201 -*AdminMessage.delete_file_request max_size:201 - -*AdminMessage.set_ringtone_message max_size:231 -*AdminMessage.get_ringtone_response max_size:231 - -*HamParameters.call_sign max_size:8 -*HamParameters.short_name max_size:5 -*NodeRemoteHardwarePinsResponse.node_remote_hardware_pins max_count:16 diff --git a/proto_def/meshtastic/admin.proto b/proto_def/meshtastic/admin.proto deleted file mode 100644 index 48d33923..00000000 --- a/proto_def/meshtastic/admin.proto +++ /dev/null @@ -1,417 +0,0 @@ -syntax = "proto3"; - -package meshtastic; - -import "meshtastic/channel.proto"; -import "meshtastic/config.proto"; -import "meshtastic/connection_status.proto"; -import "meshtastic/mesh.proto"; -import "meshtastic/module_config.proto"; - -option csharp_namespace = "Meshtastic.Protobufs"; -option go_package = "github.com/meshtastic/go/generated"; -option java_outer_classname = "AdminProtos"; -option java_package = "com.geeksville.mesh"; -option swift_prefix = ""; - -/* - * This message is handled by the Admin module and is responsible for all settings/channel read/write operations. - * This message is used to do settings operations to both remote AND local nodes. - * (Prior to 1.2 these operations were done via special ToRadio operations) - */ -message AdminMessage { - - /* - * The node generates this key and sends it with any get_x_response packets. - * The client MUST include the same key with any set_x commands. Key expires after 300 seconds. - * Prevents replay attacks for admin messages. - */ - bytes session_passkey = 101; - - /* - * TODO: REPLACE - */ - enum ConfigType { - /* - * TODO: REPLACE - */ - DEVICE_CONFIG = 0; - - /* - * TODO: REPLACE - */ - POSITION_CONFIG = 1; - - /* - * TODO: REPLACE - */ - POWER_CONFIG = 2; - - /* - * TODO: REPLACE - */ - NETWORK_CONFIG = 3; - - /* - * TODO: REPLACE - */ - DISPLAY_CONFIG = 4; - - /* - * TODO: REPLACE - */ - LORA_CONFIG = 5; - - /* - * TODO: REPLACE - */ - BLUETOOTH_CONFIG = 6; - - /* - * TODO: REPLACE - */ - SECURITY_CONFIG = 7; - - /* - * - */ - SESSIONKEY_CONFIG = 8; - } - - /* - * TODO: REPLACE - */ - enum ModuleConfigType { - /* - * TODO: REPLACE - */ - MQTT_CONFIG = 0; - - /* - * TODO: REPLACE - */ - SERIAL_CONFIG = 1; - - /* - * TODO: REPLACE - */ - EXTNOTIF_CONFIG = 2; - - /* - * TODO: REPLACE - */ - STOREFORWARD_CONFIG = 3; - - /* - * TODO: REPLACE - */ - RANGETEST_CONFIG = 4; - - /* - * TODO: REPLACE - */ - TELEMETRY_CONFIG = 5; - - /* - * TODO: REPLACE - */ - CANNEDMSG_CONFIG = 6; - - /* - * TODO: REPLACE - */ - AUDIO_CONFIG = 7; - - /* - * TODO: REPLACE - */ - REMOTEHARDWARE_CONFIG = 8; - - /* - * TODO: REPLACE - */ - NEIGHBORINFO_CONFIG = 9; - - /* - * TODO: REPLACE - */ - AMBIENTLIGHTING_CONFIG = 10; - - /* - * TODO: REPLACE - */ - DETECTIONSENSOR_CONFIG = 11; - - /* - * TODO: REPLACE - */ - PAXCOUNTER_CONFIG = 12; - } - - /* - * TODO: REPLACE - */ - oneof payload_variant { - /* - * Send the specified channel in the response to this message - * NOTE: This field is sent with the channel index + 1 (to ensure we never try to send 'zero' - which protobufs treats as not present) - */ - uint32 get_channel_request = 1; - - /* - * TODO: REPLACE - */ - Channel get_channel_response = 2; - - /* - * Send the current owner data in the response to this message. - */ - bool get_owner_request = 3; - - /* - * TODO: REPLACE - */ - User get_owner_response = 4; - - /* - * Ask for the following config data to be sent - */ - ConfigType get_config_request = 5; - - /* - * Send the current Config in the response to this message. - */ - Config get_config_response = 6; - - /* - * Ask for the following config data to be sent - */ - ModuleConfigType get_module_config_request = 7; - - /* - * Send the current Config in the response to this message. - */ - ModuleConfig get_module_config_response = 8; - - /* - * Get the Canned Message Module messages in the response to this message. - */ - bool get_canned_message_module_messages_request = 10; - - /* - * Get the Canned Message Module messages in the response to this message. - */ - string get_canned_message_module_messages_response = 11; - - /* - * Request the node to send device metadata (firmware, protobuf version, etc) - */ - bool get_device_metadata_request = 12; - - /* - * Device metadata response - */ - DeviceMetadata get_device_metadata_response = 13; - - /* - * Get the Ringtone in the response to this message. - */ - bool get_ringtone_request = 14; - - /* - * Get the Ringtone in the response to this message. - */ - string get_ringtone_response = 15; - - /* - * Request the node to send it's connection status - */ - bool get_device_connection_status_request = 16; - - /* - * Device connection status response - */ - DeviceConnectionStatus get_device_connection_status_response = 17; - - /* - * Setup a node for licensed amateur (ham) radio operation - */ - HamParameters set_ham_mode = 18; - - /* - * Get the mesh's nodes with their available gpio pins for RemoteHardware module use - */ - bool get_node_remote_hardware_pins_request = 19; - - /* - * Respond with the mesh's nodes with their available gpio pins for RemoteHardware module use - */ - NodeRemoteHardwarePinsResponse get_node_remote_hardware_pins_response = 20; - - /* - * Enter (UF2) DFU mode - * Only implemented on NRF52 currently - */ - bool enter_dfu_mode_request = 21; - - /* - * Delete the file by the specified path from the device - */ - string delete_file_request = 22; - - /* - * Set zero and offset for scale chips - */ - uint32 set_scale = 23; - - /* - * Set the owner for this node - */ - User set_owner = 32; - - /* - * Set channels (using the new API). - * A special channel is the "primary channel". - * The other records are secondary channels. - * Note: only one channel can be marked as primary. - * If the client sets a particular channel to be primary, the previous channel will be set to SECONDARY automatically. - */ - Channel set_channel = 33; - - /* - * Set the current Config - */ - Config set_config = 34; - - /* - * Set the current Config - */ - ModuleConfig set_module_config = 35; - - /* - * Set the Canned Message Module messages text. - */ - string set_canned_message_module_messages = 36; - - /* - * Set the ringtone for ExternalNotification. - */ - string set_ringtone_message = 37; - - /* - * Remove the node by the specified node-num from the NodeDB on the device - */ - uint32 remove_by_nodenum = 38; - - /* - * Set specified node-num to be favorited on the NodeDB on the device - */ - uint32 set_favorite_node = 39; - - /* - * Set specified node-num to be un-favorited on the NodeDB on the device - */ - uint32 remove_favorite_node = 40; - - /* - * Set fixed position data on the node and then set the position.fixed_position = true - */ - Position set_fixed_position = 41; - - /* - * Clear fixed position coordinates and then set position.fixed_position = false - */ - bool remove_fixed_position = 42; - - /* - * Set time only on the node - * Convenience method to set the time on the node (as Net quality) without any other position data - */ - fixed32 set_time_only = 43; - - /* - * Begins an edit transaction for config, module config, owner, and channel settings changes - * This will delay the standard *implicit* save to the file system and subsequent reboot behavior until committed (commit_edit_settings) - */ - bool begin_edit_settings = 64; - - /* - * Commits an open transaction for any edits made to config, module config, owner, and channel settings - */ - bool commit_edit_settings = 65; - - /* - * Tell the node to factory reset config everything; all device state and configuration will be returned to factory defaults and BLE bonds will be cleared. - */ - int32 factory_reset_device = 94; - - /* - * Tell the node to reboot into the OTA Firmware in this many seconds (or <0 to cancel reboot) - * Only Implemented for ESP32 Devices. This needs to be issued to send a new main firmware via bluetooth. - */ - int32 reboot_ota_seconds = 95; - - /* - * This message is only supported for the simulator Portduino build. - * If received the simulator will exit successfully. - */ - bool exit_simulator = 96; - - /* - * Tell the node to reboot in this many seconds (or <0 to cancel reboot) - */ - int32 reboot_seconds = 97; - - /* - * Tell the node to shutdown in this many seconds (or <0 to cancel shutdown) - */ - int32 shutdown_seconds = 98; - - /* - * Tell the node to factory reset config; all device state and configuration will be returned to factory defaults; BLE bonds will be preserved. - */ - int32 factory_reset_config = 99; - - /* - * Tell the node to reset the nodedb. - */ - int32 nodedb_reset = 100; - } -} - -/* - * Parameters for setting up Meshtastic for ameteur radio usage - */ -message HamParameters { - /* - * Amateur radio call sign, eg. KD2ABC - */ - string call_sign = 1; - - /* - * Transmit power in dBm at the LoRA transceiver, not including any amplification - */ - int32 tx_power = 2; - - /* - * The selected frequency of LoRA operation - * Please respect your local laws, regulations, and band plans. - * Ensure your radio is capable of operating of the selected frequency before setting this. - */ - float frequency = 3; - - /* - * Optional short name of user - */ - string short_name = 4; -} - -/* - * Response envelope for node_remote_hardware_pins - */ -message NodeRemoteHardwarePinsResponse { - /* - * Nodes and their respective remote hardware GPIO pins - */ - repeated NodeRemoteHardwarePin node_remote_hardware_pins = 1; -} diff --git a/proto_def/meshtastic/apponly.options b/proto_def/meshtastic/apponly.options deleted file mode 100644 index 28244de0..00000000 --- a/proto_def/meshtastic/apponly.options +++ /dev/null @@ -1 +0,0 @@ -*ChannelSet.settings max_count:8 diff --git a/proto_def/meshtastic/apponly.proto b/proto_def/meshtastic/apponly.proto deleted file mode 100644 index 100833f4..00000000 --- a/proto_def/meshtastic/apponly.proto +++ /dev/null @@ -1,31 +0,0 @@ -syntax = "proto3"; - -package meshtastic; - -import "meshtastic/channel.proto"; -import "meshtastic/config.proto"; - -option csharp_namespace = "Meshtastic.Protobufs"; -option go_package = "github.com/meshtastic/go/generated"; -option java_outer_classname = "AppOnlyProtos"; -option java_package = "com.geeksville.mesh"; -option swift_prefix = ""; - -/* - * This is the most compact possible representation for a set of channels. - * It includes only one PRIMARY channel (which must be first) and - * any SECONDARY channels. - * No DISABLED channels are included. - * This abstraction is used only on the the 'app side' of the world (ie python, javascript and android etc) to show a group of Channels as a (long) URL - */ -message ChannelSet { - /* - * Channel list with settings - */ - repeated ChannelSettings settings = 1; - - /* - * LoRa config - */ - Config.LoRaConfig lora_config = 2; -} diff --git a/proto_def/meshtastic/atak.options b/proto_def/meshtastic/atak.options deleted file mode 100644 index 6baa7ea8..00000000 --- a/proto_def/meshtastic/atak.options +++ /dev/null @@ -1,7 +0,0 @@ -*Contact.callsign max_size:120 -*Contact.device_callsign max_size:120 -*Status.battery int_size:8 -*PLI.course int_size:16 -*GeoChat.message max_size:200 -*GeoChat.to max_size:120 -*GeoChat.to_callsign max_size:120 diff --git a/proto_def/meshtastic/atak.proto b/proto_def/meshtastic/atak.proto deleted file mode 100644 index ccde3359..00000000 --- a/proto_def/meshtastic/atak.proto +++ /dev/null @@ -1,257 +0,0 @@ -syntax = "proto3"; - -package meshtastic; - -option csharp_namespace = "Meshtastic.Protobufs"; -option go_package = "github.com/meshtastic/go/generated"; -option java_outer_classname = "ATAKProtos"; -option java_package = "com.geeksville.mesh"; -option swift_prefix = ""; - -/* - * Packets for the official ATAK Plugin - */ -message TAKPacket { - /* - * Are the payloads strings compressed for LoRA transport? - */ - bool is_compressed = 1; - /* - * The contact / callsign for ATAK user - */ - Contact contact = 2; - /* - * The group for ATAK user - */ - Group group = 3; - /* - * The status of the ATAK EUD - */ - Status status = 4; - /* - * The payload of the packet - */ - oneof payload_variant { - /* - * TAK position report - */ - PLI pli = 5; - /* - * ATAK GeoChat message - */ - GeoChat chat = 6; - } -} - -/* - * ATAK GeoChat message - */ -message GeoChat { - /* - * The text message - */ - string message = 1; - - /* - * Uid recipient of the message - */ - optional string to = 2; - - /* - * Callsign of the recipient for the message - */ - optional string to_callsign = 3; -} - -/* - * ATAK Group - * <__group role='Team Member' name='Cyan'/> - */ -message Group { - /* - * Role of the group member - */ - MemberRole role = 1; - /* - * Team (color) - * Default Cyan - */ - Team team = 2; -} - -enum Team { - /* - * Unspecifed - */ - Unspecifed_Color = 0; - /* - * White - */ - White = 1; - /* - * Yellow - */ - Yellow = 2; - /* - * Orange - */ - Orange = 3; - /* - * Magenta - */ - Magenta = 4; - /* - * Red - */ - Red = 5; - /* - * Maroon - */ - Maroon = 6; - /* - * Purple - */ - Purple = 7; - /* - * Dark Blue - */ - Dark_Blue = 8; - /* - * Blue - */ - Blue = 9; - /* - * Cyan - */ - Cyan = 10; - /* - * Teal - */ - Teal = 11; - /* - * Green - */ - Green = 12; - /* - * Dark Green - */ - Dark_Green = 13; - /* - * Brown - */ - Brown = 14; -} - -/* - * Role of the group member - */ -enum MemberRole { - /* - * Unspecifed - */ - Unspecifed = 0; - /* - * Team Member - */ - TeamMember = 1; - /* - * Team Lead - */ - TeamLead = 2; - /* - * Headquarters - */ - HQ = 3; - /* - * Airsoft enthusiast - */ - Sniper = 4; - /* - * Medic - */ - Medic = 5; - /* - * ForwardObserver - */ - ForwardObserver = 6; - /* - * Radio Telephone Operator - */ - RTO = 7; - /* - * Doggo - */ - K9 = 8; -} - -/* - * ATAK EUD Status - * - */ -message Status { - /* - * Battery level - */ - uint32 battery = 1; -} - -/* - * ATAK Contact - * - */ -message Contact { - /* - * Callsign - */ - string callsign = 1; - - /* - * Device callsign - */ - string device_callsign = 2; - /* - * IP address of endpoint in integer form (0.0.0.0 default) - */ - // fixed32 enpoint_address = 3; - /* - * Port of endpoint (4242 default) - */ - // uint32 endpoint_port = 4; - /* - * Phone represented as integer - * Terrible practice, but we really need the wire savings - */ - // uint32 phone = 4; -} - -/* - * Position Location Information from ATAK - */ -message PLI { - /* - * The new preferred location encoding, multiply by 1e-7 to get degrees - * in floating point - */ - sfixed32 latitude_i = 1; - - /* - * The new preferred location encoding, multiply by 1e-7 to get degrees - * in floating point - */ - sfixed32 longitude_i = 2; - - /* - * Altitude (ATAK prefers HAE) - */ - int32 altitude = 3; - - /* - * Speed - */ - uint32 speed = 4; - - /* - * Course in degrees - */ - uint32 course = 5; -} diff --git a/proto_def/meshtastic/cannedmessages.options b/proto_def/meshtastic/cannedmessages.options deleted file mode 100644 index c1d537bb..00000000 --- a/proto_def/meshtastic/cannedmessages.options +++ /dev/null @@ -1 +0,0 @@ -*CannedMessageModuleConfig.messages max_size:201 diff --git a/proto_def/meshtastic/cannedmessages.proto b/proto_def/meshtastic/cannedmessages.proto deleted file mode 100644 index baa51343..00000000 --- a/proto_def/meshtastic/cannedmessages.proto +++ /dev/null @@ -1,19 +0,0 @@ -syntax = "proto3"; - -package meshtastic; - -option csharp_namespace = "Meshtastic.Protobufs"; -option go_package = "github.com/meshtastic/go/generated"; -option java_outer_classname = "CannedMessageConfigProtos"; -option java_package = "com.geeksville.mesh"; -option swift_prefix = ""; - -/* - * Canned message module configuration. - */ -message CannedMessageModuleConfig { - /* - * Predefined messages for canned message module separated by '|' characters. - */ - string messages = 1; -} diff --git a/proto_def/meshtastic/channel.options b/proto_def/meshtastic/channel.options deleted file mode 100644 index d0bdcbc9..00000000 --- a/proto_def/meshtastic/channel.options +++ /dev/null @@ -1,5 +0,0 @@ -*Channel.index int_size:8 - -# 256 bit or 128 bit psk key -*ChannelSettings.psk max_size:32 -*ChannelSettings.name max_size:12 diff --git a/proto_def/meshtastic/channel.proto b/proto_def/meshtastic/channel.proto deleted file mode 100644 index 16c8c19a..00000000 --- a/proto_def/meshtastic/channel.proto +++ /dev/null @@ -1,156 +0,0 @@ -syntax = "proto3"; - -package meshtastic; - -option csharp_namespace = "Meshtastic.Protobufs"; -option go_package = "github.com/meshtastic/go/generated"; -option java_outer_classname = "ChannelProtos"; -option java_package = "com.geeksville.mesh"; -option swift_prefix = ""; - -/* - * This information can be encoded as a QRcode/url so that other users can configure - * their radio to join the same channel. - * A note about how channel names are shown to users: channelname-X - * poundsymbol is a prefix used to indicate this is a channel name (idea from @professr). - * Where X is a letter from A-Z (base 26) representing a hash of the PSK for this - * channel - so that if the user changes anything about the channel (which does - * force a new PSK) this letter will also change. Thus preventing user confusion if - * two friends try to type in a channel name of "BobsChan" and then can't talk - * because their PSKs will be different. - * The PSK is hashed into this letter by "0x41 + [xor all bytes of the psk ] modulo 26" - * This also allows the option of someday if people have the PSK off (zero), the - * users COULD type in a channel name and be able to talk. - * FIXME: Add description of multi-channel support and how primary vs secondary channels are used. - * FIXME: explain how apps use channels for security. - * explain how remote settings and remote gpio are managed as an example - */ -message ChannelSettings { - /* - * Deprecated in favor of LoraConfig.channel_num - */ - uint32 channel_num = 1 [deprecated = true]; - - /* - * A simple pre-shared key for now for crypto. - * Must be either 0 bytes (no crypto), 16 bytes (AES128), or 32 bytes (AES256). - * A special shorthand is used for 1 byte long psks. - * These psks should be treated as only minimally secure, - * because they are listed in this source code. - * Those bytes are mapped using the following scheme: - * `0` = No crypto - * `1` = The special "default" channel key: {0xd4, 0xf1, 0xbb, 0x3a, 0x20, 0x29, 0x07, 0x59, 0xf0, 0xbc, 0xff, 0xab, 0xcf, 0x4e, 0x69, 0x01} - * `2` through 10 = The default channel key, except with 1 through 9 added to the last byte. - * Shown to user as simple1 through 10 - */ - bytes psk = 2; - - /* - * A SHORT name that will be packed into the URL. - * Less than 12 bytes. - * Something for end users to call the channel - * If this is the empty string it is assumed that this channel - * is the special (minimally secure) "Default"channel. - * In user interfaces it should be rendered as a local language translation of "X". - * For channel_num hashing empty string will be treated as "X". - * Where "X" is selected based on the English words listed above for ModemPreset - */ - string name = 3; - - /* - * Used to construct a globally unique channel ID. - * The full globally unique ID will be: "name.id" where ID is shown as base36. - * Assuming that the number of meshtastic users is below 20K (true for a long time) - * the chance of this 64 bit random number colliding with anyone else is super low. - * And the penalty for collision is low as well, it just means that anyone trying to decrypt channel messages might need to - * try multiple candidate channels. - * Any time a non wire compatible change is made to a channel, this field should be regenerated. - * There are a small number of 'special' globally known (and fairly) insecure standard channels. - * Those channels do not have a numeric id included in the settings, but instead it is pulled from - * a table of well known IDs. - * (see Well Known Channels FIXME) - */ - fixed32 id = 4; - - /* - * If true, messages on the mesh will be sent to the *public* internet by any gateway ndoe - */ - bool uplink_enabled = 5; - - /* - * If true, messages seen on the internet will be forwarded to the local mesh. - */ - bool downlink_enabled = 6; - - /* - * Per-channel module settings. - */ - ModuleSettings module_settings = 7; -} - -/* - * This message is specifically for modules to store per-channel configuration data. - */ -message ModuleSettings { - /* - * Bits of precision for the location sent in position packets. - */ - uint32 position_precision = 1; - - /* - * Controls whether or not the phone / clients should mute the current channel - * Useful for noisy public channels you don't necessarily want to disable - */ - bool is_client_muted = 2; -} - -/* - * A pair of a channel number, mode and the (sharable) settings for that channel - */ -message Channel { - /* - * How this channel is being used (or not). - * Note: this field is an enum to give us options for the future. - * In particular, someday we might make a 'SCANNING' option. - * SCANNING channels could have different frequencies and the radio would - * occasionally check that freq to see if anything is being transmitted. - * For devices that have multiple physical radios attached, we could keep multiple PRIMARY/SCANNING channels active at once to allow - * cross band routing as needed. - * If a device has only a single radio (the common case) only one channel can be PRIMARY at a time - * (but any number of SECONDARY channels can't be sent received on that common frequency) - */ - enum Role { - /* - * This channel is not in use right now - */ - DISABLED = 0; - - /* - * This channel is used to set the frequency for the radio - all other enabled channels must be SECONDARY - */ - PRIMARY = 1; - - /* - * Secondary channels are only used for encryption/decryption/authentication purposes. - * Their radio settings (freq etc) are ignored, only psk is used. - */ - SECONDARY = 2; - } - - /* - * The index of this channel in the channel table (from 0 to MAX_NUM_CHANNELS-1) - * (Someday - not currently implemented) An index of -1 could be used to mean "set by name", - * in which case the target node will find and set the channel by settings.name. - */ - int32 index = 1; - - /* - * The new settings, or NULL to disable that channel - */ - ChannelSettings settings = 2; - - /* - * TODO: REPLACE - */ - Role role = 3; -} diff --git a/proto_def/meshtastic/clientonly.options b/proto_def/meshtastic/clientonly.options deleted file mode 100644 index bc98b39d..00000000 --- a/proto_def/meshtastic/clientonly.options +++ /dev/null @@ -1,2 +0,0 @@ -*DeviceProfile.long_name max_size:40 -*DeviceProfile.short_name max_size:5 \ No newline at end of file diff --git a/proto_def/meshtastic/clientonly.proto b/proto_def/meshtastic/clientonly.proto deleted file mode 100644 index b1a27b16..00000000 --- a/proto_def/meshtastic/clientonly.proto +++ /dev/null @@ -1,42 +0,0 @@ -syntax = "proto3"; - -package meshtastic; - -import "meshtastic/localonly.proto"; - -option csharp_namespace = "Meshtastic.Protobufs"; -option go_package = "github.com/meshtastic/go/generated"; -option java_outer_classname = "ClientOnlyProtos"; -option java_package = "com.geeksville.mesh"; -option swift_prefix = ""; - -/* - * This abstraction is used to contain any configuration for provisioning a node on any client. - * It is useful for importing and exporting configurations. - */ -message DeviceProfile { - /* - * Long name for the node - */ - optional string long_name = 1; - - /* - * Short name of the node - */ - optional string short_name = 2; - - /* - * The url of the channels from our node - */ - optional string channel_url = 3; - - /* - * The Config of the node - */ - optional LocalConfig config = 4; - - /* - * The ModuleConfig of the node - */ - optional LocalModuleConfig module_config = 5; -} diff --git a/proto_def/meshtastic/config.options b/proto_def/meshtastic/config.options deleted file mode 100644 index 704d1411..00000000 --- a/proto_def/meshtastic/config.options +++ /dev/null @@ -1,21 +0,0 @@ -# longest current is 45 chars, plan with a bit of buffer -*DeviceConfig.tzdef max_size:65 - -*NetworkConfig.wifi_ssid max_size:33 -*NetworkConfig.wifi_psk max_size:65 -*NetworkConfig.ntp_server max_size:33 -*NetworkConfig.rsyslog_server max_size:33 - -# Max of three ignored nodes for our testing -*LoRaConfig.ignore_incoming max_count:3 - -*LoRaConfig.tx_power int_size:8 -*LoRaConfig.bandwidth int_size:16 -*LoRaConfig.coding_rate int_size:8 -*LoRaConfig.channel_num int_size:16 - -*PowerConfig.device_battery_ina_address int_size:8 - -*SecurityConfig.public_key max_size:32 -*SecurityConfig.private_key max_size:32 -*SecurityConfig.admin_key max_size:32 diff --git a/proto_def/meshtastic/config.proto b/proto_def/meshtastic/config.proto deleted file mode 100644 index 33fbd16a..00000000 --- a/proto_def/meshtastic/config.proto +++ /dev/null @@ -1,1091 +0,0 @@ -syntax = "proto3"; - -package meshtastic; - -option csharp_namespace = "Meshtastic.Protobufs"; -option go_package = "github.com/meshtastic/go/generated"; -option java_outer_classname = "ConfigProtos"; -option java_package = "com.geeksville.mesh"; -option swift_prefix = ""; - -message Config { - /* - * Configuration - */ - message DeviceConfig { - /* - * Defines the device's role on the Mesh network - */ - enum Role { - /* - * Description: App connected or stand alone messaging device. - * Technical Details: Default Role - */ - CLIENT = 0; - /* - * Description: Device that does not forward packets from other devices. - */ - CLIENT_MUTE = 1; - - /* - * Description: Infrastructure node for extending network coverage by relaying messages. Visible in Nodes list. - * Technical Details: Mesh packets will prefer to be routed over this node. This node will not be used by client apps. - * The wifi radio and the oled screen will be put to sleep. - * This mode may still potentially have higher power usage due to it's preference in message rebroadcasting on the mesh. - */ - ROUTER = 2; - - /* - * Description: Combination of both ROUTER and CLIENT. Not for mobile devices. - * Deprecated in v2.3.15 because improper usage is impacting public meshes: Use ROUTER or CLIENT instead. - */ - - ROUTER_CLIENT = 3 [deprecated = true]; - - /* - * Description: Infrastructure node for extending network coverage by relaying messages with minimal overhead. Not visible in Nodes list. - * Technical Details: Mesh packets will simply be rebroadcasted over this node. Nodes configured with this role will not originate NodeInfo, Position, Telemetry - * or any other packet type. They will simply rebroadcast any mesh packets on the same frequency, channel num, spread factor, and coding rate. - */ - REPEATER = 4; - - /* - * Description: Broadcasts GPS position packets as priority. - * Technical Details: Position Mesh packets will be prioritized higher and sent more frequently by default. - * When used in conjunction with power.is_power_saving = true, nodes will wake up, - * send position, and then sleep for position.position_broadcast_secs seconds. - */ - TRACKER = 5; - - /* - * Description: Broadcasts telemetry packets as priority. - * Technical Details: Telemetry Mesh packets will be prioritized higher and sent more frequently by default. - * When used in conjunction with power.is_power_saving = true, nodes will wake up, - * send environment telemetry, and then sleep for telemetry.environment_update_interval seconds. - */ - SENSOR = 6; - - /* - * Description: Optimized for ATAK system communication and reduces routine broadcasts. - * Technical Details: Used for nodes dedicated for connection to an ATAK EUD. - * Turns off many of the routine broadcasts to favor CoT packet stream - * from the Meshtastic ATAK plugin -> IMeshService -> Node - */ - TAK = 7; - - /* - * Description: Device that only broadcasts as needed for stealth or power savings. - * Technical Details: Used for nodes that "only speak when spoken to" - * Turns all of the routine broadcasts but allows for ad-hoc communication - * Still rebroadcasts, but with local only rebroadcast mode (known meshes only) - * Can be used for clandestine operation or to dramatically reduce airtime / power consumption - */ - CLIENT_HIDDEN = 8; - - /* - * Description: Broadcasts location as message to default channel regularly for to assist with device recovery. - * Technical Details: Used to automatically send a text message to the mesh - * with the current position of the device on a frequent interval: - * "I'm lost! Position: lat / long" - */ - LOST_AND_FOUND = 9; - - /* - * Description: Enables automatic TAK PLI broadcasts and reduces routine broadcasts. - * Technical Details: Turns off many of the routine broadcasts to favor ATAK CoT packet stream - * and automatic TAK PLI (position location information) broadcasts. - * Uses position module configuration to determine TAK PLI broadcast interval. - */ - TAK_TRACKER = 10; - } - - /* - * Defines the device's behavior for how messages are rebroadcast - */ - enum RebroadcastMode { - /* - * Default behavior. - * Rebroadcast any observed message, if it was on our private channel or from another mesh with the same lora params. - */ - ALL = 0; - - /* - * Same as behavior as ALL but skips packet decoding and simply rebroadcasts them. - * Only available in Repeater role. Setting this on any other roles will result in ALL behavior. - */ - ALL_SKIP_DECODING = 1; - - /* - * Ignores observed messages from foreign meshes that are open or those which it cannot decrypt. - * Only rebroadcasts message on the nodes local primary / secondary channels. - */ - LOCAL_ONLY = 2; - - /* - * Ignores observed messages from foreign meshes like LOCAL_ONLY, - * but takes it step further by also ignoring messages from nodenums not in the node's known list (NodeDB) - */ - KNOWN_ONLY = 3; - } - - /* - * Sets the role of node - */ - Role role = 1; - - /* - * Disabling this will disable the SerialConsole by not initilizing the StreamAPI - * Moved to SecurityConfig - */ - bool serial_enabled = 2[deprecated = true]; - - /* - * By default we turn off logging as soon as an API client connects (to keep shared serial link quiet). - * Set this to true to leave the debug log outputting even when API is active. - * Moved to SecurityConfig - */ - bool debug_log_enabled = 3[deprecated = true]; - - /* - * For boards without a hard wired button, this is the pin number that will be used - * Boards that have more than one button can swap the function with this one. defaults to BUTTON_PIN if defined. - */ - uint32 button_gpio = 4; - - /* - * For boards without a PWM buzzer, this is the pin number that will be used - * Defaults to PIN_BUZZER if defined. - */ - uint32 buzzer_gpio = 5; - - /* - * Sets the role of node - */ - RebroadcastMode rebroadcast_mode = 6; - - /* - * Send our nodeinfo this often - * Defaults to 900 Seconds (15 minutes) - */ - uint32 node_info_broadcast_secs = 7; - - /* - * Treat double tap interrupt on supported accelerometers as a button press if set to true - */ - bool double_tap_as_button_press = 8; - - /* - * If true, device is considered to be "managed" by a mesh administrator - * Clients should then limit available configuration and administrative options inside the user interface - * Moved to SecurityConfig - */ - bool is_managed = 9[deprecated = true]; - - /* - * Disables the triple-press of user button to enable or disable GPS - */ - bool disable_triple_click = 10; - - /* - * POSIX Timezone definition string from https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv. - */ - string tzdef = 11; - - /* - * If true, disable the default blinking LED (LED_PIN) behavior on the device - */ - bool led_heartbeat_disabled = 12; - } - - /* - * Position Config - */ - message PositionConfig { - /* - * Bit field of boolean configuration options, indicating which optional - * fields to include when assembling POSITION messages. - * Longitude, latitude, altitude, speed, heading, and DOP - * are always included (also time if GPS-synced) - * NOTE: the more fields are included, the larger the message will be - - * leading to longer airtime and a higher risk of packet loss - */ - enum PositionFlags { - /* - * Required for compilation - */ - UNSET = 0x0000; - - /* - * Include an altitude value (if available) - */ - ALTITUDE = 0x0001; - - /* - * Altitude value is MSL - */ - ALTITUDE_MSL = 0x0002; - - /* - * Include geoidal separation - */ - GEOIDAL_SEPARATION = 0x0004; - - /* - * Include the DOP value ; PDOP used by default, see below - */ - DOP = 0x0008; - - /* - * If POS_DOP set, send separate HDOP / VDOP values instead of PDOP - */ - HVDOP = 0x0010; - - /* - * Include number of "satellites in view" - */ - SATINVIEW = 0x0020; - - /* - * Include a sequence number incremented per packet - */ - SEQ_NO = 0x0040; - - /* - * Include positional timestamp (from GPS solution) - */ - TIMESTAMP = 0x0080; - - /* - * Include positional heading - * Intended for use with vehicle not walking speeds - * walking speeds are likely to be error prone like the compass - */ - HEADING = 0x0100; - - /* - * Include positional speed - * Intended for use with vehicle not walking speeds - * walking speeds are likely to be error prone like the compass - */ - SPEED = 0x0200; - } - - enum GpsMode { - /* - * GPS is present but disabled - */ - DISABLED = 0; - - /* - * GPS is present and enabled - */ - ENABLED = 1; - - /* - * GPS is not present on the device - */ - NOT_PRESENT = 2; - } - - /* - * We should send our position this often (but only if it has changed significantly) - * Defaults to 15 minutes - */ - uint32 position_broadcast_secs = 1; - - /* - * Adaptive position braoadcast, which is now the default. - */ - bool position_broadcast_smart_enabled = 2; - - /* - * If set, this node is at a fixed position. - * We will generate GPS position updates at the regular interval, but use whatever the last lat/lon/alt we have for the node. - * The lat/lon/alt can be set by an internal GPS or with the help of the app. - */ - bool fixed_position = 3; - - /* - * Is GPS enabled for this node? - */ - bool gps_enabled = 4 [deprecated = true]; - - /* - * How often should we try to get GPS position (in seconds) - * or zero for the default of once every 30 seconds - * or a very large value (maxint) to update only once at boot. - */ - uint32 gps_update_interval = 5; - - /* - * Deprecated in favor of using smart / regular broadcast intervals as implicit attempt time - */ - uint32 gps_attempt_time = 6 [deprecated = true]; - - /* - * Bit field of boolean configuration options for POSITION messages - * (bitwise OR of PositionFlags) - */ - uint32 position_flags = 7; - - /* - * (Re)define GPS_RX_PIN for your board. - */ - uint32 rx_gpio = 8; - - /* - * (Re)define GPS_TX_PIN for your board. - */ - uint32 tx_gpio = 9; - - /* - * The minimum distance in meters traveled (since the last send) before we can send a position to the mesh if position_broadcast_smart_enabled - */ - uint32 broadcast_smart_minimum_distance = 10; - - /* - * The minimum number of seconds (since the last send) before we can send a position to the mesh if position_broadcast_smart_enabled - */ - uint32 broadcast_smart_minimum_interval_secs = 11; - - /* - * (Re)define PIN_GPS_EN for your board. - */ - uint32 gps_en_gpio = 12; - - /* - * Set where GPS is enabled, disabled, or not present - */ - GpsMode gps_mode = 13; - } - - /* - * Power Config\ - * See [Power Config](/docs/settings/config/power) for additional power config details. - */ - message PowerConfig { - /* - * Description: Will sleep everything as much as possible, for the tracker and sensor role this will also include the lora radio. - * Don't use this setting if you want to use your device with the phone apps or are using a device without a user button. - * Technical Details: Works for ESP32 devices and NRF52 devices in the Sensor or Tracker roles - */ - bool is_power_saving = 1; - - /* - * Description: If non-zero, the device will fully power off this many seconds after external power is removed. - */ - uint32 on_battery_shutdown_after_secs = 2; - - /* - * Ratio of voltage divider for battery pin eg. 3.20 (R1=100k, R2=220k) - * Overrides the ADC_MULTIPLIER defined in variant for battery voltage calculation. - * https://meshtastic.org/docs/configuration/radio/power/#adc-multiplier-override - * Should be set to floating point value between 2 and 6 - */ - float adc_multiplier_override = 3; - - /* - * Description: The number of seconds for to wait before turning off BLE in No Bluetooth states - * Technical Details: ESP32 Only 0 for default of 1 minute - */ - uint32 wait_bluetooth_secs = 4; - - /* - * Super Deep Sleep Seconds - * While in Light Sleep if mesh_sds_timeout_secs is exceeded we will lower into super deep sleep - * for this value (default 1 year) or a button press - * 0 for default of one year - */ - uint32 sds_secs = 6; - - /* - * Description: In light sleep the CPU is suspended, LoRa radio is on, BLE is off an GPS is on - * Technical Details: ESP32 Only 0 for default of 300 - */ - uint32 ls_secs = 7; - - /* - * Description: While in light sleep when we receive packets on the LoRa radio we will wake and handle them and stay awake in no BLE mode for this value - * Technical Details: ESP32 Only 0 for default of 10 seconds - */ - uint32 min_wake_secs = 8; - - /* - * I2C address of INA_2XX to use for reading device battery voltage - */ - uint32 device_battery_ina_address = 9; - - /* - * If non-zero, we want powermon log outputs. With the particular (bitfield) sources enabled. - * Note: we picked an ID of 32 so that lower more efficient IDs can be used for more frequently used options. - */ - uint64 powermon_enables = 32; - } - - /* - * Network Config - */ - message NetworkConfig { - enum AddressMode { - /* - * obtain ip address via DHCP - */ - DHCP = 0; - - /* - * use static ip address - */ - STATIC = 1; - } - - message IpV4Config { - /* - * Static IP address - */ - fixed32 ip = 1; - - /* - * Static gateway address - */ - fixed32 gateway = 2; - - /* - * Static subnet mask - */ - fixed32 subnet = 3; - - /* - * Static DNS server address - */ - fixed32 dns = 4; - } - - /* - * Enable WiFi (disables Bluetooth) - */ - bool wifi_enabled = 1; - - /* - * If set, this node will try to join the specified wifi network and - * acquire an address via DHCP - */ - string wifi_ssid = 3; - - /* - * If set, will be use to authenticate to the named wifi - */ - string wifi_psk = 4; - - /* - * NTP server to use if WiFi is conneced, defaults to `0.pool.ntp.org` - */ - string ntp_server = 5; - - /* - * Enable Ethernet - */ - bool eth_enabled = 6; - - /* - * acquire an address via DHCP or assign static - */ - AddressMode address_mode = 7; - - /* - * struct to keep static address - */ - IpV4Config ipv4_config = 8; - - /* - * rsyslog Server and Port - */ - string rsyslog_server = 9; - } - - /* - * Display Config - */ - message DisplayConfig { - /* - * How the GPS coordinates are displayed on the OLED screen. - */ - enum GpsCoordinateFormat { - /* - * GPS coordinates are displayed in the normal decimal degrees format: - * DD.DDDDDD DDD.DDDDDD - */ - DEC = 0; - - /* - * GPS coordinates are displayed in the degrees minutes seconds format: - * DD°MM'SS"C DDD°MM'SS"C, where C is the compass point representing the locations quadrant - */ - DMS = 1; - - /* - * Universal Transverse Mercator format: - * ZZB EEEEEE NNNNNNN, where Z is zone, B is band, E is easting, N is northing - */ - UTM = 2; - - /* - * Military Grid Reference System format: - * ZZB CD EEEEE NNNNN, where Z is zone, B is band, C is the east 100k square, D is the north 100k square, - * E is easting, N is northing - */ - MGRS = 3; - - /* - * Open Location Code (aka Plus Codes). - */ - OLC = 4; - - /* - * Ordnance Survey Grid Reference (the National Grid System of the UK). - * Format: AB EEEEE NNNNN, where A is the east 100k square, B is the north 100k square, - * E is the easting, N is the northing - */ - OSGR = 5; - } - - /* - * Unit display preference - */ - enum DisplayUnits { - /* - * Metric (Default) - */ - METRIC = 0; - - /* - * Imperial - */ - IMPERIAL = 1; - } - - /* - * Override OLED outo detect with this if it fails. - */ - enum OledType { - /* - * Default / Auto - */ - OLED_AUTO = 0; - - /* - * Default / Auto - */ - OLED_SSD1306 = 1; - - /* - * Default / Auto - */ - OLED_SH1106 = 2; - - /* - * Can not be auto detected but set by proto. Used for 128x128 screens - */ - OLED_SH1107 = 3; - } - - /* - * Number of seconds the screen stays on after pressing the user button or receiving a message - * 0 for default of one minute MAXUINT for always on - */ - uint32 screen_on_secs = 1; - - /* - * How the GPS coordinates are formatted on the OLED screen. - */ - GpsCoordinateFormat gps_format = 2; - - /* - * Automatically toggles to the next page on the screen like a carousel, based the specified interval in seconds. - * Potentially useful for devices without user buttons. - */ - uint32 auto_screen_carousel_secs = 3; - - /* - * If this is set, the displayed compass will always point north. if unset, the old behaviour - * (top of display is heading direction) is used. - */ - bool compass_north_top = 4; - - /* - * Flip screen vertically, for cases that mount the screen upside down - */ - bool flip_screen = 5; - - /* - * Perferred display units - */ - DisplayUnits units = 6; - - /* - * Override auto-detect in screen - */ - OledType oled = 7; - - enum DisplayMode { - /* - * Default. The old style for the 128x64 OLED screen - */ - DEFAULT = 0; - - /* - * Rearrange display elements to cater for bicolor OLED displays - */ - TWOCOLOR = 1; - - /* - * Same as TwoColor, but with inverted top bar. Not so good for Epaper displays - */ - INVERTED = 2; - - /* - * TFT Full Color Displays (not implemented yet) - */ - COLOR = 3; - } - /* - * Display Mode - */ - DisplayMode displaymode = 8; - - /* - * Print first line in pseudo-bold? FALSE is original style, TRUE is bold - */ - bool heading_bold = 9; - - /* - * Should we wake the screen up on accelerometer detected motion or tap - */ - bool wake_on_tap_or_motion = 10; - - enum CompassOrientation { - /* - * The compass and the display are in the same orientation. - */ - DEGREES_0 = 0; - - /* - * Rotate the compass by 90 degrees. - */ - DEGREES_90 = 1; - - /* - * Rotate the compass by 180 degrees. - */ - DEGREES_180 = 2; - - /* - * Rotate the compass by 270 degrees. - */ - DEGREES_270 = 3; - - /* - * Don't rotate the compass, but invert the result. - */ - DEGREES_0_INVERTED = 4; - - /* - * Rotate the compass by 90 degrees and invert. - */ - DEGREES_90_INVERTED = 5; - - /* - * Rotate the compass by 180 degrees and invert. - */ - DEGREES_180_INVERTED = 6; - - /* - * Rotate the compass by 270 degrees and invert. - */ - DEGREES_270_INVERTED = 7; - } - - /* - * Indicates how to rotate or invert the compass output to accurate display on the display. - */ - CompassOrientation compass_orientation = 11; - } - - /* - * Lora Config - */ - message LoRaConfig { - enum RegionCode { - /* - * Region is not set - */ - UNSET = 0; - - /* - * United States - */ - US = 1; - - /* - * European Union 433mhz - */ - EU_433 = 2; - - /* - * European Union 868mhz - */ - EU_868 = 3; - - /* - * China - */ - CN = 4; - - /* - * Japan - */ - JP = 5; - - /* - * Australia / New Zealand - */ - ANZ = 6; - - /* - * Korea - */ - KR = 7; - - /* - * Taiwan - */ - TW = 8; - - /* - * Russia - */ - RU = 9; - - /* - * India - */ - IN = 10; - - /* - * New Zealand 865mhz - */ - NZ_865 = 11; - - /* - * Thailand - */ - TH = 12; - - /* - * WLAN Band - */ - LORA_24 = 13; - - /* - * Ukraine 433mhz - */ - UA_433 = 14; - - /* - * Ukraine 868mhz - */ - UA_868 = 15; - - /* - * Malaysia 433mhz - */ - MY_433 = 16; - - /* - * Malaysia 919mhz - */ - MY_919 = 17; - - /* - * Singapore 923mhz - */ - SG_923 = 18; - } - - /* - * Standard predefined channel settings - * Note: these mappings must match ModemPreset Choice in the device code. - */ - enum ModemPreset { - /* - * Long Range - Fast - */ - LONG_FAST = 0; - - /* - * Long Range - Slow - */ - LONG_SLOW = 1; - - /* - * Very Long Range - Slow - * Deprecated in 2.5: Works only with txco and is unusably slow - */ - VERY_LONG_SLOW = 2 [deprecated = true]; - - /* - * Medium Range - Slow - */ - MEDIUM_SLOW = 3; - - /* - * Medium Range - Fast - */ - MEDIUM_FAST = 4; - - /* - * Short Range - Slow - */ - SHORT_SLOW = 5; - - /* - * Short Range - Fast - */ - SHORT_FAST = 6; - - /* - * Long Range - Moderately Fast - */ - LONG_MODERATE = 7; - - /* - * Short Range - Turbo - * This is the fastest preset and the only one with 500kHz bandwidth. - * It is not legal to use in all regions due to this wider bandwidth. - */ - SHORT_TURBO = 8; - } - - /* - * When enabled, the `modem_preset` fields will be adhered to, else the `bandwidth`/`spread_factor`/`coding_rate` - * will be taked from their respective manually defined fields - */ - bool use_preset = 1; - - /* - * Either modem_config or bandwidth/spreading/coding will be specified - NOT BOTH. - * As a heuristic: If bandwidth is specified, do not use modem_config. - * Because protobufs take ZERO space when the value is zero this works out nicely. - * This value is replaced by bandwidth/spread_factor/coding_rate. - * If you'd like to experiment with other options add them to MeshRadio.cpp in the device code. - */ - ModemPreset modem_preset = 2; - - /* - * Bandwidth in MHz - * Certain bandwidth numbers are 'special' and will be converted to the - * appropriate floating point value: 31 -> 31.25MHz - */ - uint32 bandwidth = 3; - - /* - * A number from 7 to 12. - * Indicates number of chirps per symbol as 1< 7 results in the default - */ - uint32 hop_limit = 8; - - /* - * Disable TX from the LoRa radio. Useful for hot-swapping antennas and other tests. - * Defaults to false - */ - bool tx_enabled = 9; - - /* - * If zero, then use default max legal continuous power (ie. something that won't - * burn out the radio hardware) - * In most cases you should use zero here. - * Units are in dBm. - */ - int32 tx_power = 10; - - /* - * This controls the actual hardware frequency the radio transmits on. - * Most users should never need to be exposed to this field/concept. - * A channel number between 1 and NUM_CHANNELS (whatever the max is in the current region). - * If ZERO then the rule is "use the old channel name hash based - * algorithm to derive the channel number") - * If using the hash algorithm the channel number will be: hash(channel_name) % - * NUM_CHANNELS (Where num channels depends on the regulatory region). - */ - uint32 channel_num = 11; - - /* - * If true, duty cycle limits will be exceeded and thus you're possibly not following - * the local regulations if you're not a HAM. - * Has no effect if the duty cycle of the used region is 100%. - */ - bool override_duty_cycle = 12; - - /* - * If true, sets RX boosted gain mode on SX126X based radios - */ - bool sx126x_rx_boosted_gain = 13; - - /* - * This parameter is for advanced users and licensed HAM radio operators. - * Ignore Channel Calculation and use this frequency instead. The frequency_offset - * will still be applied. This will allow you to use out-of-band frequencies. - * Please respect your local laws and regulations. If you are a HAM, make sure you - * enable HAM mode and turn off encryption. - */ - float override_frequency = 14; - - /* - * If true, disable the build-in PA FAN using pin define in RF95_FAN_EN. - */ - bool pa_fan_disabled = 15; - - /* - * For testing it is useful sometimes to force a node to never listen to - * particular other nodes (simulating radio out of range). All nodenums listed - * in ignore_incoming will have packets they send dropped on receive (by router.cpp) - */ - repeated uint32 ignore_incoming = 103; - - /* - * If true, the device will not process any packets received via LoRa that passed via MQTT anywhere on the path towards it. - */ - bool ignore_mqtt = 104; - } - - message BluetoothConfig { - enum PairingMode { - /* - * Device generates a random PIN that will be shown on the screen of the device for pairing - */ - RANDOM_PIN = 0; - - /* - * Device requires a specified fixed PIN for pairing - */ - FIXED_PIN = 1; - - /* - * Device requires no PIN for pairing - */ - NO_PIN = 2; - } - - /* - * Enable Bluetooth on the device - */ - bool enabled = 1; - - /* - * Determines the pairing strategy for the device - */ - PairingMode mode = 2; - - /* - * Specified PIN for PairingMode.FixedPin - */ - uint32 fixed_pin = 3; - - /* - * Enables device (serial style logs) over Bluetooth - * Moved to SecurityConfig - */ - bool device_logging_enabled = 4[deprecated = true]; - } - - message SecurityConfig { - - /* - * The public key of the user's device. - * Sent out to other nodes on the mesh to allow them to compute a shared secret key. - */ - bytes public_key = 1; - - /* - * The private key of the device. - * Used to create a shared key with a remote device. - */ - bytes private_key = 2; - - /* - * The public key authorized to send admin messages to this node. - */ - bytes admin_key = 3; - - /* - * If true, device is considered to be "managed" by a mesh administrator via admin messages - * Device is managed by a mesh administrator. - */ - bool is_managed = 4; - - /* - * Serial Console over the Stream API." - */ - bool serial_enabled = 5; - - /* - * By default we turn off logging as soon as an API client connects (to keep shared serial link quiet). - * Output live debug logging over serial. - */ - bool debug_log_api_enabled = 6; - - /* - * Enables device (serial style logs) over Bluetooth - */ - bool bluetooth_logging_enabled = 7; - - /* - * Allow incoming device control over the insecure legacy admin channel. - */ - bool admin_channel_enabled = 8; - } - - /* - * Blank config request, strictly for getting the session key - */ - message SessionkeyConfig {} - - /* - * Payload Variant - */ - oneof payload_variant { - DeviceConfig device = 1; - PositionConfig position = 2; - PowerConfig power = 3; - NetworkConfig network = 4; - DisplayConfig display = 5; - LoRaConfig lora = 6; - BluetoothConfig bluetooth = 7; - SecurityConfig security = 8; - SessionkeyConfig sessionkey = 9; - } -} diff --git a/proto_def/meshtastic/connection_status.options b/proto_def/meshtastic/connection_status.options deleted file mode 100644 index d4901ddc..00000000 --- a/proto_def/meshtastic/connection_status.options +++ /dev/null @@ -1 +0,0 @@ -*WifiConnectionStatus.ssid max_size:33 diff --git a/proto_def/meshtastic/connection_status.proto b/proto_def/meshtastic/connection_status.proto deleted file mode 100644 index 75515965..00000000 --- a/proto_def/meshtastic/connection_status.proto +++ /dev/null @@ -1,120 +0,0 @@ -syntax = "proto3"; - -package meshtastic; - -option csharp_namespace = "Meshtastic.Protobufs"; -option go_package = "github.com/meshtastic/go/generated"; -option java_outer_classname = "ConnStatusProtos"; -option java_package = "com.geeksville.mesh"; -option swift_prefix = ""; - -message DeviceConnectionStatus { - /* - * WiFi Status - */ - optional WifiConnectionStatus wifi = 1; - /* - * WiFi Status - */ - optional EthernetConnectionStatus ethernet = 2; - - /* - * Bluetooth Status - */ - optional BluetoothConnectionStatus bluetooth = 3; - - /* - * Serial Status - */ - optional SerialConnectionStatus serial = 4; -} - -/* - * WiFi connection status - */ -message WifiConnectionStatus { - /* - * Connection status - */ - NetworkConnectionStatus status = 1; - - /* - * WiFi access point SSID - */ - string ssid = 2; - - /* - * RSSI of wireless connection - */ - int32 rssi = 3; -} - -/* - * Ethernet connection status - */ -message EthernetConnectionStatus { - /* - * Connection status - */ - NetworkConnectionStatus status = 1; -} - -/* - * Ethernet or WiFi connection status - */ -message NetworkConnectionStatus { - /* - * IP address of device - */ - fixed32 ip_address = 1; - - /* - * Whether the device has an active connection or not - */ - bool is_connected = 2; - - /* - * Whether the device has an active connection to an MQTT broker or not - */ - bool is_mqtt_connected = 3; - - /* - * Whether the device is actively remote syslogging or not - */ - bool is_syslog_connected = 4; -} - -/* - * Bluetooth connection status - */ -message BluetoothConnectionStatus { - /* - * The pairing PIN for bluetooth - */ - uint32 pin = 1; - - /* - * RSSI of bluetooth connection - */ - int32 rssi = 2; - - /* - * Whether the device has an active connection or not - */ - bool is_connected = 3; -} - -/* - * Serial connection status - */ -message SerialConnectionStatus { - /* - * Serial baud rate - */ - uint32 baud = 1; - - /* - * Whether the device has an active connection or not - */ - bool is_connected = 2; -} diff --git a/proto_def/meshtastic/deviceonly.options b/proto_def/meshtastic/deviceonly.options deleted file mode 100644 index 6f533177..00000000 --- a/proto_def/meshtastic/deviceonly.options +++ /dev/null @@ -1,21 +0,0 @@ -# options for nanopb -# https://jpa.kapsi.fi/nanopb/docs/reference.html#proto-file-options - -# FIXME - max_count is actually 32 but we save/load this as one long string of preencoded MeshPacket bytes - not a big array in RAM -*DeviceState.receive_queue max_count:1 - -*ChannelFile.channels max_count:8 - -*OEMStore.oem_text max_size:40 -*OEMStore.oem_icon_bits max_size:2048 -*OEMStore.oem_aes_key max_size:32 - -*DeviceState.node_remote_hardware_pins max_count:12 - -*NodeInfoLite.channel int_size:8 -*NodeInfoLite.hops_away int_size:8 - -*UserLite.long_name max_size:40 -*UserLite.short_name max_size:5 -*UserLite.public_key max_size:32 # public key -*UserLite.macaddr max_size:6 fixed_length:true diff --git a/proto_def/meshtastic/deviceonly.proto b/proto_def/meshtastic/deviceonly.proto deleted file mode 100644 index b3fb4407..00000000 --- a/proto_def/meshtastic/deviceonly.proto +++ /dev/null @@ -1,302 +0,0 @@ -syntax = "proto3"; - -package meshtastic; - -import "meshtastic/channel.proto"; -import "meshtastic/localonly.proto"; -import "meshtastic/mesh.proto"; -import "meshtastic/telemetry.proto"; -import "meshtastic/config.proto"; -import "nanopb.proto"; - -option csharp_namespace = "Meshtastic.Protobufs"; -option go_package = "github.com/meshtastic/go/generated"; -option java_outer_classname = "DeviceOnly"; -option java_package = "com.geeksville.mesh"; -option swift_prefix = ""; -option (nanopb_fileopt).include = ""; - - -/* - * Position with static location information only for NodeDBLite - */ -message PositionLite { - /* - * The new preferred location encoding, multiply by 1e-7 to get degrees - * in floating point - */ - sfixed32 latitude_i = 1; - - /* - * TODO: REPLACE - */ - sfixed32 longitude_i = 2; - - /* - * In meters above MSL (but see issue #359) - */ - int32 altitude = 3; - - /* - * This is usually not sent over the mesh (to save space), but it is sent - * from the phone so that the local device can set its RTC If it is sent over - * the mesh (because there are devices on the mesh without GPS), it will only - * be sent by devices which has a hardware GPS clock. - * seconds since 1970 - */ - fixed32 time = 4; - - /* - * TODO: REPLACE - */ - Position.LocSource location_source = 5; -} - -message UserLite { - /* - * This is the addr of the radio. - */ - bytes macaddr = 1 [deprecated = true]; - - /* - * A full name for this user, i.e. "Kevin Hester" - */ - string long_name = 2; - - /* - * A VERY short name, ideally two characters. - * Suitable for a tiny OLED screen - */ - string short_name = 3; - - /* - * TBEAM, HELTEC, etc... - * Starting in 1.2.11 moved to hw_model enum in the NodeInfo object. - * Apps will still need the string here for older builds - * (so OTA update can find the right image), but if the enum is available it will be used instead. - */ - HardwareModel hw_model = 4; - - /* - * In some regions Ham radio operators have different bandwidth limitations than others. - * If this user is a licensed operator, set this flag. - * Also, "long_name" should be their licence number. - */ - bool is_licensed = 5; - - /* - * Indicates that the user's role in the mesh - */ - Config.DeviceConfig.Role role = 6; - - /* - * The public key of the user's device. - * This is sent out to other nodes on the mesh to allow them to compute a shared secret key. - */ - bytes public_key = 7; -} - -message NodeInfoLite { - /* - * The node number - */ - uint32 num = 1; - - /* - * The user info for this node - */ - UserLite user = 2; - - /* - * This position data. Note: before 1.2.14 we would also store the last time we've heard from this node in position.time, that is no longer true. - * Position.time now indicates the last time we received a POSITION from that node. - */ - PositionLite position = 3; - - /* - * Returns the Signal-to-noise ratio (SNR) of the last received message, - * as measured by the receiver. Return SNR of the last received message in dB - */ - float snr = 4; - - /* - * Set to indicate the last time we received a packet from this node - */ - fixed32 last_heard = 5; - /* - * The latest device metrics for the node. - */ - DeviceMetrics device_metrics = 6; - - /* - * local channel index we heard that node on. Only populated if its not the default channel. - */ - uint32 channel = 7; - - /* - * True if we witnessed the node over MQTT instead of LoRA transport - */ - bool via_mqtt = 8; - - /* - * Number of hops away from us this node is (0 if adjacent) - */ - uint32 hops_away = 9; - - /* - * True if node is in our favorites list - * Persists between NodeDB internal clean ups - */ - bool is_favorite = 10; -} - -/* - * Font sizes for the device screen - */ -enum ScreenFonts { - /* - * TODO: REPLACE - */ - FONT_SMALL = 0; - - /* - * TODO: REPLACE - */ - FONT_MEDIUM = 1; - - /* - * TODO: REPLACE - */ - FONT_LARGE = 2; -} - - -/* - * This message is never sent over the wire, but it is used for serializing DB - * state to flash in the device code - * FIXME, since we write this each time we enter deep sleep (and have infinite - * flash) it would be better to use some sort of append only data structure for - * the receive queue and use the preferences store for the other stuff - */ -message DeviceState { - /* - * Read only settings/info about this node - */ - MyNodeInfo my_node = 2; - - /* - * My owner info - */ - User owner = 3; - - /* - * Received packets saved for delivery to the phone - */ - repeated MeshPacket receive_queue = 5; - - /* - * A version integer used to invalidate old save files when we make - * incompatible changes This integer is set at build time and is private to - * NodeDB.cpp in the device code. - */ - uint32 version = 8; - - /* - * We keep the last received text message (only) stored in the device flash, - * so we can show it on the screen. - * Might be null - */ - MeshPacket rx_text_message = 7; - - /* - * Used only during development. - * Indicates developer is testing and changes should never be saved to flash. - * Deprecated in 2.3.1 - */ - bool no_save = 9 [deprecated = true]; - - /* - * Some GPS receivers seem to have bogus settings from the factory, so we always do one factory reset. - */ - bool did_gps_reset = 11; - - /* - * We keep the last received waypoint stored in the device flash, - * so we can show it on the screen. - * Might be null - */ - MeshPacket rx_waypoint = 12; - - /* - * The mesh's nodes with their available gpio pins for RemoteHardware module - */ - repeated NodeRemoteHardwarePin node_remote_hardware_pins = 13; - - /* - * New lite version of NodeDB to decrease memory footprint - */ - repeated NodeInfoLite node_db_lite = 14 [(nanopb).callback_datatype = "std::vector"]; -} - -/* - * The on-disk saved channels - */ -message ChannelFile { - /* - * The channels our node knows about - */ - repeated Channel channels = 1; - - /* - * A version integer used to invalidate old save files when we make - * incompatible changes This integer is set at build time and is private to - * NodeDB.cpp in the device code. - */ - uint32 version = 2; -} - -/* - * This can be used for customizing the firmware distribution. If populated, - * show a secondary bootup screen with custom logo and text for 2.5 seconds. - */ -message OEMStore { - /* - * The Logo width in Px - */ - uint32 oem_icon_width = 1; - - /* - * The Logo height in Px - */ - uint32 oem_icon_height = 2; - - /* - * The Logo in XBM bytechar format - */ - bytes oem_icon_bits = 3; - - /* - * Use this font for the OEM text. - */ - ScreenFonts oem_font = 4; - - /* - * Use this font for the OEM text. - */ - string oem_text = 5; - - /* - * The default device encryption key, 16 or 32 byte - */ - bytes oem_aes_key = 6; - - /* - * A Preset LocalConfig to apply during factory reset - */ - LocalConfig oem_local_config = 7; - - /* - * A Preset LocalModuleConfig to apply during factory reset - */ - LocalModuleConfig oem_local_module_config = 8; -} diff --git a/proto_def/meshtastic/localonly.proto b/proto_def/meshtastic/localonly.proto deleted file mode 100644 index bcb27964..00000000 --- a/proto_def/meshtastic/localonly.proto +++ /dev/null @@ -1,140 +0,0 @@ -syntax = "proto3"; - -package meshtastic; - -import "meshtastic/config.proto"; -import "meshtastic/module_config.proto"; - -option csharp_namespace = "Meshtastic.Protobufs"; -option go_package = "github.com/meshtastic/go/generated"; -option java_outer_classname = "LocalOnlyProtos"; -option java_package = "com.geeksville.mesh"; -option swift_prefix = ""; - -/* - * Protobuf structures common to apponly.proto and deviceonly.proto - * This is never sent over the wire, only for local use - */ - -message LocalConfig { - /* - * The part of the config that is specific to the Device - */ - Config.DeviceConfig device = 1; - - /* - * The part of the config that is specific to the GPS Position - */ - Config.PositionConfig position = 2; - - /* - * The part of the config that is specific to the Power settings - */ - Config.PowerConfig power = 3; - - /* - * The part of the config that is specific to the Wifi Settings - */ - Config.NetworkConfig network = 4; - - /* - * The part of the config that is specific to the Display - */ - Config.DisplayConfig display = 5; - - /* - * The part of the config that is specific to the Lora Radio - */ - Config.LoRaConfig lora = 6; - - /* - * The part of the config that is specific to the Bluetooth settings - */ - Config.BluetoothConfig bluetooth = 7; - - /* - * A version integer used to invalidate old save files when we make - * incompatible changes This integer is set at build time and is private to - * NodeDB.cpp in the device code. - */ - uint32 version = 8; - - /* - * The part of the config that is specific to Security settings - */ - Config.SecurityConfig security = 9; -} - -message LocalModuleConfig { - /* - * The part of the config that is specific to the MQTT module - */ - ModuleConfig.MQTTConfig mqtt = 1; - - /* - * The part of the config that is specific to the Serial module - */ - ModuleConfig.SerialConfig serial = 2; - - /* - * The part of the config that is specific to the ExternalNotification module - */ - ModuleConfig.ExternalNotificationConfig external_notification = 3; - - /* - * The part of the config that is specific to the Store & Forward module - */ - ModuleConfig.StoreForwardConfig store_forward = 4; - - /* - * The part of the config that is specific to the RangeTest module - */ - ModuleConfig.RangeTestConfig range_test = 5; - - /* - * The part of the config that is specific to the Telemetry module - */ - ModuleConfig.TelemetryConfig telemetry = 6; - - /* - * The part of the config that is specific to the Canned Message module - */ - ModuleConfig.CannedMessageConfig canned_message = 7; - - /* - * The part of the config that is specific to the Audio module - */ - ModuleConfig.AudioConfig audio = 9; - - /* - * The part of the config that is specific to the Remote Hardware module - */ - ModuleConfig.RemoteHardwareConfig remote_hardware = 10; - - /* - * The part of the config that is specific to the Neighbor Info module - */ - ModuleConfig.NeighborInfoConfig neighbor_info = 11; - - /* - * The part of the config that is specific to the Ambient Lighting module - */ - ModuleConfig.AmbientLightingConfig ambient_lighting = 12; - - /* - * The part of the config that is specific to the Detection Sensor module - */ - ModuleConfig.DetectionSensorConfig detection_sensor = 13; - - /* - * Paxcounter Config - */ - ModuleConfig.PaxcounterConfig paxcounter = 14; - - /* - * A version integer used to invalidate old save files when we make - * incompatible changes This integer is set at build time and is private to - * NodeDB.cpp in the device code. - */ - uint32 version = 8; -} diff --git a/proto_def/meshtastic/mesh.options b/proto_def/meshtastic/mesh.options deleted file mode 100644 index 541e4c91..00000000 --- a/proto_def/meshtastic/mesh.options +++ /dev/null @@ -1,75 +0,0 @@ -# options for nanopb -# https://jpa.kapsi.fi/nanopb/docs/reference.html#proto-file-options - -*macaddr max_size:6 fixed_length:true # macaddrs -*id max_size:16 # node id strings -*public_key max_size:32 # public key - -*User.long_name max_size:40 -*User.short_name max_size:5 - -*RouteDiscovery.route max_count:8 -*RouteDiscovery.snr_towards max_count:8 -*RouteDiscovery.snr_towards int_size:8 -*RouteDiscovery.route_back max_count:8 -*RouteDiscovery.snr_back max_count:8 -*RouteDiscovery.snr_back int_size:8 - -# note: this payload length is ONLY the bytes that are sent inside of the Data protobuf (excluding protobuf overhead). The 16 byte header is -# outside of this envelope -*Data.payload max_size:237 - -*NodeInfo.channel int_size:8 -*NodeInfo.hops_away int_size:8 - -# Big enough for 1.2.28.568032c-d -*MyNodeInfo.firmware_version max_size:18 - -*MyNodeInfo.air_period_tx max_count:8 -*MyNodeInfo.air_period_rx max_count:8 - -# Note: the actual limit (because of header bytes) on the size of encrypted payloads is 251 bytes, but I use 256 -# here because we might need to fill with zeros for padding to encryption block size (16 bytes per block) -*MeshPacket.encrypted max_size:256 -*MeshPacket.payload_variant anonymous_oneof:true -*MeshPacket.hop_limit int_size:8 -*MeshPacket.hop_start int_size:8 -*MeshPacket.channel int_size:8 - -*QueueStatus.res int_size:8 -*QueueStatus.free int_size:8 -*QueueStatus.maxlen int_size:8 - -*ToRadio.payload_variant anonymous_oneof:true - -*FromRadio.payload_variant anonymous_oneof:true - -*Routing.variant anonymous_oneof:true - -*LogRecord.message max_size:384 -*LogRecord.source max_size:32 - -*FileInfo.file_name max_size:228 - -*ClientNotification.message max_size:400 - -# MyMessage.name max_size:40 -# or fixed_length or fixed_count, or max_count - -#This value may want to be a few bytes smaller to compensate for the parent fields. -*Compressed.data max_size:237 - -*Waypoint.name max_size:30 -*Waypoint.description max_size:100 - -*NeighborInfo.neighbors max_count:10 - -*DeviceMetadata.firmware_version max_size:18 - -*MqttClientProxyMessage.topic max_size:60 -*MqttClientProxyMessage.data max_size:435 -*MqttClientProxyMessage.text max_size:435 - -*ChunkedPayload.chunk_count int_size:16 -*ChunkedPayload.chunk_index int_size:16 -*ChunkedPayload.payload_chunk max_size:228 \ No newline at end of file diff --git a/proto_def/meshtastic/mesh.proto b/proto_def/meshtastic/mesh.proto deleted file mode 100644 index aaf5adbd..00000000 --- a/proto_def/meshtastic/mesh.proto +++ /dev/null @@ -1,1868 +0,0 @@ -syntax = "proto3"; - -package meshtastic; - -import "meshtastic/channel.proto"; -import "meshtastic/config.proto"; -import "meshtastic/module_config.proto"; -import "meshtastic/portnums.proto"; -import "meshtastic/telemetry.proto"; -import "meshtastic/xmodem.proto"; - -option csharp_namespace = "Meshtastic.Protobufs"; -option go_package = "github.com/meshtastic/go/generated"; -option java_outer_classname = "MeshProtos"; -option java_package = "com.geeksville.mesh"; -option swift_prefix = ""; - -/* - * a gps position - */ -message Position { - /* - * The new preferred location encoding, multiply by 1e-7 to get degrees - * in floating point - */ - optional sfixed32 latitude_i = 1; - - /* - * TODO: REPLACE - */ - optional sfixed32 longitude_i = 2; - - /* - * In meters above MSL (but see issue #359) - */ - optional int32 altitude = 3; - - /* - * This is usually not sent over the mesh (to save space), but it is sent - * from the phone so that the local device can set its time if it is sent over - * the mesh (because there are devices on the mesh without GPS or RTC). - * seconds since 1970 - */ - fixed32 time = 4; - - /* - * How the location was acquired: manual, onboard GPS, external (EUD) GPS - */ - enum LocSource { - /* - * TODO: REPLACE - */ - LOC_UNSET = 0; - - /* - * TODO: REPLACE - */ - LOC_MANUAL = 1; - - /* - * TODO: REPLACE - */ - LOC_INTERNAL = 2; - - /* - * TODO: REPLACE - */ - LOC_EXTERNAL = 3; - } - - /* - * TODO: REPLACE - */ - LocSource location_source = 5; - - /* - * How the altitude was acquired: manual, GPS int/ext, etc - * Default: same as location_source if present - */ - enum AltSource { - /* - * TODO: REPLACE - */ - ALT_UNSET = 0; - - /* - * TODO: REPLACE - */ - ALT_MANUAL = 1; - - /* - * TODO: REPLACE - */ - ALT_INTERNAL = 2; - - /* - * TODO: REPLACE - */ - ALT_EXTERNAL = 3; - - /* - * TODO: REPLACE - */ - ALT_BAROMETRIC = 4; - } - - /* - * TODO: REPLACE - */ - AltSource altitude_source = 6; - - /* - * Positional timestamp (actual timestamp of GPS solution) in integer epoch seconds - */ - fixed32 timestamp = 7; - - /* - * Pos. timestamp milliseconds adjustment (rarely available or required) - */ - int32 timestamp_millis_adjust = 8; - - /* - * HAE altitude in meters - can be used instead of MSL altitude - */ - optional sint32 altitude_hae = 9; - - /* - * Geoidal separation in meters - */ - optional sint32 altitude_geoidal_separation = 10; - - /* - * Horizontal, Vertical and Position Dilution of Precision, in 1/100 units - * - PDOP is sufficient for most cases - * - for higher precision scenarios, HDOP and VDOP can be used instead, - * in which case PDOP becomes redundant (PDOP=sqrt(HDOP^2 + VDOP^2)) - * TODO: REMOVE/INTEGRATE - */ - uint32 PDOP = 11; - - /* - * TODO: REPLACE - */ - uint32 HDOP = 12; - - /* - * TODO: REPLACE - */ - uint32 VDOP = 13; - - /* - * GPS accuracy (a hardware specific constant) in mm - * multiplied with DOP to calculate positional accuracy - * Default: "'bout three meters-ish" :) - */ - uint32 gps_accuracy = 14; - - /* - * Ground speed in m/s and True North TRACK in 1/100 degrees - * Clarification of terms: - * - "track" is the direction of motion (measured in horizontal plane) - * - "heading" is where the fuselage points (measured in horizontal plane) - * - "yaw" indicates a relative rotation about the vertical axis - * TODO: REMOVE/INTEGRATE - */ - optional uint32 ground_speed = 15; - - /* - * TODO: REPLACE - */ - optional uint32 ground_track = 16; - - /* - * GPS fix quality (from NMEA GxGGA statement or similar) - */ - uint32 fix_quality = 17; - - /* - * GPS fix type 2D/3D (from NMEA GxGSA statement) - */ - uint32 fix_type = 18; - - /* - * GPS "Satellites in View" number - */ - uint32 sats_in_view = 19; - - /* - * Sensor ID - in case multiple positioning sensors are being used - */ - uint32 sensor_id = 20; - - /* - * Estimated/expected time (in seconds) until next update: - * - if we update at fixed intervals of X seconds, use X - * - if we update at dynamic intervals (based on relative movement etc), - * but "AT LEAST every Y seconds", use Y - */ - uint32 next_update = 21; - - /* - * A sequence number, incremented with each Position message to help - * detect lost updates if needed - */ - uint32 seq_number = 22; - - /* - * Indicates the bits of precision set by the sending node - */ - uint32 precision_bits = 23; -} - -/* - * Note: these enum names must EXACTLY match the string used in the device - * bin/build-all.sh script. - * Because they will be used to find firmware filenames in the android app for OTA updates. - * To match the old style filenames, _ is converted to -, p is converted to . - */ -enum HardwareModel { - /* - * TODO: REPLACE - */ - UNSET = 0; - - /* - * TODO: REPLACE - */ - TLORA_V2 = 1; - - /* - * TODO: REPLACE - */ - TLORA_V1 = 2; - - /* - * TODO: REPLACE - */ - TLORA_V2_1_1P6 = 3; - - /* - * TODO: REPLACE - */ - TBEAM = 4; - - /* - * The original heltec WiFi_Lora_32_V2, which had battery voltage sensing hooked to GPIO 13 - * (see HELTEC_V2 for the new version). - */ - HELTEC_V2_0 = 5; - - /* - * TODO: REPLACE - */ - TBEAM_V0P7 = 6; - - /* - * TODO: REPLACE - */ - T_ECHO = 7; - - /* - * TODO: REPLACE - */ - TLORA_V1_1P3 = 8; - - /* - * TODO: REPLACE - */ - RAK4631 = 9; - - /* - * The new version of the heltec WiFi_Lora_32_V2 board that has battery sensing hooked to GPIO 37. - * Sadly they did not update anything on the silkscreen to identify this board - */ - HELTEC_V2_1 = 10; - - /* - * Ancient heltec WiFi_Lora_32 board - */ - HELTEC_V1 = 11; - - /* - * New T-BEAM with ESP32-S3 CPU - */ - LILYGO_TBEAM_S3_CORE = 12; - - /* - * RAK WisBlock ESP32 core: https://docs.rakwireless.com/Product-Categories/WisBlock/RAK11200/Overview/ - */ - RAK11200 = 13; - - /* - * B&Q Consulting Nano Edition G1: https://uniteng.com/wiki/doku.php?id=meshtastic:nano - */ - NANO_G1 = 14; - - /* - * TODO: REPLACE - */ - TLORA_V2_1_1P8 = 15; - - /* - * TODO: REPLACE - */ - TLORA_T3_S3 = 16; - - /* - * B&Q Consulting Nano G1 Explorer: https://wiki.uniteng.com/en/meshtastic/nano-g1-explorer - */ - NANO_G1_EXPLORER = 17; - - /* - * B&Q Consulting Nano G2 Ultra: https://wiki.uniteng.com/en/meshtastic/nano-g2-ultra - */ - NANO_G2_ULTRA = 18; - - /* - * LoRAType device: https://loratype.org/ - */ - LORA_TYPE = 19; - - /* - * wiphone https://www.wiphone.io/ - */ - WIPHONE = 20; - - /* - * WIO Tracker WM1110 family from Seeed Studio. Includes wio-1110-tracker and wio-1110-sdk - */ - WIO_WM1110 = 21; - - /* - * RAK2560 Solar base station based on RAK4630 - */ - RAK2560 = 22; - - /* - * Heltec HRU-3601: https://heltec.org/project/hru-3601/ - */ - HELTEC_HRU_3601 = 23; - - /* - * B&Q Consulting Station Edition G1: https://uniteng.com/wiki/doku.php?id=meshtastic:station - */ - STATION_G1 = 25; - - /* - * RAK11310 (RP2040 + SX1262) - */ - RAK11310 = 26; - - /* - * Makerfabs SenseLoRA Receiver (RP2040 + RFM96) - */ - SENSELORA_RP2040 = 27; - - /* - * Makerfabs SenseLoRA Industrial Monitor (ESP32-S3 + RFM96) - */ - SENSELORA_S3 = 28; - - /* - * Canary Radio Company - CanaryOne: https://canaryradio.io/products/canaryone - */ - CANARYONE = 29; - - /* - * Waveshare RP2040 LoRa - https://www.waveshare.com/rp2040-lora.htm - */ - RP2040_LORA = 30; - - /* - * B&Q Consulting Station G2: https://wiki.uniteng.com/en/meshtastic/station-g2 - */ - STATION_G2 = 31; - - /* - * --------------------------------------------------------------------------- - * Less common/prototype boards listed here (needs one more byte over the air) - * --------------------------------------------------------------------------- - */ - LORA_RELAY_V1 = 32; - - /* - * TODO: REPLACE - */ - NRF52840DK = 33; - - /* - * TODO: REPLACE - */ - PPR = 34; - - /* - * TODO: REPLACE - */ - GENIEBLOCKS = 35; - - /* - * TODO: REPLACE - */ - NRF52_UNKNOWN = 36; - - /* - * TODO: REPLACE - */ - PORTDUINO = 37; - - /* - * The simulator built into the android app - */ - ANDROID_SIM = 38; - - /* - * Custom DIY device based on @NanoVHF schematics: https://github.com/NanoVHF/Meshtastic-DIY/tree/main/Schematics - */ - DIY_V1 = 39; - - /* - * nRF52840 Dongle : https://www.nordicsemi.com/Products/Development-hardware/nrf52840-dongle/ - */ - NRF52840_PCA10059 = 40; - - /* - * Custom Disaster Radio esp32 v3 device https://github.com/sudomesh/disaster-radio/tree/master/hardware/board_esp32_v3 - */ - DR_DEV = 41; - - /* - * M5 esp32 based MCU modules with enclosure, TFT and LORA Shields. All Variants (Basic, Core, Fire, Core2, Paper) https://m5stack.com/ - */ - M5STACK = 42; - - /* - * New Heltec LoRA32 with ESP32-S3 CPU - */ - HELTEC_V3 = 43; - - /* - * New Heltec Wireless Stick Lite with ESP32-S3 CPU - */ - HELTEC_WSL_V3 = 44; - - /* - * New BETAFPV ELRS Micro TX Module 2.4G with ESP32 CPU - */ - BETAFPV_2400_TX = 45; - - /* - * BetaFPV ExpressLRS "Nano" TX Module 900MHz with ESP32 CPU - */ - BETAFPV_900_NANO_TX = 46; - - /* - * Raspberry Pi Pico (W) with Waveshare SX1262 LoRa Node Module - */ - RPI_PICO = 47; - - /* - * Heltec Wireless Tracker with ESP32-S3 CPU, built-in GPS, and TFT - * Newer V1.1, version is written on the PCB near the display. - */ - HELTEC_WIRELESS_TRACKER = 48; - - /* - * Heltec Wireless Paper with ESP32-S3 CPU and E-Ink display - */ - HELTEC_WIRELESS_PAPER = 49; - - /* - * LilyGo T-Deck with ESP32-S3 CPU, Keyboard and IPS display - */ - T_DECK = 50; - - /* - * LilyGo T-Watch S3 with ESP32-S3 CPU and IPS display - */ - T_WATCH_S3 = 51; - - /* - * Bobricius Picomputer with ESP32-S3 CPU, Keyboard and IPS display - */ - PICOMPUTER_S3 = 52; - - /* - * Heltec HT-CT62 with ESP32-C3 CPU and SX1262 LoRa - */ - HELTEC_HT62 = 53; - - /* - * EBYTE SPI LoRa module and ESP32-S3 - */ - EBYTE_ESP32_S3 = 54; - - /* - * Waveshare ESP32-S3-PICO with PICO LoRa HAT and 2.9inch e-Ink - */ - ESP32_S3_PICO = 55; - - /* - * CircuitMess Chatter 2 LLCC68 Lora Module and ESP32 Wroom - * Lora module can be swapped out for a Heltec RA-62 which is "almost" pin compatible - * with one cut and one jumper Meshtastic works - */ - CHATTER_2 = 56; - - /* - * Heltec Wireless Paper, With ESP32-S3 CPU and E-Ink display - * Older "V1.0" Variant, has no "version sticker" - * E-Ink model is DEPG0213BNS800 - * Tab on the screen protector is RED - * Flex connector marking is FPC-7528B - */ - HELTEC_WIRELESS_PAPER_V1_0 = 57; - - /* - * Heltec Wireless Tracker with ESP32-S3 CPU, built-in GPS, and TFT - * Older "V1.0" Variant - */ - HELTEC_WIRELESS_TRACKER_V1_0 = 58; - - /* - * unPhone with ESP32-S3, TFT touchscreen, LSM6DS3TR-C accelerometer and gyroscope - */ - UNPHONE = 59; - - /* - * Teledatics TD-LORAC NRF52840 based M.2 LoRA module - * Compatible with the TD-WRLS development board - */ - TD_LORAC = 60; - - /* - * CDEBYTE EoRa-S3 board using their own MM modules, clone of LILYGO T3S3 - */ - CDEBYTE_EORA_S3 = 61; - - /* - * TWC_MESH_V4 - * Adafruit NRF52840 feather express with SX1262, SSD1306 OLED and NEO6M GPS - */ - TWC_MESH_V4 = 62; - - /* - * NRF52_PROMICRO_DIY - * Promicro NRF52840 with SX1262/LLCC68, SSD1306 OLED and NEO6M GPS - */ - NRF52_PROMICRO_DIY = 63; - - /* - * RadioMaster 900 Bandit Nano, https://www.radiomasterrc.com/products/bandit-nano-expresslrs-rf-module - * ESP32-D0WDQ6 With SX1276/SKY66122, SSD1306 OLED and No GPS - */ - RADIOMASTER_900_BANDIT_NANO = 64; - - /* - * Heltec Capsule Sensor V3 with ESP32-S3 CPU, Portable LoRa device that can replace GNSS modules or sensors - */ - HELTEC_CAPSULE_SENSOR_V3 = 65; - - /* - * Heltec Vision Master T190 with ESP32-S3 CPU, and a 1.90 inch TFT display - */ - HELTEC_VISION_MASTER_T190 = 66; - - /* - * Heltec Vision Master E213 with ESP32-S3 CPU, and a 2.13 inch E-Ink display - */ - HELTEC_VISION_MASTER_E213 = 67; - - /* - * Heltec Vision Master E290 with ESP32-S3 CPU, and a 2.9 inch E-Ink display - */ - HELTEC_VISION_MASTER_E290 = 68; - - /* - * Heltec Mesh Node T114 board with nRF52840 CPU, and a 1.14 inch TFT display, Ultimate low-power design, - * specifically adapted for the Meshtatic project - */ - HELTEC_MESH_NODE_T114 = 69; - - /* - * Sensecap Indicator from Seeed Studio. ESP32-S3 device with TFT and RP2040 coprocessor - */ - SENSECAP_INDICATOR = 70; - - /* - * Seeed studio T1000-E tracker card. NRF52840 w/ LR1110 radio, GPS, button, buzzer, and sensors. - */ - TRACKER_T1000_E = 71; - - /* - * RAK3172 STM32WLE5 Module (https://store.rakwireless.com/products/wisduo-lpwan-module-rak3172) - */ - RAK3172 = 72; - - /* - * Seeed Studio Wio-E5 (either mini or Dev kit) using STM32WL chip. - */ - WIO_E5 = 73; - - /* - * RadioMaster 900 Bandit, https://www.radiomasterrc.com/products/bandit-expresslrs-rf-module - * SSD1306 OLED and No GPS - */ - RADIOMASTER_900_BANDIT = 74; - - /* - * Minewsemi ME25LS01 (ME25LE01_V1.0). NRF52840 w/ LR1110 radio, buttons and leds and pins. - */ - ME25LS01_4Y10TD = 75; - - /* - * ------------------------------------------------------------------------------------------------------------------------------------------ - * Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits. - * ------------------------------------------------------------------------------------------------------------------------------------------ - */ - PRIVATE_HW = 255; -} - -/* - * Broadcast when a newly powered mesh node wants to find a node num it can use - * Sent from the phone over bluetooth to set the user id for the owner of this node. - * Also sent from nodes to each other when a new node signs on (so all clients can have this info) - * The algorithm is as follows: - * when a node starts up, it broadcasts their user and the normal flow is for all - * other nodes to reply with their User as well (so the new node can build its nodedb) - * If a node ever receives a User (not just the first broadcast) message where - * the sender node number equals our node number, that indicates a collision has - * occurred and the following steps should happen: - * If the receiving node (that was already in the mesh)'s macaddr is LOWER than the - * new User who just tried to sign in: it gets to keep its nodenum. - * We send a broadcast message of OUR User (we use a broadcast so that the other node can - * receive our message, considering we have the same id - it also serves to let - * observers correct their nodedb) - this case is rare so it should be okay. - * If any node receives a User where the macaddr is GTE than their local macaddr, - * they have been vetoed and should pick a new random nodenum (filtering against - * whatever it knows about the nodedb) and rebroadcast their User. - * A few nodenums are reserved and will never be requested: - * 0xff - broadcast - * 0 through 3 - for future use - */ -message User { - /* - * A globally unique ID string for this user. - * In the case of Signal that would mean +16504442323, for the default macaddr derived id it would be !<8 hexidecimal bytes>. - * Note: app developers are encouraged to also use the following standard - * node IDs "^all" (for broadcast), "^local" (for the locally connected node) - */ - string id = 1; - - /* - * A full name for this user, i.e. "Kevin Hester" - */ - string long_name = 2; - - /* - * A VERY short name, ideally two characters. - * Suitable for a tiny OLED screen - */ - string short_name = 3; - - /* - * Deprecated in Meshtastic 2.1.x - * This is the addr of the radio. - * Not populated by the phone, but added by the esp32 when broadcasting - */ - bytes macaddr = 4 [deprecated = true]; - - /* - * TBEAM, HELTEC, etc... - * Starting in 1.2.11 moved to hw_model enum in the NodeInfo object. - * Apps will still need the string here for older builds - * (so OTA update can find the right image), but if the enum is available it will be used instead. - */ - HardwareModel hw_model = 5; - - /* - * In some regions Ham radio operators have different bandwidth limitations than others. - * If this user is a licensed operator, set this flag. - * Also, "long_name" should be their licence number. - */ - bool is_licensed = 6; - - /* - * Indicates that the user's role in the mesh - */ - Config.DeviceConfig.Role role = 7; - - /* - * The public key of the user's device. - * This is sent out to other nodes on the mesh to allow them to compute a shared secret key. - */ - bytes public_key = 8; -} - -/* - * A message used in a traceroute - */ -message RouteDiscovery { - /* - * The list of nodenums this packet has visited so far to the destination. - */ - repeated fixed32 route = 1; - - /* - * The list of SNRs (in dB, scaled by 4) in the route towards the destination. - */ - repeated int32 snr_towards = 2; - - /* - * The list of nodenums the packet has visited on the way back from the destination. - */ - repeated fixed32 route_back = 3; - - /* - * The list of SNRs (in dB, scaled by 4) in the route back from the destination. - */ - repeated int32 snr_back = 4; -} - -/* - * A Routing control Data packet handled by the routing module - */ -message Routing { - /* - * A failure in delivering a message (usually used for routing control messages, but might be provided in addition to ack.fail_id to provide - * details on the type of failure). - */ - enum Error { - /* - * This message is not a failure - */ - NONE = 0; - - /* - * Our node doesn't have a route to the requested destination anymore. - */ - NO_ROUTE = 1; - - /* - * We received a nak while trying to forward on your behalf - */ - GOT_NAK = 2; - - /* - * TODO: REPLACE - */ - TIMEOUT = 3; - - /* - * No suitable interface could be found for delivering this packet - */ - NO_INTERFACE = 4; - - /* - * We reached the max retransmission count (typically for naive flood routing) - */ - MAX_RETRANSMIT = 5; - - /* - * No suitable channel was found for sending this packet (i.e. was requested channel index disabled?) - */ - NO_CHANNEL = 6; - - /* - * The packet was too big for sending (exceeds interface MTU after encoding) - */ - TOO_LARGE = 7; - - /* - * The request had want_response set, the request reached the destination node, but no service on that node wants to send a response - * (possibly due to bad channel permissions) - */ - NO_RESPONSE = 8; - - /* - * Cannot send currently because duty cycle regulations will be violated. - */ - DUTY_CYCLE_LIMIT = 9; - - /* - * The application layer service on the remote node received your request, but considered your request somehow invalid - */ - BAD_REQUEST = 32; - - /* - * The application layer service on the remote node received your request, but considered your request not authorized - * (i.e you did not send the request on the required bound channel) - */ - NOT_AUTHORIZED = 33; - - /* - * The client specified a PKI transport, but the node was unable to send the packet using PKI (and did not send the message at all) - */ - PKI_FAILED = 34; - - /* - * The receiving node does not have a Public Key to decode with - */ - PKI_UNKNOWN_PUBKEY = 35; - } - - oneof variant { - /* - * A route request going from the requester - */ - RouteDiscovery route_request = 1; - - /* - * A route reply - */ - RouteDiscovery route_reply = 2; - - /* - * A failure in delivering a message (usually used for routing control messages, but might be provided - * in addition to ack.fail_id to provide details on the type of failure). - */ - Error error_reason = 3; - } -} - -/* - * (Formerly called SubPacket) - * The payload portion fo a packet, this is the actual bytes that are sent - * inside a radio packet (because from/to are broken out by the comms library) - */ -message Data { - /* - * Formerly named typ and of type Type - */ - PortNum portnum = 1; - - /* - * TODO: REPLACE - */ - bytes payload = 2; - - /* - * Not normally used, but for testing a sender can request that recipient - * responds in kind (i.e. if it received a position, it should unicast back it's position). - * Note: that if you set this on a broadcast you will receive many replies. - */ - bool want_response = 3; - - /* - * The address of the destination node. - * This field is is filled in by the mesh radio device software, application - * layer software should never need it. - * RouteDiscovery messages _must_ populate this. - * Other message types might need to if they are doing multihop routing. - */ - fixed32 dest = 4; - - /* - * The address of the original sender for this message. - * This field should _only_ be populated for reliable multihop packets (to keep - * packets small). - */ - fixed32 source = 5; - - /* - * Only used in routing or response messages. - * Indicates the original message ID that this message is reporting failure on. (formerly called original_id) - */ - fixed32 request_id = 6; - - /* - * If set, this message is intened to be a reply to a previously sent message with the defined id. - */ - fixed32 reply_id = 7; - - /* - * Defaults to false. If true, then what is in the payload should be treated as an emoji like giving - * a message a heart or poop emoji. - */ - fixed32 emoji = 8; -} - -/* - * Waypoint message, used to share arbitrary locations across the mesh - */ -message Waypoint { - /* - * Id of the waypoint - */ - uint32 id = 1; - - /* - * latitude_i - */ - optional sfixed32 latitude_i = 2; - - /* - * longitude_i - */ - optional sfixed32 longitude_i = 3; - - /* - * Time the waypoint is to expire (epoch) - */ - uint32 expire = 4; - - /* - * If greater than zero, treat the value as a nodenum only allowing them to update the waypoint. - * If zero, the waypoint is open to be edited by any member of the mesh. - */ - uint32 locked_to = 5; - - /* - * Name of the waypoint - max 30 chars - */ - string name = 6; - - /* - * Description of the waypoint - max 100 chars - */ - string description = 7; - - /* - * Designator icon for the waypoint in the form of a unicode emoji - */ - fixed32 icon = 8; -} - -/* - * This message will be proxied over the PhoneAPI for the client to deliver to the MQTT server - */ -message MqttClientProxyMessage { - /* - * The MQTT topic this message will be sent /received on - */ - string topic = 1; - - /* - * The actual service envelope payload or text for mqtt pub / sub - */ - oneof payload_variant { - /* - * Bytes - */ - bytes data = 2; - - /* - * Text - */ - string text = 3; - } - - /* - * Whether the message should be retained (or not) - */ - bool retained = 4; -} - -/* - * A packet envelope sent/received over the mesh - * only payload_variant is sent in the payload portion of the LORA packet. - * The other fields are either not sent at all, or sent in the special 16 byte LORA header. - */ -message MeshPacket { - /* - * The priority of this message for sending. - * Higher priorities are sent first (when managing the transmit queue). - * This field is never sent over the air, it is only used internally inside of a local device node. - * API clients (either on the local node or connected directly to the node) - * can set this parameter if necessary. - * (values must be <= 127 to keep protobuf field to one byte in size. - * Detailed background on this field: - * I noticed a funny side effect of lora being so slow: Usually when making - * a protocol there isn’t much need to use message priority to change the order - * of transmission (because interfaces are fairly fast). - * But for lora where packets can take a few seconds each, it is very important - * to make sure that critical packets are sent ASAP. - * In the case of meshtastic that means we want to send protocol acks as soon as possible - * (to prevent unneeded retransmissions), we want routing messages to be sent next, - * then messages marked as reliable and finally 'background' packets like periodic position updates. - * So I bit the bullet and implemented a new (internal - not sent over the air) - * field in MeshPacket called 'priority'. - * And the transmission queue in the router object is now a priority queue. - */ - enum Priority { - /* - * Treated as Priority.DEFAULT - */ - UNSET = 0; - - /* - * TODO: REPLACE - */ - MIN = 1; - - /* - * Background position updates are sent with very low priority - - * if the link is super congested they might not go out at all - */ - BACKGROUND = 10; - - /* - * This priority is used for most messages that don't have a priority set - */ - DEFAULT = 64; - - /* - * If priority is unset but the message is marked as want_ack, - * assume it is important and use a slightly higher priority - */ - RELIABLE = 70; - - /* - * Ack/naks are sent with very high priority to ensure that retransmission - * stops as soon as possible - */ - ACK = 120; - - /* - * TODO: REPLACE - */ - MAX = 127; - } - - /* - * Identify if this is a delayed packet - */ - enum Delayed { - /* - * If unset, the message is being sent in real time. - */ - NO_DELAY = 0; - - /* - * The message is delayed and was originally a broadcast - */ - DELAYED_BROADCAST = 1; - - /* - * The message is delayed and was originally a direct message - */ - DELAYED_DIRECT = 2; - } - - /* - * The sending node number. - * Note: Our crypto implementation uses this field as well. - * See [crypto](/docs/overview/encryption) for details. - */ - fixed32 from = 1; - - /* - * The (immediate) destination for this packet - */ - fixed32 to = 2; - - /* - * (Usually) If set, this indicates the index in the secondary_channels table that this packet was sent/received on. - * If unset, packet was on the primary channel. - * A particular node might know only a subset of channels in use on the mesh. - * Therefore channel_index is inherently a local concept and meaningless to send between nodes. - * Very briefly, while sending and receiving deep inside the device Router code, this field instead - * contains the 'channel hash' instead of the index. - * This 'trick' is only used while the payload_variant is an 'encrypted'. - */ - uint32 channel = 3; - - /* - * Internally to the mesh radios we will route SubPackets encrypted per [this](docs/developers/firmware/encryption). - * However, when a particular node has the correct - * key to decode a particular packet, it will decode the payload into a SubPacket protobuf structure. - * Software outside of the device nodes will never encounter a packet where - * "decoded" is not populated (i.e. any encryption/decryption happens before reaching the applications) - * The numeric IDs for these fields were selected to keep backwards compatibility with old applications. - */ - - oneof payload_variant { - /* - * TODO: REPLACE - */ - Data decoded = 4; - - /* - * TODO: REPLACE - */ - bytes encrypted = 5; - } - - /* - * A unique ID for this packet. - * Always 0 for no-ack packets or non broadcast packets (and therefore take zero bytes of space). - * Otherwise a unique ID for this packet, useful for flooding algorithms. - * ID only needs to be unique on a _per sender_ basis, and it only - * needs to be unique for a few minutes (long enough to last for the length of - * any ACK or the completion of a mesh broadcast flood). - * Note: Our crypto implementation uses this id as well. - * See [crypto](/docs/overview/encryption) for details. - */ - fixed32 id = 6; - - /* - * The time this message was received by the esp32 (secs since 1970). - * Note: this field is _never_ sent on the radio link itself (to save space) Times - * are typically not sent over the mesh, but they will be added to any Packet - * (chain of SubPacket) sent to the phone (so the phone can know exact time of reception) - */ - fixed32 rx_time = 7; - - /* - * *Never* sent over the radio links. - * Set during reception to indicate the SNR of this packet. - * Used to collect statistics on current link quality. - */ - float rx_snr = 8; - - /* - * If unset treated as zero (no forwarding, send to adjacent nodes only) - * if 1, allow hopping through one node, etc... - * For our usecase real world topologies probably have a max of about 3. - * This field is normally placed into a few of bits in the header. - */ - uint32 hop_limit = 9; - - /* - * This packet is being sent as a reliable message, we would prefer it to arrive at the destination. - * We would like to receive a ack packet in response. - * Broadcasts messages treat this flag specially: Since acks for broadcasts would - * rapidly flood the channel, the normal ack behavior is suppressed. - * Instead, the original sender listens to see if at least one node is rebroadcasting this packet (because naive flooding algorithm). - * If it hears that the odds (given typical LoRa topologies) the odds are very high that every node should eventually receive the message. - * So FloodingRouter.cpp generates an implicit ack which is delivered to the original sender. - * If after some time we don't hear anyone rebroadcast our packet, we will timeout and retransmit, using the regular resend logic. - * Note: This flag is normally sent in a flag bit in the header when sent over the wire - */ - bool want_ack = 10; - - /* - * The priority of this message for sending. - * See MeshPacket.Priority description for more details. - */ - Priority priority = 11; - - /* - * rssi of received packet. Only sent to phone for dispay purposes. - */ - int32 rx_rssi = 12; - - /* - * Describe if this message is delayed - */ - Delayed delayed = 13 [deprecated = true]; - - /* - * Describes whether this packet passed via MQTT somewhere along the path it currently took. - */ - bool via_mqtt = 14; - - /* - * Hop limit with which the original packet started. Sent via LoRa using three bits in the unencrypted header. - * When receiving a packet, the difference between hop_start and hop_limit gives how many hops it traveled. - */ - uint32 hop_start = 15; - - /* - * Records the public key the packet was encrypted with, if applicable. - */ - bytes public_key = 16; - - /* - * Indicates whether the packet was en/decrypted using PKI - */ - bool pki_encrypted = 17; -} - -/* - * Shared constants between device and phone - */ -enum Constants { - /* - * First enum must be zero, and we are just using this enum to - * pass int constants between two very different environments - */ - ZERO = 0; - - /* - * From mesh.options - * note: this payload length is ONLY the bytes that are sent inside of the Data protobuf (excluding protobuf overhead). The 16 byte header is - * outside of this envelope - */ - DATA_PAYLOAD_LEN = 237; -} - -/* - * The bluetooth to device link: - * Old BTLE protocol docs from TODO, merge in above and make real docs... - * use protocol buffers, and NanoPB - * messages from device to phone: - * POSITION_UPDATE (..., time) - * TEXT_RECEIVED(from, text, time) - * OPAQUE_RECEIVED(from, payload, time) (for signal messages or other applications) - * messages from phone to device: - * SET_MYID(id, human readable long, human readable short) (send down the unique ID - * string used for this node, a human readable string shown for that id, and a very - * short human readable string suitable for oled screen) SEND_OPAQUE(dest, payload) - * (for signal messages or other applications) SEND_TEXT(dest, text) Get all - * nodes() (returns list of nodes, with full info, last time seen, loc, battery - * level etc) SET_CONFIG (switches device to a new set of radio params and - * preshared key, drops all existing nodes, force our node to rejoin this new group) - * Full information about a node on the mesh - */ -message NodeInfo { - /* - * The node number - */ - uint32 num = 1; - - /* - * The user info for this node - */ - User user = 2; - - /* - * This position data. Note: before 1.2.14 we would also store the last time we've heard from this node in position.time, that is no longer true. - * Position.time now indicates the last time we received a POSITION from that node. - */ - Position position = 3; - - /* - * Returns the Signal-to-noise ratio (SNR) of the last received message, - * as measured by the receiver. Return SNR of the last received message in dB - */ - float snr = 4; - - /* - * TODO: REMOVE/INTEGRATE - * Returns the last measured frequency error. - * The LoRa receiver estimates the frequency offset between the receiver - * center frequency and that of the received LoRa signal. This function - * returns the estimates offset (in Hz) of the last received message. - * Caution: this measurement is not absolute, but is measured relative to the - * local receiver's oscillator. Apparent errors may be due to the - * transmitter, the receiver or both. \return The estimated center frequency - * offset in Hz of the last received message. - * int32 frequency_error = 6; - * enum RouteState { - * Invalid = 0; - * Discovering = 1; - * Valid = 2; - * } - * Not needed? - * RouteState route = 4; - */ - - /* - * TODO: REMOVE/INTEGRATE - * Not currently used (till full DSR deployment?) Our current preferred node node for routing - might be the same as num if - * we are adjacent Or zero if we don't yet know a route to this node. - * fixed32 next_hop = 5; - */ - - /* - * Set to indicate the last time we received a packet from this node - */ - fixed32 last_heard = 5; - /* - * The latest device metrics for the node. - */ - DeviceMetrics device_metrics = 6; - - /* - * local channel index we heard that node on. Only populated if its not the default channel. - */ - uint32 channel = 7; - - /* - * True if we witnessed the node over MQTT instead of LoRA transport - */ - bool via_mqtt = 8; - - /* - * Number of hops away from us this node is (0 if adjacent) - */ - uint32 hops_away = 9; - - /* - * True if node is in our favorites list - * Persists between NodeDB internal clean ups - */ - bool is_favorite = 10; -} - -/* - * Error codes for critical errors - * The device might report these fault codes on the screen. - * If you encounter a fault code, please post on the meshtastic.discourse.group - * and we'll try to help. - */ -enum CriticalErrorCode { - /* - * TODO: REPLACE - */ - NONE = 0; - - /* - * A software bug was detected while trying to send lora - */ - TX_WATCHDOG = 1; - - /* - * A software bug was detected on entry to sleep - */ - SLEEP_ENTER_WAIT = 2; - - /* - * No Lora radio hardware could be found - */ - NO_RADIO = 3; - - /* - * Not normally used - */ - UNSPECIFIED = 4; - - /* - * We failed while configuring a UBlox GPS - */ - UBLOX_UNIT_FAILED = 5; - - /* - * This board was expected to have a power management chip and it is missing or broken - */ - NO_AXP192 = 6; - - /* - * The channel tried to set a radio setting which is not supported by this chipset, - * radio comms settings are now undefined. - */ - INVALID_RADIO_SETTING = 7; - - /* - * Radio transmit hardware failure. We sent data to the radio chip, but it didn't - * reply with an interrupt. - */ - TRANSMIT_FAILED = 8; - - /* - * We detected that the main CPU voltage dropped below the minimum acceptable value - */ - BROWNOUT = 9; - - /* Selftest of SX1262 radio chip failed */ - SX1262_FAILURE = 10; - - /* - * A (likely software but possibly hardware) failure was detected while trying to send packets. - * If this occurs on your board, please post in the forum so that we can ask you to collect some information to allow fixing this bug - */ - RADIO_SPI_BUG = 11; - - /* - * Corruption was detected on the flash filesystem but we were able to repair things. - * If you see this failure in the field please post in the forum because we are interested in seeing if this is occurring in the field. - */ - FLASH_CORRUPTION_RECOVERABLE = 12; - - /* - * Corruption was detected on the flash filesystem but we were unable to repair things. - * NOTE: Your node will probably need to be reconfigured the next time it reboots (it will lose the region code etc...) - * If you see this failure in the field please post in the forum because we are interested in seeing if this is occurring in the field. - */ - FLASH_CORRUPTION_UNRECOVERABLE = 13; -} - -/* - * Unique local debugging info for this node - * Note: we don't include position or the user info, because that will come in the - * Sent to the phone in response to WantNodes. - */ -message MyNodeInfo { - /* - * Tells the phone what our node number is, default starting value is - * lowbyte of macaddr, but it will be fixed if that is already in use - */ - uint32 my_node_num = 1; - - /* - * The total number of reboots this node has ever encountered - * (well - since the last time we discarded preferences) - */ - uint32 reboot_count = 8; - - /* - * The minimum app version that can talk to this device. - * Phone/PC apps should compare this to their build number and if too low tell the user they must update their app - */ - uint32 min_app_version = 11; -} - -/* - * Debug output from the device. - * To minimize the size of records inside the device code, if a time/source/level is not set - * on the message it is assumed to be a continuation of the previously sent message. - * This allows the device code to use fixed maxlen 64 byte strings for messages, - * and then extend as needed by emitting multiple records. - */ -message LogRecord { - /* - * Log levels, chosen to match python logging conventions. - */ - enum Level { - /* - * Log levels, chosen to match python logging conventions. - */ - UNSET = 0; - - /* - * Log levels, chosen to match python logging conventions. - */ - CRITICAL = 50; - - /* - * Log levels, chosen to match python logging conventions. - */ - ERROR = 40; - - /* - * Log levels, chosen to match python logging conventions. - */ - WARNING = 30; - - /* - * Log levels, chosen to match python logging conventions. - */ - INFO = 20; - - /* - * Log levels, chosen to match python logging conventions. - */ - DEBUG = 10; - - /* - * Log levels, chosen to match python logging conventions. - */ - TRACE = 5; - } - - /* - * Log levels, chosen to match python logging conventions. - */ - string message = 1; - - /* - * Seconds since 1970 - or 0 for unknown/unset - */ - fixed32 time = 2; - - /* - * Usually based on thread name - if known - */ - string source = 3; - - /* - * Not yet set - */ - Level level = 4; -} - -message QueueStatus { - /* Last attempt to queue status, ErrorCode */ - int32 res = 1; - - /* Free entries in the outgoing queue */ - uint32 free = 2; - - /* Maximum entries in the outgoing queue */ - uint32 maxlen = 3; - - /* What was mesh packet id that generated this response? */ - uint32 mesh_packet_id = 4; -} - -/* - * Packets from the radio to the phone will appear on the fromRadio characteristic. - * It will support READ and NOTIFY. When a new packet arrives the device will BLE notify? - * It will sit in that descriptor until consumed by the phone, - * at which point the next item in the FIFO will be populated. - */ -message FromRadio { - /* - * The packet id, used to allow the phone to request missing read packets from the FIFO, - * see our bluetooth docs - */ - uint32 id = 1; - - /* - * Log levels, chosen to match python logging conventions. - */ - oneof payload_variant { - /* - * Log levels, chosen to match python logging conventions. - */ - MeshPacket packet = 2; - - /* - * Tells the phone what our node number is, can be -1 if we've not yet joined a mesh. - * NOTE: This ID must not change - to keep (minimal) compatibility with <1.2 version of android apps. - */ - MyNodeInfo my_info = 3; - - /* - * One packet is sent for each node in the on radio DB - * starts over with the first node in our DB - */ - NodeInfo node_info = 4; - - /* - * Include a part of the config (was: RadioConfig radio) - */ - Config config = 5; - - /* - * Set to send debug console output over our protobuf stream - */ - LogRecord log_record = 6; - - /* - * Sent as true once the device has finished sending all of the responses to want_config - * recipient should check if this ID matches our original request nonce, if - * not, it means your config responses haven't started yet. - * NOTE: This ID must not change - to keep (minimal) compatibility with <1.2 version of android apps. - */ - uint32 config_complete_id = 7; - - /* - * Sent to tell clients the radio has just rebooted. - * Set to true if present. - * Not used on all transports, currently just used for the serial console. - * NOTE: This ID must not change - to keep (minimal) compatibility with <1.2 version of android apps. - */ - bool rebooted = 8; - - /* - * Include module config - */ - ModuleConfig moduleConfig = 9; - - /* - * One packet is sent for each channel - */ - Channel channel = 10; - - /* - * Queue status info - */ - QueueStatus queueStatus = 11; - - /* - * File Transfer Chunk - */ - XModem xmodemPacket = 12; - - /* - * Device metadata message - */ - DeviceMetadata metadata = 13; - - /* - * MQTT Client Proxy Message (device sending to client / phone for publishing to MQTT) - */ - MqttClientProxyMessage mqttClientProxyMessage = 14; - - /* - * File system manifest messages - */ - FileInfo fileInfo = 15; - - /* - * Notification message to the client - */ - ClientNotification clientNotification = 16; - } -} - -/* - * A notification message from the device to the client - * To be used for important messages that should to be displayed to the user - * in the form of push notifications or validation messages when saving - * invalid configuration. - */ -message ClientNotification { - /* - * The id of the packet we're notifying in response to - */ - optional uint32 reply_id = 1; - - /* - * Seconds since 1970 - or 0 for unknown/unset - */ - fixed32 time = 2; - - /* - * The level type of notification - */ - LogRecord.Level level = 3; - /* - * The message body of the notification - */ - string message = 4; -} - -/* - * Individual File info for the device - */ -message FileInfo { - /* - * The fully qualified path of the file - */ - string file_name = 1; - - /* - * The size of the file in bytes - */ - uint32 size_bytes = 2; -} - -/* - * Packets/commands to the radio will be written (reliably) to the toRadio characteristic. - * Once the write completes the phone can assume it is handled. - */ -message ToRadio { - /* - * Log levels, chosen to match python logging conventions. - */ - oneof payload_variant { - /* - * Send this packet on the mesh - */ - MeshPacket packet = 1; - - /* - * Phone wants radio to send full node db to the phone, This is - * typically the first packet sent to the radio when the phone gets a - * bluetooth connection. The radio will respond by sending back a - * MyNodeInfo, a owner, a radio config and a series of - * FromRadio.node_infos, and config_complete - * the integer you write into this field will be reported back in the - * config_complete_id response this allows clients to never be confused by - * a stale old partially sent config. - */ - uint32 want_config_id = 3; - - /* - * Tell API server we are disconnecting now. - * This is useful for serial links where there is no hardware/protocol based notification that the client has dropped the link. - * (Sending this message is optional for clients) - */ - bool disconnect = 4; - - /* - * File Transfer Chunk - */ - - XModem xmodemPacket = 5; - - /* - * MQTT Client Proxy Message (for client / phone subscribed to MQTT sending to device) - */ - MqttClientProxyMessage mqttClientProxyMessage = 6; - - /* - * Heartbeat message (used to keep the device connection awake on serial) - */ - Heartbeat heartbeat = 7; - } -} - -/* - * Compressed message payload - */ -message Compressed { - /* - * PortNum to determine the how to handle the compressed payload. - */ - PortNum portnum = 1; - - /* - * Compressed data. - */ - bytes data = 2; -} - -/* - * Full info on edges for a single node - */ -message NeighborInfo { - /* - * The node ID of the node sending info on its neighbors - */ - uint32 node_id = 1; - /* - * Field to pass neighbor info for the next sending cycle - */ - uint32 last_sent_by_id = 2; - - /* - * Broadcast interval of the represented node (in seconds) - */ - uint32 node_broadcast_interval_secs = 3; - /* - * The list of out edges from this node - */ - repeated Neighbor neighbors = 4; -} - -/* - * A single edge in the mesh - */ -message Neighbor { - /* - * Node ID of neighbor - */ - uint32 node_id = 1; - - /* - * SNR of last heard message - */ - float snr = 2; - - /* - * Reception time (in secs since 1970) of last message that was last sent by this ID. - * Note: this is for local storage only and will not be sent out over the mesh. - */ - fixed32 last_rx_time = 3; - - /* - * Broadcast interval of this neighbor (in seconds). - * Note: this is for local storage only and will not be sent out over the mesh. - */ - uint32 node_broadcast_interval_secs = 4; -} - -/* - * Device metadata response - */ -message DeviceMetadata { - /* - * Device firmware version string - */ - string firmware_version = 1; - - /* - * Device state version - */ - uint32 device_state_version = 2; - - /* - * Indicates whether the device can shutdown CPU natively or via power management chip - */ - bool canShutdown = 3; - - /* - * Indicates that the device has native wifi capability - */ - bool hasWifi = 4; - - /* - * Indicates that the device has native bluetooth capability - */ - bool hasBluetooth = 5; - - /* - * Indicates that the device has an ethernet peripheral - */ - bool hasEthernet = 6; - - /* - * Indicates that the device's role in the mesh - */ - Config.DeviceConfig.Role role = 7; - - /* - * Indicates the device's current enabled position flags - */ - uint32 position_flags = 8; - - /* - * Device hardware model - */ - HardwareModel hw_model = 9; - - /* - * Has Remote Hardware enabled - */ - bool hasRemoteHardware = 10; -} - -/* - * A heartbeat message is sent to the node from the client to keep the connection alive. - * This is currently only needed to keep serial connections alive, but can be used by any PhoneAPI. - */ -message Heartbeat {} - -/* - * RemoteHardwarePins associated with a node - */ -message NodeRemoteHardwarePin { - /* - * The node_num exposing the available gpio pin - */ - uint32 node_num = 1; - - /* - * The the available gpio pin for usage with RemoteHardware module - */ - RemoteHardwarePin pin = 2; -} - -message ChunkedPayload { - /* - * The ID of the entire payload - */ - uint32 payload_id = 1; - - /* - * The total number of chunks in the payload - */ - uint32 chunk_count = 2; - - /* - * The current chunk index in the total - */ - uint32 chunk_index = 3; - - /* - * The binary data of the current chunk - */ - bytes payload_chunk = 4; -} - -/* - * Wrapper message for broken repeated oneof support - */ -message resend_chunks { - repeated uint32 chunks = 1; -} - -/* - * Responses to a ChunkedPayload request - */ -message ChunkedPayloadResponse { - /* - * The ID of the entire payload - */ - uint32 payload_id = 1; - - oneof payload_variant { - /* - * Request to transfer chunked payload - */ - bool request_transfer = 2; - - /* - * Accept the transfer chunked payload - */ - bool accept_transfer = 3; - /* - * Request missing indexes in the chunked payload - */ - resend_chunks resend_chunks = 4; - } -} \ No newline at end of file diff --git a/proto_def/meshtastic/module_config.options b/proto_def/meshtastic/module_config.options deleted file mode 100644 index 68aba7c1..00000000 --- a/proto_def/meshtastic/module_config.options +++ /dev/null @@ -1,28 +0,0 @@ -*CannedMessageConfig.allow_input_source max_size:16 - -*MQTTConfig.address max_size:64 -*MQTTConfig.username max_size:64 -*MQTTConfig.password max_size:64 -*MQTTConfig.root max_size:32 - -*AudioConfig.ptt_pin int_size:8 -*AudioConfig.i2s_ws int_size:8 -*AudioConfig.i2s_sd int_size:8 -*AudioConfig.i2s_din int_size:8 -*AudioConfig.i2s_sck int_size:8 - -*ExternalNotificationConfig.output_vibra int_size:8 -*ExternalNotificationConfig.output_buzzer int_size:8 -*ExternalNotificationConfig.nag_timeout int_size:16 - -*RemoteHardwareConfig.available_pins max_count:4 -*RemoteHardwarePin.name max_size:15 -*RemoteHardwarePin.gpio_pin int_size:8 - -*AmbientLightingConfig.current int_size:8 -*AmbientLightingConfig.red int_size:8 -*AmbientLightingConfig.green int_size:8 -*AmbientLightingConfig.blue int_size:8 - -*DetectionSensorConfig.monitor_pin int_size:8 -*DetectionSensorConfig.name max_size:20 diff --git a/proto_def/meshtastic/module_config.proto b/proto_def/meshtastic/module_config.proto deleted file mode 100644 index 9945e1b9..00000000 --- a/proto_def/meshtastic/module_config.proto +++ /dev/null @@ -1,810 +0,0 @@ -syntax = "proto3"; - -package meshtastic; - -option csharp_namespace = "Meshtastic.Protobufs"; -option go_package = "github.com/meshtastic/go/generated"; -option java_outer_classname = "ModuleConfigProtos"; -option java_package = "com.geeksville.mesh"; -option swift_prefix = ""; - -/* - * Module Config - */ -message ModuleConfig { - /* - * MQTT Client Config - */ - message MQTTConfig { - /* - * If a meshtastic node is able to reach the internet it will normally attempt to gateway any channels that are marked as - * is_uplink_enabled or is_downlink_enabled. - */ - bool enabled = 1; - - /* - * The server to use for our MQTT global message gateway feature. - * If not set, the default server will be used - */ - string address = 2; - - /* - * MQTT username to use (most useful for a custom MQTT server). - * If using a custom server, this will be honoured even if empty. - * If using the default server, this will only be honoured if set, otherwise the device will use the default username - */ - string username = 3; - - /* - * MQTT password to use (most useful for a custom MQTT server). - * If using a custom server, this will be honoured even if empty. - * If using the default server, this will only be honoured if set, otherwise the device will use the default password - */ - string password = 4; - - /* - * Whether to send encrypted or decrypted packets to MQTT. - * This parameter is only honoured if you also set server - * (the default official mqtt.meshtastic.org server can handle encrypted packets) - * Decrypted packets may be useful for external systems that want to consume meshtastic packets - */ - bool encryption_enabled = 5; - - /* - * Whether to send / consume json packets on MQTT - */ - bool json_enabled = 6; - - /* - * If true, we attempt to establish a secure connection using TLS - */ - bool tls_enabled = 7; - - /* - * The root topic to use for MQTT messages. Default is "msh". - * This is useful if you want to use a single MQTT server for multiple meshtastic networks and separate them via ACLs - */ - string root = 8; - - /* - * If true, we can use the connected phone / client to proxy messages to MQTT instead of a direct connection - */ - bool proxy_to_client_enabled = 9; - - /* - * If true, we will periodically report unencrypted information about our node to a map via MQTT - */ - bool map_reporting_enabled = 10; - - /* - * Settings for reporting information about our node to a map via MQTT - */ - MapReportSettings map_report_settings = 11; - } - - /* - * Settings for reporting unencrypted information about our node to a map via MQTT - */ - message MapReportSettings { - /* - * How often we should report our info to the map (in seconds) - */ - uint32 publish_interval_secs = 1; - - /* - * Bits of precision for the location sent (default of 32 is full precision). - */ - uint32 position_precision = 2; - } - - /* - * RemoteHardwareModule Config - */ - message RemoteHardwareConfig { - /* - * Whether the Module is enabled - */ - bool enabled = 1; - - /* - * Whether the Module allows consumers to read / write to pins not defined in available_pins - */ - bool allow_undefined_pin_access = 2; - - /* - * Exposes the available pins to the mesh for reading and writing - */ - repeated RemoteHardwarePin available_pins = 3; - } - - /* - * NeighborInfoModule Config - */ - message NeighborInfoConfig { - /* - * Whether the Module is enabled - */ - bool enabled = 1; - - /* - * Interval in seconds of how often we should try to send our - * Neighbor Info to the mesh - */ - uint32 update_interval = 2; - } - - /* - * Detection Sensor Module Config - */ - message DetectionSensorConfig { - /* - * Whether the Module is enabled - */ - bool enabled = 1; - - /* - * Interval in seconds of how often we can send a message to the mesh when a state change is detected - */ - uint32 minimum_broadcast_secs = 2; - - /* - * Interval in seconds of how often we should send a message to the mesh with the current state regardless of changes - * When set to 0, only state changes will be broadcasted - * Works as a sort of status heartbeat for peace of mind - */ - uint32 state_broadcast_secs = 3; - - /* - * Send ASCII bell with alert message - * Useful for triggering ext. notification on bell - */ - bool send_bell = 4; - - /* - * Friendly name used to format message sent to mesh - * Example: A name "Motion" would result in a message "Motion detected" - * Maximum length of 20 characters - */ - string name = 5; - - /* - * GPIO pin to monitor for state changes - */ - uint32 monitor_pin = 6; - - /* - * Whether or not the GPIO pin state detection is triggered on HIGH (1) - * Otherwise LOW (0) - */ - bool detection_triggered_high = 7; - - /* - * Whether or not use INPUT_PULLUP mode for GPIO pin - * Only applicable if the board uses pull-up resistors on the pin - */ - bool use_pullup = 8; - } - - /* - * Audio Config for codec2 voice - */ - message AudioConfig { - /* - * Baudrate for codec2 voice - */ - enum Audio_Baud { - CODEC2_DEFAULT = 0; - CODEC2_3200 = 1; - CODEC2_2400 = 2; - CODEC2_1600 = 3; - CODEC2_1400 = 4; - CODEC2_1300 = 5; - CODEC2_1200 = 6; - CODEC2_700 = 7; - CODEC2_700B = 8; - } - - /* - * Whether Audio is enabled - */ - bool codec2_enabled = 1; - - /* - * PTT Pin - */ - uint32 ptt_pin = 2; - - /* - * The audio sample rate to use for codec2 - */ - Audio_Baud bitrate = 3; - - /* - * I2S Word Select - */ - uint32 i2s_ws = 4; - - /* - * I2S Data IN - */ - uint32 i2s_sd = 5; - - /* - * I2S Data OUT - */ - uint32 i2s_din = 6; - - /* - * I2S Clock - */ - uint32 i2s_sck = 7; - } - - /* - * Config for the Paxcounter Module - */ - message PaxcounterConfig { - /* - * Enable the Paxcounter Module - */ - bool enabled = 1; - - /* - * Interval in seconds of how often we should try to send our - * metrics to the mesh - */ - - uint32 paxcounter_update_interval = 2; - - /* - * WiFi RSSI threshold. Defaults to -80 - */ - int32 wifi_threshold = 3; - - /* - * BLE RSSI threshold. Defaults to -80 - */ - int32 ble_threshold = 4; - - } - - /* - * Serial Config - */ - message SerialConfig { - /* - * TODO: REPLACE - */ - enum Serial_Baud { - BAUD_DEFAULT = 0; - BAUD_110 = 1; - BAUD_300 = 2; - BAUD_600 = 3; - BAUD_1200 = 4; - BAUD_2400 = 5; - BAUD_4800 = 6; - BAUD_9600 = 7; - BAUD_19200 = 8; - BAUD_38400 = 9; - BAUD_57600 = 10; - BAUD_115200 = 11; - BAUD_230400 = 12; - BAUD_460800 = 13; - BAUD_576000 = 14; - BAUD_921600 = 15; - } - - /* - * TODO: REPLACE - */ - enum Serial_Mode { - DEFAULT = 0; - SIMPLE = 1; - PROTO = 2; - TEXTMSG = 3; - NMEA = 4; - // NMEA messages specifically tailored for CalTopo - CALTOPO = 5; - // Ecowitt WS85 weather station - WS85 = 6; - } - - /* - * Preferences for the SerialModule - */ - bool enabled = 1; - - /* - * TODO: REPLACE - */ - bool echo = 2; - - /* - * RX pin (should match Arduino gpio pin number) - */ - uint32 rxd = 3; - - /* - * TX pin (should match Arduino gpio pin number) - */ - uint32 txd = 4; - - /* - * Serial baud rate - */ - Serial_Baud baud = 5; - - /* - * TODO: REPLACE - */ - uint32 timeout = 6; - - /* - * Mode for serial module operation - */ - Serial_Mode mode = 7; - - /* - * Overrides the platform's defacto Serial port instance to use with Serial module config settings - * This is currently only usable in output modes like NMEA / CalTopo and may behave strangely or not work at all in other modes - * Existing logging over the Serial Console will still be present - */ - bool override_console_serial_port = 8; - } - - /* - * External Notifications Config - */ - message ExternalNotificationConfig { - /* - * Enable the ExternalNotificationModule - */ - bool enabled = 1; - - /* - * When using in On/Off mode, keep the output on for this many - * milliseconds. Default 1000ms (1 second). - */ - uint32 output_ms = 2; - - /* - * Define the output pin GPIO setting Defaults to - * EXT_NOTIFY_OUT if set for the board. - * In standalone devices this pin should drive the LED to match the UI. - */ - uint32 output = 3; - - /* - * Optional: Define a secondary output pin for a vibra motor - * This is used in standalone devices to match the UI. - */ - uint32 output_vibra = 8; - - /* - * Optional: Define a tertiary output pin for an active buzzer - * This is used in standalone devices to to match the UI. - */ - uint32 output_buzzer = 9; - - /* - * IF this is true, the 'output' Pin will be pulled active high, false - * means active low. - */ - bool active = 4; - - /* - * True: Alert when a text message arrives (output) - */ - bool alert_message = 5; - - /* - * True: Alert when a text message arrives (output_vibra) - */ - bool alert_message_vibra = 10; - - /* - * True: Alert when a text message arrives (output_buzzer) - */ - bool alert_message_buzzer = 11; - - /* - * True: Alert when the bell character is received (output) - */ - bool alert_bell = 6; - - /* - * True: Alert when the bell character is received (output_vibra) - */ - bool alert_bell_vibra = 12; - - /* - * True: Alert when the bell character is received (output_buzzer) - */ - bool alert_bell_buzzer = 13; - - /* - * use a PWM output instead of a simple on/off output. This will ignore - * the 'output', 'output_ms' and 'active' settings and use the - * device.buzzer_gpio instead. - */ - bool use_pwm = 7; - - /* - * The notification will toggle with 'output_ms' for this time of seconds. - * Default is 0 which means don't repeat at all. 60 would mean blink - * and/or beep for 60 seconds - */ - uint32 nag_timeout = 14; - - /* - * When true, enables devices with native I2S audio output to use the RTTTL over speaker like a buzzer - * T-Watch S3 and T-Deck for example have this capability - */ - bool use_i2s_as_buzzer = 15; - } - - /* - * Store and Forward Module Config - */ - message StoreForwardConfig { - /* - * Enable the Store and Forward Module - */ - bool enabled = 1; - - /* - * TODO: REPLACE - */ - bool heartbeat = 2; - - /* - * TODO: REPLACE - */ - uint32 records = 3; - - /* - * TODO: REPLACE - */ - uint32 history_return_max = 4; - - /* - * TODO: REPLACE - */ - uint32 history_return_window = 5; - - /* - * Set to true to let this node act as a server that stores received messages and resends them upon request. - */ - bool is_server = 6; - } - - /* - * Preferences for the RangeTestModule - */ - message RangeTestConfig { - /* - * Enable the Range Test Module - */ - bool enabled = 1; - - /* - * Send out range test messages from this node - */ - uint32 sender = 2; - - /* - * Bool value indicating that this node should save a RangeTest.csv file. - * ESP32 Only - */ - bool save = 3; - } - - /* - * Configuration for both device and environment metrics - */ - message TelemetryConfig { - /* - * Interval in seconds of how often we should try to send our - * device metrics to the mesh - */ - uint32 device_update_interval = 1; - - /* - * Interval in seconds of how often we should try to send our - * environment measurements to the mesh - */ - - uint32 environment_update_interval = 2; - - /* - * Preferences for the Telemetry Module (Environment) - * Enable/Disable the telemetry measurement module measurement collection - */ - bool environment_measurement_enabled = 3; - - /* - * Enable/Disable the telemetry measurement module on-device display - */ - bool environment_screen_enabled = 4; - - /* - * We'll always read the sensor in Celsius, but sometimes we might want to - * display the results in Fahrenheit as a "user preference". - */ - bool environment_display_fahrenheit = 5; - - /* - * Enable/Disable the air quality metrics - */ - bool air_quality_enabled = 6; - - /* - * Interval in seconds of how often we should try to send our - * air quality metrics to the mesh - */ - uint32 air_quality_interval = 7; - - /* - * Interval in seconds of how often we should try to send our - * air quality metrics to the mesh - */ - bool power_measurement_enabled = 8; - - /* - * Interval in seconds of how often we should try to send our - * air quality metrics to the mesh - */ - uint32 power_update_interval = 9; - - /* - * Interval in seconds of how often we should try to send our - * air quality metrics to the mesh - */ - bool power_screen_enabled = 10; - } - - /* - * TODO: REPLACE - */ - message CannedMessageConfig { - /* - * TODO: REPLACE - */ - enum InputEventChar { - /* - * TODO: REPLACE - */ - NONE = 0; - - /* - * TODO: REPLACE - */ - UP = 17; - - /* - * TODO: REPLACE - */ - DOWN = 18; - - /* - * TODO: REPLACE - */ - LEFT = 19; - - /* - * TODO: REPLACE - */ - RIGHT = 20; - - /* - * '\n' - */ - SELECT = 10; - - /* - * TODO: REPLACE - */ - BACK = 27; - - /* - * TODO: REPLACE - */ - CANCEL = 24; - } - - /* - * Enable the rotary encoder #1. This is a 'dumb' encoder sending pulses on both A and B pins while rotating. - */ - bool rotary1_enabled = 1; - - /* - * GPIO pin for rotary encoder A port. - */ - uint32 inputbroker_pin_a = 2; - - /* - * GPIO pin for rotary encoder B port. - */ - uint32 inputbroker_pin_b = 3; - - /* - * GPIO pin for rotary encoder Press port. - */ - uint32 inputbroker_pin_press = 4; - - /* - * Generate input event on CW of this kind. - */ - InputEventChar inputbroker_event_cw = 5; - - /* - * Generate input event on CCW of this kind. - */ - InputEventChar inputbroker_event_ccw = 6; - - /* - * Generate input event on Press of this kind. - */ - InputEventChar inputbroker_event_press = 7; - - /* - * Enable the Up/Down/Select input device. Can be RAK rotary encoder or 3 buttons. Uses the a/b/press definitions from inputbroker. - */ - bool updown1_enabled = 8; - - /* - * Enable/disable CannedMessageModule. - */ - bool enabled = 9; - - /* - * Input event origin accepted by the canned message module. - * Can be e.g. "rotEnc1", "upDownEnc1", "scanAndSelect", "cardkb", "serialkb", or keyword "_any" - */ - string allow_input_source = 10; - - /* - * CannedMessageModule also sends a bell character with the messages. - * ExternalNotificationModule can benefit from this feature. - */ - bool send_bell = 11; - } - - /* - Ambient Lighting Module - Settings for control of onboard LEDs to allow users to adjust the brightness levels and respective color levels. - Initially created for the RAK14001 RGB LED module. - */ - message AmbientLightingConfig { - /* - * Sets LED to on or off. - */ - bool led_state = 1; - - /* - * Sets the current for the LED output. Default is 10. - */ - uint32 current = 2; - - /* - * Sets the red LED level. Values are 0-255. - */ - uint32 red = 3; - - /* - * Sets the green LED level. Values are 0-255. - */ - uint32 green = 4; - - /* - * Sets the blue LED level. Values are 0-255. - */ - uint32 blue = 5; - } - - /* - * TODO: REPLACE - */ - oneof payload_variant { - /* - * TODO: REPLACE - */ - MQTTConfig mqtt = 1; - - /* - * TODO: REPLACE - */ - SerialConfig serial = 2; - - /* - * TODO: REPLACE - */ - ExternalNotificationConfig external_notification = 3; - - /* - * TODO: REPLACE - */ - StoreForwardConfig store_forward = 4; - - /* - * TODO: REPLACE - */ - RangeTestConfig range_test = 5; - - /* - * TODO: REPLACE - */ - TelemetryConfig telemetry = 6; - - /* - * TODO: REPLACE - */ - CannedMessageConfig canned_message = 7; - - /* - * TODO: REPLACE - */ - AudioConfig audio = 8; - - /* - * TODO: REPLACE - */ - RemoteHardwareConfig remote_hardware = 9; - - /* - * TODO: REPLACE - */ - NeighborInfoConfig neighbor_info = 10; - - /* - * TODO: REPLACE - */ - AmbientLightingConfig ambient_lighting = 11; - - /* - * TODO: REPLACE - */ - DetectionSensorConfig detection_sensor = 12; - - /* - * TODO: REPLACE - */ - PaxcounterConfig paxcounter = 13; - } -} - -/* - * A GPIO pin definition for remote hardware module - */ -message RemoteHardwarePin { - /* - * GPIO Pin number (must match Arduino) - */ - uint32 gpio_pin = 1; - - /* - * Name for the GPIO pin (i.e. Front gate, mailbox, etc) - */ - string name = 2; - - /* - * Type of GPIO access available to consumers on the mesh - */ - RemoteHardwarePinType type = 3; -} - -enum RemoteHardwarePinType { - /* - * Unset/unused - */ - UNKNOWN = 0; - - /* - * GPIO pin can be read (if it is high / low) - */ - DIGITAL_READ = 1; - - /* - * GPIO pin can be written to (high / low) - */ - DIGITAL_WRITE = 2; -} diff --git a/proto_def/meshtastic/mqtt.options b/proto_def/meshtastic/mqtt.options deleted file mode 100644 index 591e898e..00000000 --- a/proto_def/meshtastic/mqtt.options +++ /dev/null @@ -1,8 +0,0 @@ -*ServiceEnvelope.packet type:FT_POINTER -*ServiceEnvelope.channel_id type:FT_POINTER -*ServiceEnvelope.gateway_id type:FT_POINTER - -*MapReport.long_name max_size:40 -*MapReport.short_name max_size:5 -*MapReport.firmware_version max_size:18 -*MapReport.num_online_local_nodes int_size:16 \ No newline at end of file diff --git a/proto_def/meshtastic/mqtt.proto b/proto_def/meshtastic/mqtt.proto deleted file mode 100644 index 2dbc820c..00000000 --- a/proto_def/meshtastic/mqtt.proto +++ /dev/null @@ -1,106 +0,0 @@ -syntax = "proto3"; - -package meshtastic; - -import "meshtastic/config.proto"; -import "meshtastic/mesh.proto"; - -option csharp_namespace = "Meshtastic.Protobufs"; -option go_package = "github.com/meshtastic/go/generated"; -option java_outer_classname = "MQTTProtos"; -option java_package = "com.geeksville.mesh"; -option swift_prefix = ""; - -/* - * This message wraps a MeshPacket with extra metadata about the sender and how it arrived. - */ -message ServiceEnvelope { - /* - * The (probably encrypted) packet - */ - MeshPacket packet = 1; - - /* - * The global channel ID it was sent on - */ - string channel_id = 2; - - /* - * The sending gateway node ID. Can we use this to authenticate/prevent fake - * nodeid impersonation for senders? - i.e. use gateway/mesh id (which is authenticated) + local node id as - * the globally trusted nodenum - */ - string gateway_id = 3; -} - -/* - * Information about a node intended to be reported unencrypted to a map using MQTT. - */ -message MapReport { - /* - * A full name for this user, i.e. "Kevin Hester" - */ - string long_name = 1; - - /* - * A VERY short name, ideally two characters. - * Suitable for a tiny OLED screen - */ - string short_name = 2; - - /* - * Role of the node that applies specific settings for a particular use-case - */ - Config.DeviceConfig.Role role = 3; - - /* - * Hardware model of the node, i.e. T-Beam, Heltec V3, etc... - */ - HardwareModel hw_model = 4; - - /* - * Device firmware version string - */ - string firmware_version = 5; - - /* - * The region code for the radio (US, CN, EU433, etc...) - */ - Config.LoRaConfig.RegionCode region = 6; - - /* - * Modem preset used by the radio (LongFast, MediumSlow, etc...) - */ - Config.LoRaConfig.ModemPreset modem_preset = 7; - - /* - * Whether the node has a channel with default PSK and name (LongFast, MediumSlow, etc...) - * and it uses the default frequency slot given the region and modem preset. - */ - bool has_default_channel = 8; - - /* - * Latitude: multiply by 1e-7 to get degrees in floating point - */ - sfixed32 latitude_i = 9; - - /* - * Longitude: multiply by 1e-7 to get degrees in floating point - */ - sfixed32 longitude_i = 10; - - /* - * Altitude in meters above MSL - */ - int32 altitude = 11; - - /* - * Indicates the bits of precision for latitude and longitude set by the sending node - */ - uint32 position_precision = 12; - - /* - * Number of online nodes (heard in the last 2 hours) this node has in its list that were received locally (not via MQTT) - */ - uint32 num_online_local_nodes = 13; -} diff --git a/proto_def/meshtastic/paxcount.proto b/proto_def/meshtastic/paxcount.proto deleted file mode 100644 index 47b26398..00000000 --- a/proto_def/meshtastic/paxcount.proto +++ /dev/null @@ -1,29 +0,0 @@ -syntax = "proto3"; - -package meshtastic; - -option csharp_namespace = "Meshtastic.Protobufs"; -option go_package = "github.com/meshtastic/go/generated"; -option java_outer_classname = "PaxcountProtos"; -option java_package = "com.geeksville.mesh"; -option swift_prefix = ""; - -/* - * TODO: REPLACE - */ -message Paxcount { - /* - * seen Wifi devices - */ - uint32 wifi = 1; - - /* - * Seen BLE devices - */ - uint32 ble = 2; - - /* - * Uptime in seconds - */ - uint32 uptime = 3; -} diff --git a/proto_def/meshtastic/portnums.proto b/proto_def/meshtastic/portnums.proto deleted file mode 100644 index 1f8cdb9f..00000000 --- a/proto_def/meshtastic/portnums.proto +++ /dev/null @@ -1,221 +0,0 @@ -syntax = "proto3"; - -package meshtastic; - -option csharp_namespace = "Meshtastic.Protobufs"; -option go_package = "github.com/meshtastic/go/generated"; -option java_outer_classname = "Portnums"; -option java_package = "com.geeksville.mesh"; -option swift_prefix = ""; - -/* - * For any new 'apps' that run on the device or via sister apps on phones/PCs they should pick and use a - * unique 'portnum' for their application. - * If you are making a new app using meshtastic, please send in a pull request to add your 'portnum' to this - * master table. - * PortNums should be assigned in the following range: - * 0-63 Core Meshtastic use, do not use for third party apps - * 64-127 Registered 3rd party apps, send in a pull request that adds a new entry to portnums.proto to register your application - * 256-511 Use one of these portnums for your private applications that you don't want to register publically - * All other values are reserved. - * Note: This was formerly a Type enum named 'typ' with the same id # - * We have change to this 'portnum' based scheme for specifying app handlers for particular payloads. - * This change is backwards compatible by treating the legacy OPAQUE/CLEAR_TEXT values identically. - */ -enum PortNum { - /* - * Deprecated: do not use in new code (formerly called OPAQUE) - * A message sent from a device outside of the mesh, in a form the mesh does not understand - * NOTE: This must be 0, because it is documented in IMeshService.aidl to be so - * ENCODING: binary undefined - */ - UNKNOWN_APP = 0; - - /* - * A simple UTF-8 text message, which even the little micros in the mesh - * can understand and show on their screen eventually in some circumstances - * even signal might send messages in this form (see below) - * ENCODING: UTF-8 Plaintext (?) - */ - TEXT_MESSAGE_APP = 1; - - /* - * Reserved for built-in GPIO/example app. - * See remote_hardware.proto/HardwareMessage for details on the message sent/received to this port number - * ENCODING: Protobuf - */ - REMOTE_HARDWARE_APP = 2; - - /* - * The built-in position messaging app. - * Payload is a Position message. - * ENCODING: Protobuf - */ - POSITION_APP = 3; - - /* - * The built-in user info app. - * Payload is a User message. - * ENCODING: Protobuf - */ - NODEINFO_APP = 4; - - /* - * Protocol control packets for mesh protocol use. - * Payload is a Routing message. - * ENCODING: Protobuf - */ - ROUTING_APP = 5; - - /* - * Admin control packets. - * Payload is a AdminMessage message. - * ENCODING: Protobuf - */ - ADMIN_APP = 6; - - /* - * Compressed TEXT_MESSAGE payloads. - * ENCODING: UTF-8 Plaintext (?) with Unishox2 Compression - * NOTE: The Device Firmware converts a TEXT_MESSAGE_APP to TEXT_MESSAGE_COMPRESSED_APP if the compressed - * payload is shorter. There's no need for app developers to do this themselves. Also the firmware will decompress - * any incoming TEXT_MESSAGE_COMPRESSED_APP payload and convert to TEXT_MESSAGE_APP. - */ - TEXT_MESSAGE_COMPRESSED_APP = 7; - - /* - * Waypoint payloads. - * Payload is a Waypoint message. - * ENCODING: Protobuf - */ - WAYPOINT_APP = 8; - - /* - * Audio Payloads. - * Encapsulated codec2 packets. On 2.4 GHZ Bandwidths only for now - * ENCODING: codec2 audio frames - * NOTE: audio frames contain a 3 byte header (0xc0 0xde 0xc2) and a one byte marker for the decompressed bitrate. - * This marker comes from the 'moduleConfig.audio.bitrate' enum minus one. - */ - AUDIO_APP = 9; - - /* - * Same as Text Message but originating from Detection Sensor Module. - * NOTE: This portnum traffic is not sent to the public MQTT starting at firmware version 2.2.9 - */ - DETECTION_SENSOR_APP = 10; - - /* - * Provides a 'ping' service that replies to any packet it receives. - * Also serves as a small example module. - * ENCODING: ASCII Plaintext - */ - REPLY_APP = 32; - - /* - * Used for the python IP tunnel feature - * ENCODING: IP Packet. Handled by the python API, firmware ignores this one and pases on. - */ - IP_TUNNEL_APP = 33; - - /* - * Paxcounter lib included in the firmware - * ENCODING: protobuf - */ - PAXCOUNTER_APP = 34; - - /* - * Provides a hardware serial interface to send and receive from the Meshtastic network. - * Connect to the RX/TX pins of a device with 38400 8N1. Packets received from the Meshtastic - * network is forwarded to the RX pin while sending a packet to TX will go out to the Mesh network. - * Maximum packet size of 240 bytes. - * Module is disabled by default can be turned on by setting SERIAL_MODULE_ENABLED = 1 in SerialPlugh.cpp. - * ENCODING: binary undefined - */ - SERIAL_APP = 64; - - /* - * STORE_FORWARD_APP (Work in Progress) - * Maintained by Jm Casler (MC Hamster) : jm@casler.org - * ENCODING: Protobuf - */ - STORE_FORWARD_APP = 65; - - /* - * Optional port for messages for the range test module. - * ENCODING: ASCII Plaintext - * NOTE: This portnum traffic is not sent to the public MQTT starting at firmware version 2.2.9 - */ - RANGE_TEST_APP = 66; - - /* - * Provides a format to send and receive telemetry data from the Meshtastic network. - * Maintained by Charles Crossan (crossan007) : crossan007@gmail.com - * ENCODING: Protobuf - */ - TELEMETRY_APP = 67; - - /* - * Experimental tools for estimating node position without a GPS - * Maintained by Github user a-f-G-U-C (a Meshtastic contributor) - * Project files at https://github.com/a-f-G-U-C/Meshtastic-ZPS - * ENCODING: arrays of int64 fields - */ - ZPS_APP = 68; - - /* - * Used to let multiple instances of Linux native applications communicate - * as if they did using their LoRa chip. - * Maintained by GitHub user GUVWAF. - * Project files at https://github.com/GUVWAF/Meshtasticator - * ENCODING: Protobuf (?) - */ - SIMULATOR_APP = 69; - - /* - * Provides a traceroute functionality to show the route a packet towards - * a certain destination would take on the mesh. Contains a RouteDiscovery message as payload. - * ENCODING: Protobuf - */ - TRACEROUTE_APP = 70; - - /* - * Aggregates edge info for the network by sending out a list of each node's neighbors - * ENCODING: Protobuf - */ - NEIGHBORINFO_APP = 71; - - /* - * ATAK Plugin - * Portnum for payloads from the official Meshtastic ATAK plugin - */ - ATAK_PLUGIN = 72; - - /* - * Provides unencrypted information about a node for consumption by a map via MQTT - */ - MAP_REPORT_APP = 73; - - /* - * PowerStress based monitoring support (for automated power consumption testing) - */ - POWERSTRESS_APP = 74; - - /* - * Private applications should use portnums >= 256. - * To simplify initial development and testing you can use "PRIVATE_APP" - * in your code without needing to rebuild protobuf files (via [regen-protos.sh](https://github.com/meshtastic/firmware/blob/master/bin/regen-protos.sh)) - */ - PRIVATE_APP = 256; - - /* - * ATAK Forwarder Module https://github.com/paulmandal/atak-forwarder - * ENCODING: libcotshrink - */ - ATAK_FORWARDER = 257; - - /* - * Currently we limit port nums to no higher than this value - */ - MAX = 511; -} \ No newline at end of file diff --git a/proto_def/meshtastic/powermon.proto b/proto_def/meshtastic/powermon.proto deleted file mode 100644 index 5f65b511..00000000 --- a/proto_def/meshtastic/powermon.proto +++ /dev/null @@ -1,104 +0,0 @@ -syntax = "proto3"; - -option csharp_namespace = "Meshtastic.Protobufs"; -option go_package = "github.com/meshtastic/go/generated"; -option java_outer_classname = "PowerMonProtos"; -option java_package = "com.geeksville.mesh"; -option swift_prefix = ""; - -package meshtastic; - -/* Note: There are no 'PowerMon' messages normally in use (PowerMons are sent only as structured logs - slogs). -But we wrap our State enum in this message to effectively nest a namespace (without our linter yelling at us) -*/ -message PowerMon { - /* Any significant power changing event in meshtastic should be tagged with a powermon state transition. - If you are making new meshtastic features feel free to add new entries at the end of this definition. - */ - enum State { - None = 0; - - CPU_DeepSleep = 0x01; - CPU_LightSleep = 0x02; - - /* - The external Vext1 power is on. Many boards have auxillary power rails that the CPU turns on only - occasionally. In cases where that rail has multiple devices on it we usually want to have logging on - the state of that rail as an independent record. - For instance on the Heltec Tracker 1.1 board, this rail is the power source for the GPS and screen. - - The log messages will be short and complete (see PowerMon.Event in the protobufs for details). - something like "S:PM:C,0x00001234,REASON" where the hex number is the bitmask of all current states. - (We use a bitmask for states so that if a log message gets lost it won't be fatal) - */ - Vext1_On = 0x04; - - Lora_RXOn = 0x08; - Lora_TXOn = 0x10; - Lora_RXActive = 0x20; - BT_On = 0x40; - LED_On = 0x80; - - Screen_On = 0x100; - Screen_Drawing = 0x200; - Wifi_On = 0x400; - - /* - GPS is actively trying to find our location - See GPSPowerState for more details - */ - GPS_Active = 0x800; - } -} - - -/* - * PowerStress testing support via the C++ PowerStress module - */ -message PowerStressMessage { - /* - * What operation would we like the UUT to perform. - note: senders should probably set want_response in their request packets, so that they can know when the state - machine has started processing their request - */ - enum Opcode { - /* - * Unset/unused - */ - UNSET = 0; - - PRINT_INFO = 1; // Print board version slog and send an ack that we are alive and ready to process commands - FORCE_QUIET = 2; // Try to turn off all automatic processing of packets, screen, sleeping, etc (to make it easier to measure in isolation) - END_QUIET = 3; // Stop powerstress processing - probably by just rebooting the board - - SCREEN_ON = 16; // Turn the screen on - SCREEN_OFF = 17; // Turn the screen off - - CPU_IDLE = 32; // Let the CPU run but we assume mostly idling for num_seconds - CPU_DEEPSLEEP = 33; // Force deep sleep for FIXME seconds - CPU_FULLON = 34; // Spin the CPU as fast as possible for num_seconds - - LED_ON = 48; // Turn the LED on for num_seconds (and leave it on - for baseline power measurement purposes) - LED_OFF = 49; // Force the LED off for num_seconds - - LORA_OFF = 64; // Completely turn off the LORA radio for num_seconds - LORA_TX = 65; // Send Lora packets for num_seconds - LORA_RX = 66; // Receive Lora packets for num_seconds (node will be mostly just listening, unless an external agent is helping stress this by sending packets on the current channel) - - BT_OFF = 80; // Turn off the BT radio for num_seconds - BT_ON = 81; // Turn on the BT radio for num_seconds - - WIFI_OFF = 96; // Turn off the WIFI radio for num_seconds - WIFI_ON = 97; // Turn on the WIFI radio for num_seconds - - GPS_OFF = 112; // Turn off the GPS radio for num_seconds - GPS_ON = 113; // Turn on the GPS radio for num_seconds - } - - /* - * What type of HardwareMessage is this? - */ - Opcode cmd = 1; - - float num_seconds = 2; -} \ No newline at end of file diff --git a/proto_def/meshtastic/remote_hardware.proto b/proto_def/meshtastic/remote_hardware.proto deleted file mode 100644 index ba4a6930..00000000 --- a/proto_def/meshtastic/remote_hardware.proto +++ /dev/null @@ -1,75 +0,0 @@ -syntax = "proto3"; - -package meshtastic; - -option csharp_namespace = "Meshtastic.Protobufs"; -option go_package = "github.com/meshtastic/go/generated"; -option java_outer_classname = "RemoteHardware"; -option java_package = "com.geeksville.mesh"; -option swift_prefix = ""; - -/* - * An example app to show off the module system. This message is used for - * REMOTE_HARDWARE_APP PortNums. - * Also provides easy remote access to any GPIO. - * In the future other remote hardware operations can be added based on user interest - * (i.e. serial output, spi/i2c input/output). - * FIXME - currently this feature is turned on by default which is dangerous - * because no security yet (beyond the channel mechanism). - * It should be off by default and then protected based on some TBD mechanism - * (a special channel once multichannel support is included?) - */ -message HardwareMessage { - /* - * TODO: REPLACE - */ - enum Type { - /* - * Unset/unused - */ - UNSET = 0; - - /* - * Set gpio gpios based on gpio_mask/gpio_value - */ - WRITE_GPIOS = 1; - - /* - * We are now interested in watching the gpio_mask gpios. - * If the selected gpios change, please broadcast GPIOS_CHANGED. - * Will implicitly change the gpios requested to be INPUT gpios. - */ - WATCH_GPIOS = 2; - - /* - * The gpios listed in gpio_mask have changed, the new values are listed in gpio_value - */ - GPIOS_CHANGED = 3; - - /* - * Read the gpios specified in gpio_mask, send back a READ_GPIOS_REPLY reply with gpio_value populated - */ - READ_GPIOS = 4; - - /* - * A reply to READ_GPIOS. gpio_mask and gpio_value will be populated - */ - READ_GPIOS_REPLY = 5; - } - - /* - * What type of HardwareMessage is this? - */ - Type type = 1; - - /* - * What gpios are we changing. Not used for all MessageTypes, see MessageType for details - */ - uint64 gpio_mask = 2; - - /* - * For gpios that were listed in gpio_mask as valid, what are the signal levels for those gpios. - * Not used for all MessageTypes, see MessageType for details - */ - uint64 gpio_value = 3; -} diff --git a/proto_def/meshtastic/rtttl.options b/proto_def/meshtastic/rtttl.options deleted file mode 100644 index 1ae0c2f7..00000000 --- a/proto_def/meshtastic/rtttl.options +++ /dev/null @@ -1 +0,0 @@ -*RTTTLConfig.ringtone max_size:230 diff --git a/proto_def/meshtastic/rtttl.proto b/proto_def/meshtastic/rtttl.proto deleted file mode 100644 index 11c8b925..00000000 --- a/proto_def/meshtastic/rtttl.proto +++ /dev/null @@ -1,19 +0,0 @@ -syntax = "proto3"; - -package meshtastic; - -option csharp_namespace = "Meshtastic.Protobufs"; -option go_package = "github.com/meshtastic/go/generated"; -option java_outer_classname = "RTTTLConfigProtos"; -option java_package = "com.geeksville.mesh"; -option swift_prefix = ""; - -/* - * Canned message module configuration. - */ -message RTTTLConfig { - /* - * Ringtone for PWM Buzzer in RTTTL Format. - */ - string ringtone = 1; -} diff --git a/proto_def/meshtastic/storeforward.options b/proto_def/meshtastic/storeforward.options deleted file mode 100644 index 8580aab3..00000000 --- a/proto_def/meshtastic/storeforward.options +++ /dev/null @@ -1 +0,0 @@ -*StoreAndForward.text max_size:237 \ No newline at end of file diff --git a/proto_def/meshtastic/storeforward.proto b/proto_def/meshtastic/storeforward.proto deleted file mode 100644 index 651eae57..00000000 --- a/proto_def/meshtastic/storeforward.proto +++ /dev/null @@ -1,218 +0,0 @@ -syntax = "proto3"; - -package meshtastic; - -option csharp_namespace = "Meshtastic.Protobufs"; -option go_package = "github.com/meshtastic/go/generated"; -option java_outer_classname = "StoreAndForwardProtos"; -option java_package = "com.geeksville.mesh"; -option swift_prefix = ""; - -/* - * TODO: REPLACE - */ -message StoreAndForward { - /* - * 001 - 063 = From Router - * 064 - 127 = From Client - */ - enum RequestResponse { - /* - * Unset/unused - */ - UNSET = 0; - - /* - * Router is an in error state. - */ - ROUTER_ERROR = 1; - - /* - * Router heartbeat - */ - ROUTER_HEARTBEAT = 2; - - /* - * Router has requested the client respond. This can work as a - * "are you there" message. - */ - ROUTER_PING = 3; - - /* - * The response to a "Ping" - */ - ROUTER_PONG = 4; - - /* - * Router is currently busy. Please try again later. - */ - ROUTER_BUSY = 5; - - /* - * Router is responding to a request for history. - */ - ROUTER_HISTORY = 6; - - /* - * Router is responding to a request for stats. - */ - ROUTER_STATS = 7; - - /* - * Router sends a text message from its history that was a direct message. - */ - ROUTER_TEXT_DIRECT = 8; - - /* - * Router sends a text message from its history that was a broadcast. - */ - ROUTER_TEXT_BROADCAST = 9; - - /* - * Client is an in error state. - */ - CLIENT_ERROR = 64; - - /* - * Client has requested a replay from the router. - */ - CLIENT_HISTORY = 65; - - /* - * Client has requested stats from the router. - */ - CLIENT_STATS = 66; - - /* - * Client has requested the router respond. This can work as a - * "are you there" message. - */ - CLIENT_PING = 67; - - /* - * The response to a "Ping" - */ - CLIENT_PONG = 68; - - /* - * Client has requested that the router abort processing the client's request - */ - CLIENT_ABORT = 106; - } - - /* - * TODO: REPLACE - */ - message Statistics { - /* - * Number of messages we have ever seen - */ - uint32 messages_total = 1; - - /* - * Number of messages we have currently saved our history. - */ - uint32 messages_saved = 2; - - /* - * Maximum number of messages we will save - */ - uint32 messages_max = 3; - - /* - * Router uptime in seconds - */ - uint32 up_time = 4; - - /* - * Number of times any client sent a request to the S&F. - */ - uint32 requests = 5; - - /* - * Number of times the history was requested. - */ - uint32 requests_history = 6; - - /* - * Is the heartbeat enabled on the server? - */ - bool heartbeat = 7; - - /* - * Maximum number of messages the server will return. - */ - uint32 return_max = 8; - - /* - * Maximum history window in minutes the server will return messages from. - */ - uint32 return_window = 9; - } - - /* - * TODO: REPLACE - */ - message History { - /* - * Number of that will be sent to the client - */ - uint32 history_messages = 1; - - /* - * The window of messages that was used to filter the history client requested - */ - uint32 window = 2; - - /* - * Index in the packet history of the last message sent in a previous request to the server. - * Will be sent to the client before sending the history and can be set in a subsequent request to avoid getting packets the server already sent to the client. - */ - uint32 last_request = 3; - } - - /* - * TODO: REPLACE - */ - message Heartbeat { - /* - * Period in seconds that the heartbeat is sent out that will be sent to the client - */ - uint32 period = 1; - - /* - * If set, this is not the primary Store & Forward router on the mesh - */ - uint32 secondary = 2; - } - - /* - * TODO: REPLACE - */ - RequestResponse rr = 1; - - /* - * TODO: REPLACE - */ - oneof variant { - /* - * TODO: REPLACE - */ - Statistics stats = 2; - - /* - * TODO: REPLACE - */ - History history = 3; - - /* - * TODO: REPLACE - */ - Heartbeat heartbeat = 4; - - /* - * Text from history message. - */ - bytes text = 5; - } -} diff --git a/proto_def/meshtastic/telemetry.options b/proto_def/meshtastic/telemetry.options deleted file mode 100644 index 073412ac..00000000 --- a/proto_def/meshtastic/telemetry.options +++ /dev/null @@ -1,8 +0,0 @@ -# options for nanopb -# https://jpa.kapsi.fi/nanopb/docs/reference.html#proto-file-options - -*EnvironmentMetrics.iaq int_size:16 -*EnvironmentMetrics.wind_direction int_size:16 - -*LocalStats.num_online_nodes int_size:16 -*LocalStats.num_total_nodes int_size:16 diff --git a/proto_def/meshtastic/telemetry.proto b/proto_def/meshtastic/telemetry.proto deleted file mode 100644 index 6ca8db30..00000000 --- a/proto_def/meshtastic/telemetry.proto +++ /dev/null @@ -1,476 +0,0 @@ -syntax = "proto3"; - -package meshtastic; - -option csharp_namespace = "Meshtastic.Protobufs"; -option go_package = "github.com/meshtastic/go/generated"; -option java_outer_classname = "TelemetryProtos"; -option java_package = "com.geeksville.mesh"; -option swift_prefix = ""; - -/* - * Key native device metrics such as battery level - */ -message DeviceMetrics { - /* - * 0-100 (>100 means powered) - */ - optional uint32 battery_level = 1; - - /* - * Voltage measured - */ - optional float voltage = 2; - - /* - * Utilization for the current channel, including well formed TX, RX and malformed RX (aka noise). - */ - optional float channel_utilization = 3; - - /* - * Percent of airtime for transmission used within the last hour. - */ - optional float air_util_tx = 4; - - /* - * How long the device has been running since the last reboot (in seconds) - */ - optional uint32 uptime_seconds = 5; -} - -/* - * Weather station or other environmental metrics - */ -message EnvironmentMetrics { - /* - * Temperature measured - */ - optional float temperature = 1; - - /* - * Relative humidity percent measured - */ - optional float relative_humidity = 2; - - /* - * Barometric pressure in hPA measured - */ - optional float barometric_pressure = 3; - - /* - * Gas resistance in MOhm measured - */ - optional float gas_resistance = 4; - - /* - * Voltage measured (To be depreciated in favor of PowerMetrics in Meshtastic 3.x) - */ - optional float voltage = 5; - - /* - * Current measured (To be depreciated in favor of PowerMetrics in Meshtastic 3.x) - */ - optional float current = 6; - - /* - * relative scale IAQ value as measured by Bosch BME680 . value 0-500. - * Belongs to Air Quality but is not particle but VOC measurement. Other VOC values can also be put in here. - */ - optional uint32 iaq = 7; - - /* - * RCWL9620 Doppler Radar Distance Sensor, used for water level detection. Float value in mm. - */ - optional float distance = 8; - - /* - * VEML7700 high accuracy ambient light(Lux) digital 16-bit resolution sensor. - */ - optional float lux = 9; - - /* - * VEML7700 high accuracy white light(irradiance) not calibrated digital 16-bit resolution sensor. - */ - optional float white_lux = 10; - - /* - * Infrared lux - */ - optional float ir_lux = 11; - - /* - * Ultraviolet lux - */ - optional float uv_lux = 12; - - /* - * Wind direction in degrees - * 0 degrees = North, 90 = East, etc... - */ - optional uint32 wind_direction = 13; - - /* - * Wind speed in m/s - */ - optional float wind_speed = 14; - - /* - * Weight in KG - */ - optional float weight = 15; - - /* - * Wind gust in m/s - */ - optional float wind_gust = 16; - - /* - * Wind lull in m/s - */ - optional float wind_lull = 17; -} - -/* - * Power Metrics (voltage / current / etc) - */ -message PowerMetrics { - /* - * Voltage (Ch1) - */ - optional float ch1_voltage = 1; - - /* - * Current (Ch1) - */ - optional float ch1_current = 2; - - /* - * Voltage (Ch2) - */ - optional float ch2_voltage = 3; - - /* - * Current (Ch2) - */ - optional float ch2_current = 4; - - /* - * Voltage (Ch3) - */ - optional float ch3_voltage = 5; - - /* - * Current (Ch3) - */ - optional float ch3_current = 6; -} - -/* - * Air quality metrics - */ -message AirQualityMetrics { - /* - * Concentration Units Standard PM1.0 - */ - optional uint32 pm10_standard = 1; - - /* - * Concentration Units Standard PM2.5 - */ - optional uint32 pm25_standard = 2; - - /* - * Concentration Units Standard PM10.0 - */ - optional uint32 pm100_standard = 3; - - /* - * Concentration Units Environmental PM1.0 - */ - optional uint32 pm10_environmental = 4; - - /* - * Concentration Units Environmental PM2.5 - */ - optional uint32 pm25_environmental = 5; - - /* - * Concentration Units Environmental PM10.0 - */ - optional uint32 pm100_environmental = 6; - - /* - * 0.3um Particle Count - */ - optional uint32 particles_03um = 7; - - /* - * 0.5um Particle Count - */ - optional uint32 particles_05um = 8; - - /* - * 1.0um Particle Count - */ - optional uint32 particles_10um = 9; - - /* - * 2.5um Particle Count - */ - optional uint32 particles_25um = 10; - - /* - * 5.0um Particle Count - */ - optional uint32 particles_50um = 11; - - /* - * 10.0um Particle Count - */ - optional uint32 particles_100um = 12; -} - -/* - * Local device mesh statistics - */ -message LocalStats { - /* - * How long the device has been running since the last reboot (in seconds) - */ - uint32 uptime_seconds = 1; - /* - * Utilization for the current channel, including well formed TX, RX and malformed RX (aka noise). - */ - float channel_utilization = 2; - /* - * Percent of airtime for transmission used within the last hour. - */ - float air_util_tx = 3; - - /* - * Number of packets sent - */ - uint32 num_packets_tx = 4; - - /* - * Number of packets received good - */ - uint32 num_packets_rx = 5; - - /* - * Number of packets received that are malformed or violate the protocol - */ - uint32 num_packets_rx_bad = 6; - - /* - * Number of nodes online (in the past 2 hours) - */ - uint32 num_online_nodes = 7; - - /* - * Number of nodes total - */ - uint32 num_total_nodes = 8; -} - -/* - * Types of Measurements the telemetry module is equipped to handle - */ -message Telemetry { - /* - * Seconds since 1970 - or 0 for unknown/unset - */ - fixed32 time = 1; - - oneof variant { - /* - * Key native device metrics such as battery level - */ - DeviceMetrics device_metrics = 2; - - /* - * Weather station or other environmental metrics - */ - EnvironmentMetrics environment_metrics = 3; - - /* - * Air quality metrics - */ - AirQualityMetrics air_quality_metrics = 4; - - /* - * Power Metrics - */ - PowerMetrics power_metrics = 5; - - /* - * Local device mesh statistics - */ - LocalStats local_stats = 6; - } -} - -/* - * Supported I2C Sensors for telemetry in Meshtastic - */ -enum TelemetrySensorType { - /* - * No external telemetry sensor explicitly set - */ - SENSOR_UNSET = 0; - - /* - * High accuracy temperature, pressure, humidity - */ - BME280 = 1; - - /* - * High accuracy temperature, pressure, humidity, and air resistance - */ - BME680 = 2; - - /* - * Very high accuracy temperature - */ - MCP9808 = 3; - - /* - * Moderate accuracy current and voltage - */ - INA260 = 4; - - /* - * Moderate accuracy current and voltage - */ - INA219 = 5; - - /* - * High accuracy temperature and pressure - */ - BMP280 = 6; - - /* - * High accuracy temperature and humidity - */ - SHTC3 = 7; - - /* - * High accuracy pressure - */ - LPS22 = 8; - - /* - * 3-Axis magnetic sensor - */ - QMC6310 = 9; - - /* - * 6-Axis inertial measurement sensor - */ - QMI8658 = 10; - - /* - * 3-Axis magnetic sensor - */ - QMC5883L = 11; - - /* - * High accuracy temperature and humidity - */ - SHT31 = 12; - - /* - * PM2.5 air quality sensor - */ - PMSA003I = 13; - - /* - * INA3221 3 Channel Voltage / Current Sensor - */ - INA3221 = 14; - - /* - * BMP085/BMP180 High accuracy temperature and pressure (older Version of BMP280) - */ - BMP085 = 15; - - /* - * RCWL-9620 Doppler Radar Distance Sensor, used for water level detection - */ - RCWL9620 = 16; - - /* - * Sensirion High accuracy temperature and humidity - */ - SHT4X = 17; - - /* - * VEML7700 high accuracy ambient light(Lux) digital 16-bit resolution sensor. - */ - VEML7700 = 18; - - /* - * MLX90632 non-contact IR temperature sensor. - */ - MLX90632 = 19; - - /* - * TI OPT3001 Ambient Light Sensor - */ - OPT3001 = 20; - - /* - * Lite On LTR-390UV-01 UV Light Sensor - */ - LTR390UV = 21; - - /* - * AMS TSL25911FN RGB Light Sensor - */ - TSL25911FN = 22; - - /* - * AHT10 Integrated temperature and humidity sensor - */ - AHT10 = 23; - - /* - * DFRobot Lark Weather station (temperature, humidity, pressure, wind speed and direction) - */ - DFROBOT_LARK = 24; - - /* - * NAU7802 Scale Chip or compatible - */ - NAU7802 = 25; - - /* - * BMP3XX High accuracy temperature and pressure - */ - BMP3XX = 26; - - /* - * ICM-20948 9-Axis digital motion processor - */ - ICM20948 = 27; - - /* - * MAX17048 1S lipo battery sensor (voltage, state of charge, time to go) - */ - MAX17048 = 28; -} - -/* - * NAU7802 Telemetry configuration, for saving to flash - */ -message Nau7802Config { - /* - * The offset setting for the NAU7802 - */ - int32 zeroOffset = 1; - - /* - * The calibration factor for the NAU7802 - */ - float calibrationFactor = 2; -} diff --git a/proto_def/meshtastic/xmodem.options b/proto_def/meshtastic/xmodem.options deleted file mode 100644 index 3af6125a..00000000 --- a/proto_def/meshtastic/xmodem.options +++ /dev/null @@ -1,6 +0,0 @@ -# options for nanopb -# https://jpa.kapsi.fi/nanopb/docs/reference.html#proto-file-options - -*XModem.buffer max_size:128 -*XModem.seq int_size:16 -*XModem.crc16 int_size:16 diff --git a/proto_def/meshtastic/xmodem.proto b/proto_def/meshtastic/xmodem.proto deleted file mode 100644 index 732780a3..00000000 --- a/proto_def/meshtastic/xmodem.proto +++ /dev/null @@ -1,27 +0,0 @@ -syntax = "proto3"; - -package meshtastic; - -option csharp_namespace = "Meshtastic.Protobufs"; -option go_package = "github.com/meshtastic/go/generated"; -option java_outer_classname = "XmodemProtos"; -option java_package = "com.geeksville.mesh"; -option swift_prefix = ""; - -message XModem { - enum Control { - NUL = 0; - SOH = 1; - STX = 2; - EOT = 4; - ACK = 6; - NAK = 21; - CAN = 24; - CTRLZ = 26; - } - - Control control = 1; - uint32 seq = 2; - uint32 crc16 = 3; - bytes buffer = 4; -} diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..6d9aeb1d --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,59 @@ +[project] +name = "meshview" +version = "3.0.0" +description = "Real-time monitoring and diagnostic tool for the Meshtastic mesh network" +readme = "README.md" +requires-python = ">=3.10" +dependencies = [ + # Core async + networking + "aiohttp>=3.11.12,<4.0.0", + "aiohttp-sse", + "aiodns>=3.2.0,<4.0.0", + "aiomqtt>=2.3.0,<3.0.0", + "asyncpg>=0.30.0,<0.31.0", + "aiosqlite>=0.21.0,<0.22.0", + # Database + ORM + "sqlalchemy[asyncio]>=2.0.38,<3.0.0", + "alembic>=1.14.0,<2.0.0", + # Serialization / security + "protobuf>=5.29.3,<6.0.0", + "cryptography>=44.0.1,<45.0.0", + # Templates + "Jinja2>=3.1.5,<4.0.0", + "MarkupSafe>=3.0.2,<4.0.0", + # Graphs / diagrams + "pydot>=3.0.4,<4.0.0", +] + +[project.optional-dependencies] +dev = [ + # Data science stack + "numpy>=2.2.3,<3.0.0", + "pandas>=2.2.3,<3.0.0", + "matplotlib>=3.10.0,<4.0.0", + "seaborn>=0.13.2,<1.0.0", + "plotly>=6.0.0,<7.0.0", + # Image support + "pillow>=11.1.0,<12.0.0", + # Debugging / profiling + "psutil>=7.0.0,<8.0.0", + "objgraph>=3.6.2,<4.0.0", + # Testing + "pytest>=8.3.4,<9.0.0", + "pytest-aiohttp>=1.0.5,<2.0.0", + "pytest-asyncio>=0.24.0,<1.0.0", +] + +[tool.ruff] +# Linting +target-version = "py313" +line-length = 100 +extend-exclude = ["build", "dist", ".venv"] + +[tool.ruff.lint] +select = ["E", "F", "I", "UP", "B"] # pick your rulesets +ignore = ["E501"] # example; let formatter handle line length + +[tool.ruff.format] +quote-style = "preserve" +indent-style = "space" \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index e064e6ae..5265cb5f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,13 +1,50 @@ -protobuf -aiomqtt -sqlalchemy[asyncio] -cryptography -aiosqlite -aiohttp -aiodns -Jinja2 +############################# +# Runtime dependencies +############################# + +# Core async + networking +aiohttp~=3.11.12 aiohttp-sse -asyncpg -seaborn -pydot -plotly +aiodns~=3.2.0 +aiomqtt~=2.3.0 +asyncpg~=0.30.0 +aiosqlite~=0.21.0 + +# Database + ORM +sqlalchemy[asyncio]~=2.0.38 +alembic~=1.14.0 + +# Serialization / security +protobuf~=5.29.3 +cryptography~=44.0.1 + +# Templates +Jinja2~=3.1.5 +MarkupSafe~=3.0.2 + +# Graphs / diagrams +pydot~=3.0.4 + + +############################# +# Development / Analysis / Debugging +############################# + +# Data science stack +numpy~=2.2.3 +pandas~=2.2.3 +matplotlib~=3.10.0 +seaborn~=0.13.2 +plotly~=6.0.0 + +# Image support +pillow~=11.1.0 + +# Debugging / profiling +psutil~=7.0.0 +objgraph~=3.6.2 + +# Testing +pytest~=8.3.4 +pytest-aiohttp~=1.0.5 +pytest-asyncio~=0.24.0 \ No newline at end of file diff --git a/sample.config.ini b/sample.config.ini new file mode 100644 index 00000000..e4b834bd --- /dev/null +++ b/sample.config.ini @@ -0,0 +1,125 @@ +# ------------------------- +# Server Configuration +# ------------------------- +[server] +# The address to bind the server to. Use * to listen on all interfaces. +bind = * + +# Port to run the web server on. +port = 8081 + +# Path to TLS certificate (leave blank to disable HTTPS). +tls_cert = + +# Path for the ACME challenge if using Let's Encrypt. +acme_challenge = + + +# ------------------------- +# Site Appearance & Behavior +# ------------------------- +[site] +# The domain name of your site. +domain = + +# Select language +language = en + +# Site title to show in the browser title bar and headers. +title = Bay Area Mesh + +# A brief message shown on the homepage. +message = Real time data from around the bay area and beyond. + +# Starting URL when loading the index page. +starting = /chat + +# Enable or disable site features (as strings: "True" or "False"). +nodes = True +conversations = True +everything = True +graphs = True +stats = True +net = True +map = True +top = True + +# Map boundaries (used for the map view). +map_top_left_lat = 39 +map_top_left_lon = -123 +map_bottom_right_lat = 36 +map_bottom_right_lon = -121 + +# Updates intervals in seconds, zero or negative number means no updates +# defaults will be 3 seconds +map_interval=3 +firehose_interal=3 + +# Weekly net details +weekly_net_message = Weekly Mesh check-in. We will keep it open on every Wednesday from 5:00pm for checkins. The message format should be (LONG NAME) - (CITY YOU ARE IN) #BayMeshNet. +net_tag = #BayMeshNet + +# ------------------------- +# MQTT Broker Configuration +# ------------------------- +[mqtt] +# MQTT server hostname or IP. +server = mqtt.bayme.sh + +# Topics to subscribe to (as JSON-like list, but still a string). +topics = ["msh/US/bayarea/#", "msh/US/CA/mrymesh/#", "msh/US/CA/sacvalley"] + +# Port used by MQTT (typically 1883 for unencrypted). +port = 1883 + +# MQTT username and password. +username = meshdev +password = large4cats + + +# ------------------------- +# Database Configuration +# ------------------------- +[database] +# SQLAlchemy async connection string. +# Examples: +# sqlite+aiosqlite:///packets.db +# postgresql+asyncpg://user:pass@host:5432/meshview +connection_string = sqlite+aiosqlite:///packets.db + + +# ------------------------- +# Database Cleanup Configuration +# ------------------------- +[cleanup] +# Enable or disable daily cleanup +enabled = False +# Number of days to keep records in the database +days_to_keep = 14 +# Time to run daily cleanup (24-hour format) +hour = 2 +minute = 00 +# Run VACUUM after cleanup +vacuum = False + +# Enable database backups (independent of cleanup) +backup_enabled = False +# Directory to store database backups (relative or absolute path) +backup_dir = ./backups +# Time to run daily backup (24-hour format) +# If not specified, uses cleanup hour/minute +backup_hour = 2 +backup_minute = 00 + + +# ------------------------- +# Logging Configuration +# ------------------------- +[logging] +# Enable or disable HTTP access logs from the web server +# When disabled, request logs like "GET /api/chat" will not appear +# Application logs (errors, startup messages, etc.) are unaffected +# Set to True to enable, False to disable (default: False) +access_log = False +# Database cleanup logfile +db_cleanup_logfile = dbcleanup.log diff --git a/screenshots/animated.gif b/screenshots/animated.gif new file mode 100644 index 00000000..729a8e86 Binary files /dev/null and b/screenshots/animated.gif differ diff --git a/screenshots/conversation.png b/screenshots/conversation.png new file mode 100644 index 00000000..bd359577 Binary files /dev/null and b/screenshots/conversation.png differ diff --git a/screenshots/everything.png b/screenshots/everything.png new file mode 100644 index 00000000..4ab11375 Binary files /dev/null and b/screenshots/everything.png differ diff --git a/screenshots/graph.png b/screenshots/graph.png new file mode 100644 index 00000000..5ecb3f07 Binary files /dev/null and b/screenshots/graph.png differ diff --git a/screenshots/main.png b/screenshots/main.png new file mode 100644 index 00000000..6b98c2bc Binary files /dev/null and b/screenshots/main.png differ diff --git a/screenshots/nodes.png b/screenshots/nodes.png new file mode 100644 index 00000000..0f19c198 Binary files /dev/null and b/screenshots/nodes.png differ diff --git a/screenshots/reach.png b/screenshots/reach.png new file mode 100644 index 00000000..072d0445 Binary files /dev/null and b/screenshots/reach.png differ diff --git a/screenshots/stats.png b/screenshots/stats.png new file mode 100644 index 00000000..c932b043 Binary files /dev/null and b/screenshots/stats.png differ diff --git a/screenshots/top.png b/screenshots/top.png new file mode 100644 index 00000000..8053965d Binary files /dev/null and b/screenshots/top.png differ diff --git a/setup-dev.sh b/setup-dev.sh new file mode 100755 index 00000000..d8edbf94 --- /dev/null +++ b/setup-dev.sh @@ -0,0 +1,84 @@ +#!/bin/bash +# +# setup-dev.sh +# +# Development environment setup script for MeshView +# This script sets up the Python virtual environment and installs development tools + +set -e + +echo "Setting up MeshView development environment..." +echo "" + +# Check if uv is installed +if ! command -v uv &> /dev/null; then + echo "Error: 'uv' is not installed." + echo "Install it with: curl -LsSf https://astral.sh/uv/install.sh | sh" + exit 1 +fi + +# Create virtual environment if it doesn't exist +if [ ! -d "env" ]; then + echo "Creating Python virtual environment with uv..." + uv venv env + echo "✓ Virtual environment created" +else + echo "✓ Virtual environment already exists" +fi + +# Install requirements +echo "" +echo "Installing requirements..." +uv pip install -r requirements.txt +echo "✓ Requirements installed" + +# Install development tools +echo "" +echo "Installing development tools..." +uv pip install pre-commit pytest pytest-asyncio pytest-aiohttp +echo "✓ Development tools installed" + +# Install pre-commit hooks +echo "" +echo "Installing pre-commit hooks..." +./env/bin/pre-commit install +echo "✓ Pre-commit hooks installed" + +# Install graphviz check +echo "" +if command -v dot &> /dev/null; then + echo "✓ graphviz is installed" +else + echo "⚠ Warning: graphviz is not installed" + echo " Install it with:" + echo " macOS: brew install graphviz" + echo " Debian: sudo apt-get install graphviz" +fi + +# Create config.ini if it doesn't exist +echo "" +if [ ! -f "config.ini" ]; then + echo "Creating config.ini from sample..." + cp sample.config.ini config.ini + echo "✓ config.ini created" + echo " Edit config.ini to configure your MQTT and site settings" +else + echo "✓ config.ini already exists" +fi + +echo "" +echo "==========================================" +echo "Development environment setup complete!" +echo "==========================================" +echo "" +echo "Next steps:" +echo " 1. Edit config.ini with your MQTT settings" +echo " 2. Run: ./env/bin/python mvrun.py" +echo " 3. Open: http://localhost:8081" +echo "" +echo "Pre-commit hooks are now active:" +echo " - Ruff will auto-format and fix issues before each commit" +echo " - If files are changed, you'll need to git add and commit again" +echo "" +echo "Run tests with: ./env/bin/pytest tests/" +echo "" diff --git a/startdb.py b/startdb.py new file mode 100644 index 00000000..57ea461f --- /dev/null +++ b/startdb.py @@ -0,0 +1,335 @@ +import asyncio +import datetime +import gzip +import json +import logging +import shutil +from pathlib import Path + +from sqlalchemy import delete +from sqlalchemy.engine.url import make_url + +from meshview import migrations, models, mqtt_database, mqtt_reader, mqtt_store +from meshview.config import CONFIG + +# ------------------------- +# Basic logging configuration +# ------------------------- +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s %(filename)s:%(lineno)d [pid:%(process)d] %(levelname)s - %(message)s", + datefmt="%Y-%m-%d %H:%M:%S", +) + +# ------------------------- +# Logging for cleanup +# ------------------------- +cleanup_logger = logging.getLogger("dbcleanup") +cleanup_logger.setLevel(logging.INFO) +cleanup_logfile = CONFIG.get("logging", {}).get("db_cleanup_logfile", "dbcleanup.log") +file_handler = logging.FileHandler(cleanup_logfile) +file_handler.setLevel(logging.INFO) +formatter = logging.Formatter('%(asctime)s [%(levelname)s] %(message)s') +file_handler.setFormatter(formatter) +cleanup_logger.addHandler(file_handler) + + +# ------------------------- +# Helper functions +# ------------------------- +def get_bool(config, section, key, default=False): + return str(config.get(section, {}).get(key, default)).lower() in ("1", "true", "yes", "on") + + +def get_int(config, section, key, default=0): + try: + return int(config.get(section, {}).get(key, default)) + except ValueError: + return default + + +# ------------------------- +# Shared DB lock +# ------------------------- +db_lock = asyncio.Lock() + + +# ------------------------- +# Database backup function +# ------------------------- +async def backup_database(database_url: str, backup_dir: str = ".") -> None: + """ + Create a compressed backup of the database file. + + Args: + database_url: SQLAlchemy connection string + backup_dir: Directory to store backups (default: current directory) + """ + try: + url = make_url(database_url) + if not url.drivername.startswith("sqlite"): + cleanup_logger.warning("Backup only supported for SQLite databases") + return + + if not url.database or url.database == ":memory:": + cleanup_logger.error("Could not extract database path from connection string") + return + + db_file = Path(url.database) + if not db_file.exists(): + cleanup_logger.error(f"Database file not found: {db_file}") + return + + # Create backup directory if it doesn't exist + backup_path = Path(backup_dir) + backup_path.mkdir(parents=True, exist_ok=True) + + # Generate backup filename with timestamp + timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") + backup_filename = f"{db_file.stem}_backup_{timestamp}.db.gz" + backup_file = backup_path / backup_filename + + cleanup_logger.info(f"Creating backup: {backup_file}") + + # Copy and compress the database file + with open(db_file, 'rb') as f_in: + with gzip.open(backup_file, 'wb', compresslevel=9) as f_out: + shutil.copyfileobj(f_in, f_out) + + # Get file sizes for logging + original_size = db_file.stat().st_size / (1024 * 1024) # MB + compressed_size = backup_file.stat().st_size / (1024 * 1024) # MB + compression_ratio = (1 - compressed_size / original_size) * 100 if original_size > 0 else 0 + + cleanup_logger.info( + f"Backup created successfully: {backup_file.name} " + f"({original_size:.2f} MB -> {compressed_size:.2f} MB, " + f"{compression_ratio:.1f}% compression)" + ) + + except Exception as e: + cleanup_logger.error(f"Error creating database backup: {e}") + + +# ------------------------- +# Database backup scheduler +# ------------------------- +async def daily_backup_at(hour: int = 2, minute: int = 0, backup_dir: str = "."): + while True: + now = datetime.datetime.now() + next_run = now.replace(hour=hour, minute=minute, second=0, microsecond=0) + if next_run <= now: + next_run += datetime.timedelta(days=1) + delay = (next_run - now).total_seconds() + cleanup_logger.info(f"Next backup scheduled at {next_run}") + await asyncio.sleep(delay) + + database_url = CONFIG["database"]["connection_string"] + await backup_database(database_url, backup_dir) + + +# ------------------------- +# Database cleanup using ORM +# ------------------------- +async def daily_cleanup_at( + hour: int = 2, + minute: int = 0, + days_to_keep: int = 14, + vacuum_db: bool = True, + wait_for_backup: bool = False, +): + while True: + now = datetime.datetime.now() + next_run = now.replace(hour=hour, minute=minute, second=0, microsecond=0) + if next_run <= now: + next_run += datetime.timedelta(days=1) + delay = (next_run - now).total_seconds() + cleanup_logger.info(f"Next cleanup scheduled at {next_run}") + await asyncio.sleep(delay) + + # If backup is enabled, wait a bit to let backup complete first + if wait_for_backup: + cleanup_logger.info("Waiting 60 seconds for backup to complete...") + await asyncio.sleep(60) + + cutoff_dt = ( + datetime.datetime.now(datetime.UTC) - datetime.timedelta(days=days_to_keep) + ).replace(tzinfo=None) + cutoff_us = int(cutoff_dt.timestamp() * 1_000_000) + cleanup_logger.info(f"Running cleanup for records older than {cutoff_dt.isoformat()}...") + + try: + async with db_lock: # Pause ingestion + cleanup_logger.info("Ingestion paused for cleanup.") + + async with mqtt_database.async_session() as session: + # ------------------------- + # Packet + # ------------------------- + result = await session.execute( + delete(models.Packet).where(models.Packet.import_time_us < cutoff_us) + ) + cleanup_logger.info(f"Deleted {result.rowcount} rows from Packet") + + # ------------------------- + # PacketSeen + # ------------------------- + result = await session.execute( + delete(models.PacketSeen).where( + models.PacketSeen.import_time_us < cutoff_us + ) + ) + cleanup_logger.info(f"Deleted {result.rowcount} rows from PacketSeen") + + # ------------------------- + # Traceroute + # ------------------------- + result = await session.execute( + delete(models.Traceroute).where( + models.Traceroute.import_time_us < cutoff_us + ) + ) + cleanup_logger.info(f"Deleted {result.rowcount} rows from Traceroute") + + # ------------------------- + # Node + # ------------------------- + result = await session.execute( + delete(models.Node).where(models.Node.last_seen_us < cutoff_us) + ) + cleanup_logger.info(f"Deleted {result.rowcount} rows from Node") + + await session.commit() + + if vacuum_db and mqtt_database.engine.dialect.name == "sqlite": + cleanup_logger.info("Running VACUUM...") + async with mqtt_database.engine.begin() as conn: + await conn.exec_driver_sql("VACUUM;") + cleanup_logger.info("VACUUM completed.") + elif vacuum_db: + cleanup_logger.info("VACUUM skipped (not supported for this database).") + + cleanup_logger.info("Cleanup completed successfully.") + cleanup_logger.info("Ingestion resumed after cleanup.") + + except Exception as e: + cleanup_logger.error(f"Error during cleanup: {e}") + + +# ------------------------- +# MQTT loading +# ------------------------- +async def load_database_from_mqtt( + mqtt_server: str, + mqtt_port: int, + topics: list, + mqtt_user: str | None = None, + mqtt_passwd: str | None = None, +): + async for topic, env in mqtt_reader.get_topic_envelopes( + mqtt_server, mqtt_port, topics, mqtt_user, mqtt_passwd + ): + async with db_lock: # Block if cleanup is running + await mqtt_store.process_envelope(topic, env) + + +# ------------------------- +# Main function +# ------------------------- +async def main(): + logger = logging.getLogger(__name__) + + # Initialize database + database_url = CONFIG["database"]["connection_string"] + mqtt_database.init_database(database_url) + + # Create migration status table + await migrations.create_migration_status_table(mqtt_database.engine) + + # Set migration in progress flag + await migrations.set_migration_in_progress(mqtt_database.engine, True) + logger.info("Migration status set to 'in progress'") + + try: + # Check if migrations are needed before running them + logger.info("Checking for pending database migrations...") + if await migrations.is_database_up_to_date(mqtt_database.engine, database_url): + logger.info("Database schema is already up to date, skipping migrations") + else: + logger.info("Database schema needs updating, running migrations...") + migrations.run_migrations(database_url) + logger.info("Database migrations completed") + + # Create tables if needed (for backwards compatibility) + logger.info("Creating database tables...") + await mqtt_database.create_tables() + logger.info("Database tables created") + + finally: + # Clear migration in progress flag + logger.info("Clearing migration status...") + await migrations.set_migration_in_progress(mqtt_database.engine, False) + logger.info("Migration status cleared - database ready") + + mqtt_user = CONFIG["mqtt"].get("username") or None + mqtt_passwd = CONFIG["mqtt"].get("password") or None + mqtt_topics = json.loads(CONFIG["mqtt"]["topics"]) + + cleanup_enabled = get_bool(CONFIG, "cleanup", "enabled", False) + cleanup_days = get_int(CONFIG, "cleanup", "days_to_keep", 14) + vacuum_db = get_bool(CONFIG, "cleanup", "vacuum", False) + cleanup_hour = get_int(CONFIG, "cleanup", "hour", 2) + cleanup_minute = get_int(CONFIG, "cleanup", "minute", 0) + + backup_enabled = get_bool(CONFIG, "cleanup", "backup_enabled", False) + backup_dir = CONFIG.get("cleanup", {}).get("backup_dir", "./backups") + backup_hour = get_int(CONFIG, "cleanup", "backup_hour", cleanup_hour) + backup_minute = get_int(CONFIG, "cleanup", "backup_minute", cleanup_minute) + + logger.info(f"Starting MQTT ingestion from {CONFIG['mqtt']['server']}:{CONFIG['mqtt']['port']}") + if cleanup_enabled: + logger.info( + f"Daily cleanup enabled: keeping {cleanup_days} days of data at {cleanup_hour:02d}:{cleanup_minute:02d}" + ) + if backup_enabled: + logger.info( + f"Daily backups enabled: storing in {backup_dir} at {backup_hour:02d}:{backup_minute:02d}" + ) + + async with asyncio.TaskGroup() as tg: + tg.create_task( + load_database_from_mqtt( + CONFIG["mqtt"]["server"], + int(CONFIG["mqtt"]["port"]), + mqtt_topics, + mqtt_user, + mqtt_passwd, + ) + ) + + # Start backup task if enabled + if backup_enabled: + tg.create_task(daily_backup_at(backup_hour, backup_minute, backup_dir)) + + # Start cleanup task if enabled (waits for backup if both run at same time) + if cleanup_enabled: + wait_for_backup = ( + backup_enabled + and (backup_hour == cleanup_hour) + and (backup_minute == cleanup_minute) + ) + tg.create_task( + daily_cleanup_at( + cleanup_hour, cleanup_minute, cleanup_days, vacuum_db, wait_for_backup + ) + ) + + if not cleanup_enabled and not backup_enabled: + cleanup_logger.info("Daily cleanup and backups are both disabled by configuration.") + + +# ------------------------- +# Entry point +# ------------------------- +if __name__ == '__main__': + asyncio.run(main())