TCP based bot that monitors Meshtastic channels and responds to various commands.
Check-Mate provides a variety of useful services to your Meshtastic mesh network:
- Radio Checks: Verify signal quality with other nodes
- Network Status: Get visibility into your mesh network topology
- Weather Information: Access current weather conditions and alerts
- Signal Reports: Detailed signal metrics including RSSI and SNR
The bot connects to a Meshtastic node on your network and responds to commands sent on any channel.
- Python 3.7+
- Meshtastic node connected via WiFi to the same network as the machine running this program.
Make sure Python 3.7+ is installed, then clone the repo:
git clone git@github.com:dpup/meshtastic-check-mate
cd meshtastic-check-mateThe simplest way to set up the project is using the provided Makefile:
# Create virtualenv and install dependencies
make setup
# Install the package in development mode
make developAlternatively, you can install manually:
# Create a virtual environment
python3 -m venv venv
source venv/bin/activate
# Install the package
pip install -e .
# For development, install with extra dev dependencies
pip install -e ".[dev]"# Start the application
make run HOST=meshtastic.local LOCATION="Base Camp" \
LATITUDE=40.7128 LONGITUDE=-74.0060 WEATHER_API_KEY=your_api_key_here
# Check status
make status
# Run tests
make test
# Run linting
make lint
# Clean up
make cleanOnce installed, you can run Check-Mate directly:
# As a module
python -m checkmate.main --host meshtastic.local --location 'Base Camp' \
--latitude 40.7128 --longitude -74.0060 --weather-api-key your_api_key_here
# Or using the installed script
check-mate --host meshtastic.local --location 'Base Camp' \
--latitude 40.7128 --longitude -74.0060 --weather-api-key your_api_key_hereCheck-Mate responds to the following commands on any channel except the default channel (channel 0):
Triggers a signal quality report. The bot will respond with a message indicating how well it's receiving your signal.
Example:
Outrider (a4bc) : Radio check
Base camp (ffea) : Copy a4bc, 5.75 SNR from Everest Base Camp
Responses are randomized and vary based on signal quality.
Displays visible nodes in your net, grouped by hop distance. Only shows nodes that have been seen in the last 3 hours.
Example:
Outrider (a4bc) : ?net
Base camp (ffea) : Net report! In the last 3hrs:
- 0 hops x 3
- 1 hop x 5
- 2 hops x 2
Returns signal information with hop count, signal strength (RSSI), and signal-to-noise ratio (SNR).
Example:
Outrider (a4bc) : ?check
Base camp (ffea) : copy from 2 hops away with -85Db and 58Db SNR
Provides current weather information for the node's location. Requires an OpenWeatherMap API key and location coordinates. The location can be obtained either from command-line arguments or automatically from the node's position data.
Example:
Outrider (a4bc) : ?weather
Base camp (ffea) : Weather for (37° 46.49′ N, 122° 25.17′ W):
Broken clouds, 14.7°C (feels like 14.3°C)
Humidity 80%, Wind 8.2m
⚠️ Beach Hazards
Provides detailed information about active weather alerts for the node's location. Requires an OpenWeatherMap API key and location coordinates. Sends multiple messages to avoid exceeding the maximum message size, with each alert broken down into manageable chunks with continuity indicators (n/m).
Example:
Outrider (a4bc) : ?alerts
Base camp (ffea) : Weather Alerts for (37° 46.49′ N, 122° 25.17′ W): 1 active alert
Base camp (ffea) : ALERT 1/1: Small Craft Advisory
From: NWS San Francisco Bay Area (1/5)
Base camp (ffea) : ...SMALL CRAFT ADVISORY REMAINS IN EFFECT FROM 3 PM THIS AFTERNOON TO 9 PM PDT FRIDAY... (2/5)
Base camp (ffea) : * WHAT...Northwest winds 15 to 25 kt with gusts up to 30 kt expected. (3/5)
Base camp (ffea) : * WHERE...Coastal waters from Point Pinos to Point Piedras Blancas. (4/5)
Base camp (ffea) : * WHEN...From 3 PM this afternoon to 9 PM PDT Friday. * IMPACTS...Conditions will be hazardous to small craft. (5/5)
Messages are sent with a 2-second delay between them to avoid network saturation.
Displays a help message with available commands and basic usage information.
Shows a list of currently configured scheduled messages, including their timing, timezone, and target channels. This command is only available when scheduled messages are configured.
Check-Mate can send automated messages at scheduled times. This feature is useful for regular announcements, net reminders, or periodic information broadcasts.
Scheduled messages are configured using the --messages command line argument or the SCHEDULED_MESSAGES environment variable:
# Using command line argument
make run HOST=meshtastic.local \
--messages "Monday;18:45;America/Los_Angeles;1;Reminder: practice net starts in 15 minutes"
# Using environment variable
SCHEDULED_MESSAGES="Monday;18:45;America/Los_Angeles;1;Reminder: practice net starts in 15 minutes" \
make run HOST=meshtastic.local
# Multiple messages (separate with triple semicolons, newlines, or multiple --messages flags)
SCHEDULED_MESSAGES="Monday;18:45;America/Los_Angeles;1;Net reminder;;;Tuesday,Thursday;20:00;UTC;2;Weekly check-in time" \
make run HOST=meshtastic.localEach scheduled message follows this format: Day(s);Time;Timezone;ChannelIndex;Message
- Day(s): Day of the week when the message should be sent
- Single day:
Monday,Tuesday, etc. - Multiple days:
Monday,Wednesday,Friday(comma-separated) - Supports full names (
Monday) or abbreviations (Mon)
- Single day:
- Time: 24-hour format
HH:MM(e.g.,18:45,07:30) - Timezone: IANA timezone identifier (e.g.,
America/Los_Angeles,Europe/London,UTC) - ChannelIndex: Numeric channel index (e.g.,
0for primary,1,2, etc.) - Message: The text content to send
When specifying multiple messages, you can use any of these delimiters (in order of priority):
- Triple semicolons (
;;;) - Primary delimiter, works well in environment variables --messagesseparator - Secondary delimiter for command-line usage- Newlines - Fallback delimiter for multi-line strings
# Weekly net reminder every Monday at 6:45 PM Pacific Time on channel 1
--messages "Monday;18:45;America/Los_Angeles;1;Reminder: GRMS practice net starts in 15 minutes"
# Multiple messages for different events across timezones (using triple semicolons)
--messages "Monday;18:45;America/Los_Angeles;1;West Coast practice net reminder;;;Sunday;19:00;America/New_York;2;East Coast emergency communications check;;;Tuesday;14:30;Europe/London;0;UK afternoon check-in"
# Weekday morning announcement in UTC on primary channel
--messages "Monday,Tuesday,Wednesday,Thursday,Friday;08:00;UTC;0;Good morning! Weather update at noon."
# Multiple days with different timezones on channel 3
--messages "Monday,Wednesday,Friday;09:00;America/Chicago;3;Midwest morning net"
# Using triple semicolons in environment variables (easier for complex configurations)
SCHEDULED_MESSAGES="Monday;18:45;America/Los_Angeles;1;West Coast net;;;Tuesday;19:00;America/New_York;1;East Coast net;;;Sunday;20:00;UTC;2;International check-in"- Timing: Messages are checked every 30 seconds and sent when the current time matches the scheduled time
- Precision: Messages are sent within 30 seconds of the scheduled time
- Duplicate Prevention: Each message is sent only once per day, even if the service restarts
- Daily Reset: The duplicate prevention resets at midnight each day
Warning
Important Notes about Service Restarts:
Missed Messages: If the service is down during a scheduled time, that message will be skipped for that day Duplicate Messages: If the service restarts within the same minute as a scheduled message, it may send the message again
Best Practices:
- Deploy Check-Mate with automatic restart capabilities (e.g., systemd, Docker restart policies, or ECS)
- Monitor service health to ensure scheduled messages are being sent
- Consider using health check URLs to track service availability
- Test scheduled messages during off-peak hours first
On any Meshtastic node in your mesh network, send any of the supported commands on a non-default channel (any channel except channel 0).
| Arg | Env | Description |
|---|---|---|
| -h | N/A | Show help |
| --host | HOST | The IP or hostname of the meshtastic node, e.g. 192.168.5.10 |
| --location | LOCATION | Text description of where your node is, e.g. SF Mission District |
| --healthcheck | HEALTHCHECKURL | URL to send healthcheck pings to when receiving messages |
| --status | N/A | Print JSON of latest status |
| --status-dir | STATUS_DIR | Override where the status file is located (see below) |
| --latitude | LATITUDE | Latitude for location services (e.g. weather) |
| --longitude | LONGITUDE | Longitude for location services (e.g. weather) |
| --weather-api-key | WEATHER_API_KEY | API key for OpenWeatherMap |
| --messages | SCHEDULED_MESSAGES | Scheduled messages in format: 'Day(s);Time;Timezone;ChannelIndex;Message' |
Check-Mate can be run using Docker and Docker Compose:
# Build and start with docker-compose
HOST=meshtastic.local LOCATION="Base Camp" \
LATITUDE=40.7128 LONGITUDE=-74.0060 \
WEATHER_API_KEY=your_api_key_here \
docker-compose up -d
# Check status
docker-compose exec check-mate python -m checkmate.main --statusECS does not use Docker healthchecks directly and the healthcheck runs as a different user than the appuser specified in the Dockerfile. To get around this, set the STATUS_DIR environment variable to /tmp then add the following healthcheck to the container definition (what follows is a terraform snippet):
healthCheck = {
command = ["CMD-SHELL", "cd /app && python3 -m checkmate.main --status --status-dir=/tmp"]
interval = 60
timeout = 10
retries = 3
startPeriod = 60
}(These healthchecks and restarts seem to be pretty important, because the underlying meshtastic client can get in a bad state that doesn't trigger the disconnect pubsub topic.)
check-mate/
├── .github/ # GitHub configuration
│ └── workflows/ # GitHub Actions workflows
├── src/ # Source code
│ └── checkmate/ # Main package
│ ├── __init__.py
│ ├── main.py # Main entry point
│ ├── status.py # Status management
│ ├── quality.py # Signal quality
│ ├── packet_utils.py
│ ├── constants.py
│ └── responders/ # Message responders
│ ├── __init__.py
│ ├── base.py
│ ├── radiocheck.py
│ ├── netstat.py
│ └── ...
├── tests/ # Test code
├── Makefile # Build and development commands
├── Dockerfile # Standard container definition
└── ... # Other supporting files and configurations
The project uses GitHub Actions for Continuous Integration and Deployment:
- PR Validation: Each pull request runs tests and linting on multiple Python versions
- CI/CD Pipeline: Runs on main branch, performs tests and builds/pushes Docker images tagged as
latest - Publish Docker Image: Builds and pushes Docker images tagged as
releasewhen code is pushed to release branch
Docker images are available at: ghcr.io/meshtastic/check-mate with tags:
latest- latest development version (from main branch)release- stable release version (from release branch)
For more details, see .github/CICD.md
Improvements and enhancements welcome. If you find issues or spot possible improvements, please submit a pull-request or file an issue.
- Fork the repository
- Create a feature branch
- Make your changes
- Run tests and linting locally with
make testandmake lint - Submit a pull request
- CI will automatically run tests and linting on your PR
I’ve set up a geographically distributed mesh. Each location has a Meshtastic node connected to the Internet via WiFi and MQTT. Check-Mate instances monitor each node through AWS ECS and Tailscale.
- Tailscale bridges AWS and physical locations.
- Each location has its own local Meshtastic mesh with multiple nodes.
- A gateway node at each location connects to the internet with a fixed IP and MQTT configuration.
- Tailscale allows ECS to access the gateway node.
- Two Check-Mate instances run in AWS ECS (Elastic Container Service), each monitoring a different Meshtastic node.
- ECS ensures automatic restarts if an instance becomes unresponsive.
- Container logs are sent to CloudWatch for monitoring and troubleshooting.
- Several non-default channels are set up for different purposes.
- Both Check-Mate instances monitor these channels.
- MQTT bridges the physical meshes, creating shared private channels (as long as the internet is up).
The ECS deployment ensures Check-Mate instances run continuously with minimal maintenance. Health checks automatically restart containers if they become unresponsive, which can happen with long-running Python MQTT clients.