- Python 79.1%
- HTML 20.5%
- Dockerfile 0.4%
|
|
||
|---|---|---|
| .forgejo/workflows | ||
| bot | ||
| bridge | ||
| dashboard | ||
| docs | ||
| .gitignore | ||
| CHANGELOG.md | ||
| config.example.yaml | ||
| docker-compose.yml | ||
| Dockerfile | ||
| main.py | ||
| README.en.md | ||
| README.md | ||
| requirements.txt | ||
| ruff.toml | ||
| VERSION | ||
meshcore-rtfmc-bridge
Integration bridge between Remote-Terminal-for-MeshCore (RTfMC) and a custom web dashboard + bot.
RTfMC handles the direct radio communication with the MeshCore device. This bridge consumes the RTfMC REST API and WebSocket stream to provide:
- a web dashboard (aiohttp, port 8880) with live repeater telemetry, contact list and message stream
- a bot handler that responds to
!commandsin DMs and channel messages - a telemetry poller that periodically fetches repeater status and optionally publishes it via MQTT
Architecture
MeshCore Radio (USB/TCP)
│
▼
┌─────────────────────────────┐
│ Remote-Terminal-for-MeshCore│ port 8765
│ (RTfMC – external service) │
│ │
│ REST API /contacts │
│ /messages │
│ /channels │
│ /repeaters │
│ /statistics │
│ WebSocket /ws │
└──────────────┬──────────────┘
│ HTTP + WS
▼
┌─────────────────────────────────────────────────────┐
│ meshcore-rtfmc-bridge │
│ │
│ bridge/rtfmc_client.py ← async REST + WS client │
│ bridge/state.py ← shared in-memory state │
│ bridge/telemetry_poller.py ← periodic poll │
│ │
│ bot/bot_handler.py ← !command handler │
│ │
│ dashboard/dashboard.py ← aiohttp web UI + API │
│ dashboard/templates/ ← HTML frontend │
└──────┬──────────────────────────────┬───────────────┘
│ WS push │ HTTP REST
▼ ▼
Browser dashboard bot responses
(port 8880) (via RTfMC API)
Quickstart
cp config.example.yaml config.yaml
# edit config.yaml: set RTfMC URL, dashboard port, admin keys
python -m venv venv && source venv/bin/activate
pip install -r requirements.txt
python main.py
# → http://localhost:8880
Docker / Portainer
cp config.example.yaml config.yaml
# edit config.yaml, then:
docker compose up -d
If RTfMC runs on the same host outside Docker, set the URL in config.yaml:
rtfmc:
url: "http://host.docker.internal:8765"
host.docker.internal is mapped via extra_hosts in the Compose file.
Configuration
All settings live in config.yaml (copy from config.example.yaml):
rtfmc:
url: "http://localhost:8765" # RTfMC backend URL
password: "" # leave empty if not set
telemetry:
interval_seconds: 300 # how often to poll repeater status
dashboard:
host: "0.0.0.0"
port: 8880
bot:
admin_keys:
- "<your-64-char-public-key>" # hex string; grants access to privileged commands
Bot Commands
Send these as a DM or channel flood message to your own node:
| Command | Arguments | Description |
|---|---|---|
!help |
– | List available commands |
!ping |
– | Responds with pong |
!status |
– | Radio status and network summary |
!nodes |
– | All known nodes (up to 20) |
!repeater |
[name] |
Telemetry for a repeater; without argument: first repeater in state |
!advert |
– | Trigger a manual advert (admin keys only) |
Rate limit: 10 seconds per sender / channel.
Dashboard API
The bridge exposes its own REST API and WebSocket on port 8880:
| Endpoint | Description |
|---|---|
GET /api/summary |
Network overview (contact counts, radio status) |
GET /api/contacts?type=repeater |
Contact list, optional type filter |
GET /api/messages?limit=50 |
In-memory message buffer |
GET /api/telemetry |
Cached repeater telemetry |
GET /api/statistics |
Packet statistics forwarded from RTfMC |
POST /api/repeater/{key}/status |
Trigger immediate telemetry fetch |
WS /ws |
Live events: contact, message, telemetry, health |
Project Structure
meshcore-rtfmc-bridge/
├── main.py # entry point
├── config.example.yaml # configuration template
├── requirements.txt # aiohttp, pyyaml
├── VERSION # current version (SemVer)
├── CHANGELOG.md # per-version release notes
├── Dockerfile
├── docker-compose.yml
│
├── bridge/
│ ├── rtfmc_client.py # async REST + WS client
│ ├── state.py # shared in-memory state singleton
│ └── telemetry_poller.py # background repeater poll task
│
├── bot/
│ └── bot_handler.py # !command parser and responder
│
├── dashboard/
│ ├── dashboard.py # aiohttp routes, WS broadcast
│ └── templates/index.html # dark-mode single-page dashboard
│
├── docs/
│ ├── architecture.md # system design and data flow
│ ├── api-reference.md # full REST, WS and Python API docs
│ ├── deployment.md # local, Docker, Portainer, systemd, Caddy
│ └── versioning.md # SemVer strategy and release workflow
│
└── .forgejo/workflows/
├── ci.yml # lint + syntax check on push / PR
├── bump-version.yml # manual SemVer bump (ppfeiffer only)
└── release.yml # auto-release on version tags
Versioning
This project uses Semantic Versioning (MAJOR.MINOR.PATCH).
| Level | When |
|---|---|
PATCH |
Bug fixes, no API changes |
MINOR |
New features, backwards compatible |
MAJOR |
Breaking changes |
Releases are created exclusively by the repository owner via the bump-version Forgejo Actions workflow (Actions → bump-version → Run workflow). The workflow updates VERSION, appends a section to CHANGELOG.md, commits both, creates an annotated tag, and triggers the release workflow which publishes a Forgejo release with changelog notes and a source archive.
See docs/versioning.md for the full guide.
CI
Every push and pull request runs the CI workflow:
ruff check– style and import lintingpyflakes– import error detection- YAML validation of
config.example.yaml py_compileof all Python modules
Security Note
RTfMC should not be exposed to the public internet directly (the built-in bot code execution uses exec() and carries RCE risk). Place Caddy or another reverse proxy with authentication in front, or restrict RTfMC to loopback only.
Documentation
License
See repository for license details.