Fit bulky power adapters on the outlet strip you already own.
WallWart is an offline exact solver for the case where a strip has enough sockets and watts on paper, but several wall-wart bodies block each other in reality. Describe outlet anchors, adapter bodies, off-centre prongs, allowed rotations, keep-out regions, and a watt budget. WallWart finds the best legal layout or proves that the required devices cannot coexist.
There is no account, browser UI, telemetry, cloud solver, or runtime dependency. The useful output is a machine-verifiable plan plus a printable SVG and a script-free HTML report.
- Rotates each adapter around its real, possibly off-centre prong anchor.
- Respects outlet orientation, allowed quarter turns, blocked switches/ports, minimum clearance, one-device-per-outlet, and total watts.
- Places required devices, then maximizes optional priority and count.
- Uses bounded exact branch-and-bound; successful plans are optimal.
- Produces deterministic
plan.json,layout.svg, andreport.htmlfiles. - Re-verifies receipts by reconstructing geometry and rerunning optimization.
WallWart does not certify electrical safety. Measure your own hardware, follow manufacturer ratings, and never daisy-chain strips.
Requires Python 3.11+ and uv.
git clone https://github.com/KanadeK/wallwart.git
cd wallwart
uv sync --locked --all-extras --cache-dir .uv-cache
uv run --cache-dir .uv-cache --locked --no-sync wallwart validate examples/studio-desk.json
uv run --cache-dir .uv-cache --locked --no-sync wallwart plan examples/studio-desk.json --output-dir studio-plan
uv run --cache-dir .uv-cache --locked --no-sync wallwart verify examples/studio-desk.json studio-plan/plan.jsonExpected final lines:
VALID: Harbor synth desk · 4 outlets · 3 devices
PLANNED: 3 devices · 110 W · studio-plan
VERIFIED: studio-plan/plan.json
The planner refuses to overwrite studio-plan. Use a new destination or
remove an old result only after confirming it is yours.
The complete example is examples/studio-desk.json. Measurements are millimetres; angles are quarter turns.
{
"schema_version": 1,
"name": "Tiny desk",
"workspace": {"width_mm": 220, "height_mm": 120},
"strip": {
"x_mm": 10,
"y_mm": 35,
"width_mm": 200,
"height_mm": 50,
"max_watts": 100
},
"minimum_gap_mm": 4,
"outlets": [
{"id": "A1", "x_mm": 60, "y_mm": 60, "rotation_deg": 0},
{"id": "A2", "x_mm": 150, "y_mm": 60, "rotation_deg": 0}
],
"blocked_regions": [],
"devices": [
{
"id": "router",
"label": "Router",
"watts": 30,
"required": true,
"priority": 0,
"plug": {
"width_mm": 36,
"height_mm": 28,
"anchor_x_mm": 10,
"anchor_y_mm": 14,
"allowed_turns_deg": [0, 180]
}
}
]
}Unknown fields, duplicate IDs, booleans used as numbers, non-finite numbers, non-quarter rotations, and geometry outside the workspace fail fast. Inputs are limited to 1 MiB, 14 devices, and 16 outlets so exact search remains a clear public contract.
wallwart validate SCENARIO
wallwart plan SCENARIO --output-dir DIRECTORY
wallwart verify SCENARIO PLAN_JSON
wallwart --version
- Exit
0: validation, optimal planning, or verification succeeded. - Exit
1: a valid scenario is infeasible, or a receipt is invalid. - Exit
2: usage, JSON, schema, input file, or output path is invalid.
Planning validates and solves before creating the destination. A failed or infeasible run leaves no partial result directory.
WallWart rotates every adapter around its declared plug anchor and rejects placements outside the workspace or over a keep-out region. It then searches compatible combinations. The objective is:
- place every required device;
- maximize the sum of placed optional priorities;
- maximize the number of placed optional devices; and
- choose the smallest
(device, outlet, rotation)signature.
The fixed limits favor trustworthy exact answers over silently returning a guess for an arbitrarily large job.
- power-budget-choice.json demonstrates a real priority trade-off: the router and synth win over the lower-priority phone charger.
- impossible-required.json has two wide
required adapters that cannot coexist;
planexits1and creates no output.
uv run --cache-dir .uv-cache --locked --no-sync python scripts/check.pyThis runs a locked sync, Ruff, strict mypy, pytest with at least 90% branch coverage, dependency audit, wheel/sdist builds, a deterministic demo ZIP, and a clean temporary wheel installation followed by plan/verify acceptance.
Individual commands:
uv run --cache-dir .uv-cache --locked --no-sync ruff check .
uv run --cache-dir .uv-cache --locked --no-sync ruff format --check .
uv run --cache-dir .uv-cache --locked --no-sync mypy
uv run --cache-dir .uv-cache --locked --no-sync pytest --basetemp .pytest-tmp
uv build --cache-dir .uv-cache --out-dir dist
uv run --cache-dir .uv-cache --locked --no-sync pip-auditCI runs the full gate on Windows and Linux. Releases contain the wheel, source distribution, and a demo ZIP with input and verified output.
| Symptom | Meaning | Repair |
|---|---|---|
Failed to initialize cache |
The global uv cache is unusable. | Keep --cache-dir .uv-cache, or set UV_CACHE_DIR to a writable project-local directory. |
ERROR: ... invalid JSON or a field path |
Input broke the strict schema. | Run validate, fix the first named field, and retry. |
required_power_exceeds_limit |
Required watts exceed the declared budget. | Correct measured watts/budget or make a genuinely optional device optional. |
required_devices_cannot_coexist |
Required bodies collide or outlets are insufficient. | Recheck body/anchor measurements, turns, gap, and keep-outs. |
output path already exists |
WallWart protected an earlier plan. | Choose a new path, or manually remove only the result you intend to replace. |
pip-audit cannot reach its source |
The security gate is incomplete. | Restore network and rerun the audit; do not waive the gate. |
| Installed-wheel smoke test fails | Built artifact differs from source. | Remove only dist/, rerun the uv-based release gate above, and inspect the first failing command. |
If a test fails, rerun that exact command without -q before changing code.
The suite includes an independent brute-force oracle, so optimizer mismatches
are correctness defects, not flaky assertions.
MIT licensed.