Python bot for mesh radios: Meshtastic (TCP) and MeshCore (USB serial / BLE). Connects to a device, listens for messages, runs commands/responders, and (Meshtastic only) reports packets to meshflow-api. Part of the Meshflow system alongside meshflow-api and meshflow-ui.
Protocol is selected at runtime with RADIO_PROTOCOL (meshtastic default, or meshcore). Adapters implement RadioInterface in src/radio/interface.py.
src/
├── main.py # Entry: build_radio(), StorageAPI / WS wiring
├── bot.py # MeshflowBot (protocol-agnostic core)
├── radio/ # RadioInterface + shared events/errors
├── meshtastic/ # MeshtasticRadio, TCP, translation, serializers
├── meshcore/ # MeshCoreRadio (asyncio thread), translation, dump, serializers stub
├── ws_client.py # MeshflowWSClient (Meshtastic traceroute commands)
├── data_classes.py # MeshNode, shared models
├── helpers.py
├── base_feature.py
├── commands/
├── responders/
├── api/ # StorageAPIWrapper, serializers base
└── persistence/
docs/
├── MESHTASTIC.md # Meshtastic env + behaviour
├── MESHCORE.md # MeshCore capture-only (Phase 0.3)
└── packets/ # Sample Meshtastic JSON fixtures
test/
deploy/
- MeshflowBot (
src/bot.py): RegistersRadioHandlerson the activeRadioInterface; scheduling, commands, responders, persistence, optionalStorageAPIWrapper+MeshflowWSClient. - MeshtasticRadio (
src/meshtastic/radio.py): TCP + pubsub; translates toIncomingPacket/IncomingTextMessage/NodeUpdate. - MeshCoreRadio (
src/meshcore/radio.py): Runsmeshcoreon a dedicated asyncio loop in a thread; dumps JSON todata/meshcore_packets/; Phase 0.3 no API upload. - Commands / responders: Same as before; extend
AbstractCommand/AbstractResponder. - StorageAPIWrapper: Used when
RADIO_PROTOCOL=meshtasticandSTORAGE_API_ROOTis set. - MeshflowWSClient: Started only for
RADIO_PROTOCOL=meshtasticwhen WS URL + token are configured.
- Packet ingestion: v2
POST /api/packets/{my_nodenum}/ingest/, v1/api/raw-packet/. - Node sync:
StorageAPIWrappernode endpoints. - WebSocket: remote traceroute and similar commands.
source venv/bin/activate
pip install -r requirements.txt
cp .env.example .env
python -m src.main- Unit tests:
pytest test/ --doctest-modules - CI runs on Python 3.12, 3.13, 3.14
- Python 3.12+
- meshtastic, meshcore (PyPI), Pypubsub, requests, websockets, schedule, pytest
See .env.example, docs/MESHTASTIC.md, and docs/MESHCORE.md.
- Commands:
src/commands/, register inCommandFactory. - Responders:
src/responders/, register inResponderFactory. - Use
reply_in_channel/reply_in_dmfromAbstractBaseFeature. - Meshtastic node IDs:
!+ 8 hex nibbles;my_nodenumis decimal. - MeshCore: pubkey-based ids (
mc:...);my_nodenumis0for API feeder paths (e.g. bot-version).
When asked to create a pull request description, follow the template at
.github/pull_request_template.md, and output a markdown file named tmp/PR.md
When creating a plan, Include that we should branch from the latest origin/main, do the work, commit, push, and open a PR. Use the github-personal MCP. the gh command is not available, do not use it.