WisMAP identifies potential pin conflicts between modules in RAKwireless WisBlock integrations, with a web interface and a CLI tool. Its module catalog comes from the wisblock-data repository, which is the source of truth for WisBlock module data.
Features:
- Browse all WisBlock modules with filtering by type and category (filters persist across navigation)
- Search modules by name, description, or tags (protocol, sensor type, communication, use case)
- View detailed pin mappings, documentation links, and clickable tags for any module
- Combine modules on a base board and detect pin conflicts, I2C address collisions, and signal incompatibilities
- Export combine results to Markdown or PDF
- Shareable URLs for combine configurations
- Versioned JSON API (
/api/v1/*) with interactive Swagger UI documentation at/api/v1/docs - Supports ~140 modules across all WisBlock types (Base, Core, IO, Sensor, Power, Module, Accessories)
The REST API (Flask) and the web frontend (React) have been developed with the assistance of Claude Code, Anthropic's AI coding tool.
The quickest way to run WisMAP is with Docker Compose:
git clone https://github.com/rakwireless/wismap
cd wismap
docker compose up -d --build
The web interface will be available at http://localhost:5000.
To stop the service:
docker compose down
Alternatively, using plain Docker:
docker build -t wismap .
docker run -p 5000:5000 wismap
Requirements: Python 3, Node.js, virtualenv and make.
git clone https://github.com/rakwireless/wismap
cd wismap
make init
make frontend-install
make frontend-build
make serve
This starts the Flask server on port 5000, serving both the API and the built frontend.
For local development, run the Flask API and the Vite dev server simultaneously in two terminals:
make serve
make frontend-dev
The Vite dev server proxies /api requests to Flask on port 5000 and provides hot module replacement.
data/definitions.yml (141 modules), data/config.yml (slot pin maps, slot catalogue, function
groups) and data/rules.yml (conflict rules) are committed to this repository but are not
authored here — they are verbatim copies of the dist/wismap/ artifacts generated by
wisblock-data, which authors WisBlock module facts
once and feeds every consumer from them.
.github/workflows/sync-data.yml does this on its own: daily, on manual dispatch, and whenever
wisblock-data announces a new build with a wisblock-data-updated repository dispatch. It pulls
the upstream artifacts, regenerates the contract fixtures to match, and opens a pull request on
chore/sync-wisblock-data when anything moved. Nothing lands on master without review — a
catalog change alters what the API reports.
One-time setup:
- Settings → Actions → General → "Allow GitHub Actions to create and approve pull requests", so the workflow can open the PR.
secrets.WISBLOCK_DATA_TOKEN— a token with read access to wisblock-data. Needed only while that repository is private: sharing an organisation does not let one repository'sGITHUB_TOKENread another's. Once it is public the workflows fall back to the built-in token on their own and the secret can be deleted.
Optionally set secrets.SYNC_PR_TOKEN to a PAT as well: a branch pushed with GITHUB_TOKEN
does not trigger other workflows, so without it ci.yml will not run on the sync PR.
Point WISBLOCK_DATA at a wisblock-data checkout (it defaults to ../../wisblock-data):
make sync-data # copy dist/wismap/*.yml into data/
make check-fixtures # regenerate the consumer fixtures; fails if they had gone stale
make check-data # assert data/ matches upstream
With no checkout there, tools/sync-data.sh clones upstream for the run instead, so the same
command works on a machine that has never checked wisblock-data out. Override
WISBLOCK_DATA_REPO / WISBLOCK_DATA_REF to sync from a fork or a branch.
data/UPSTREAM records which upstream commit produced the vendored files.
A wrong or missing module value is fixed upstream — in wisblock-data's
modules/<id>/{meta,hardware}.yml — then regenerated there with wbdata generate and synced
here. Editing the files under data/ directly loses the change on the next sync and puts WisMAP
out of step with the other consumers of the same data.
.github/workflows/ci.yml runs three drift guards on every pull request and push to master.
Each regenerates a committed artifact and requires that it did not move:
| Target | Guards |
|---|---|
make check-data |
data/ still matches the upstream artifacts it was vendored from |
make check-fixtures |
tests/fixtures/ still describes what the server returns |
make check-openapi |
wismap/openapi.yaml still matches the registered routes and version |
check-data skips with a notice on pull requests from forks, which get no secrets and so cannot
check out the private upstream repository.
WisMAP also provides a command-line interface for terminal usage. See CLI.md for full documentation.
Quick examples:
python wismap.py -v # show version
python wismap.py list # list all modules
python wismap.py search modbus # search modules by tag, type, or description
python wismap.py info rak12008 # show module details
python wismap.py combine rak6421 rak5802 rak5801 empty empty rak12002 rak18001
WisMAP exposes a versioned JSON API at /api/v1/*. The full contract is
machine-readable as OpenAPI 3.1:
- Interactive browser: http://localhost:5000/api/v1/docs (Swagger UI)
- Raw OpenAPI document: http://localhost:5000/api/v1/openapi.yaml
Endpoints:
| Method | Path | Description |
|---|---|---|
GET |
/api/v1/healthz |
Liveness probe |
GET |
/api/v1/cores · /cores/:id |
List / get a Core |
GET |
/api/v1/bases · /bases/:id |
List / get a Base |
GET |
/api/v1/modules · /modules/:id |
List / get a non-Core, non-Base module |
POST |
/api/v1/validate |
Validate a {core, base, slots[]} configuration; returns structured conflicts and a resolved pin map |
POST |
/api/v1/solve |
Solve which slot each requested module goes in; returns up to max_solutions ranked placements (no pin map — call /validate on the chosen one) |
Test fixtures — canonical request/response pairs for /api/v1/validate and
/api/v1/solve — live in tests/fixtures/{validate,solve}/ for
downstream-consumer CI.
POST /api/v1/validate and POST /api/v1/solve (the compute-bound endpoints)
require authentication; all discovery/read (GET) endpoints stay public.
Machine consumers send an API key as a bearer token:
curl -X POST http://localhost:5000/api/v1/validate \
-H "Authorization: Bearer <key>" \
-H "Content-Type: application/json" \
-d @request.json
Keys live in a YAML file pointed at by WISMAP_API_KEYS_FILE — one
{key, label} entry each; see data/api_keys.example.yml. Generate a key with:
python -c "import secrets; print(secrets.token_urlsafe(48))"
Keys are read once at boot, so rotation is a file edit plus a server restart.
The browser SPA needs no key: on load the server mints a wismap_session
cookie and a wismap_csrf token, and the SPA replays the CSRF token as an
X-CSRF-Token header on each /validate / /solve call.
Auth is on by default. For local development without keys, set
WISMAP_AUTH_ENABLED=false. When enabled, WISMAP_API_KEYS_FILE and
WISMAP_SECRET_KEY are required — the server refuses to start without them
(fail closed).
The following environment variables can be used to configure the server:
| Variable | Default | Description |
|---|---|---|
FLASK_DEBUG |
false |
Enable Flask debug mode (true, 1, or yes to enable) |
RATELIMIT_ENABLED |
true |
Enable API rate limiting (false to disable, e.g. during development) |
RATELIMIT_DEFAULT |
120/minute |
Default rate limit for all API endpoints |
RATELIMIT_PROXY |
60/minute |
Rate limit for GET /api/image-proxy |
RATELIMIT_STORAGE_URI |
memory:// |
Storage backend for rate limit counters (use redis://host:6379 for multi-worker deployments) |
WISMAP_AUTH_ENABLED |
true |
Gate POST /validate + /solve behind auth. Set false for local dev (no keys needed; ephemeral session secret) |
WISMAP_API_KEYS_FILE |
(none) | Path to the YAML API-keys file (a {key, label} list). Required when auth is enabled |
WISMAP_SECRET_KEY |
(none) | Secret that signs the SPA session cookie and derives CSRF tokens. Required when auth is enabled |
WISMAP_LOG_LEVEL |
INFO |
Level for the wismap.* loggers (boot key-load line, per-request auth label, denials). Set WARNING for quieter logs |
Rate limit values use the format <amount>/<period> where period is second, minute, hour, or day.
When running with Docker Compose, set these in a .env file or pass them directly:
docker compose up -d -e RATELIMIT_DEFAULT=240/minute
- Suggest which module should go to which slot
- Add dependencies between modules (i.e. RAK14011 depends on RAK14004)
There are several ways to contribute to this project. You can report bugs or ask for new features directly on GitHub. You can also submit your own new features of bug fixes via a pull request.
This project is licensed under Apache 2.0 license.