Hourly dispatch simulator for PV + battery + a hydro-cooled bitcoin miner as thermal-cascade heat source. Sized a real system.
SaferSkills independently audited hashheat-dispatch (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Hourly dispatch simulator for PV + battery + a hydro-cooled bitcoin miner used as a thermal-cascade heat source.
I'm renovating a family house and wanted the heating to run on a bitcoin miner fed exclusively by solar surplus. Every installer I asked had an opinion about whether that works. None of them had numbers. So I built the hour-by-hour model that settles the argument, used it to size my own system, and stripped out the personal data so the engine itself could be published.
A single-pass hourly engine (hashheat/dispatch.py) over parsed weather rows, configured entirely from a JSON file (hashheat/config.py). One hour of dispatch:
flowchart TD
SUN(["hourly irradiance + T2m"]) --> GEN["PV generation"]
HEAT(["heat demand<br>space + DHW"]) --> BUF["thermal buffers<br>serve first"]
BUF -- "unmet heat" --> HP["heat pump<br>heat ÷ COP"]
HP -- "electric load" --> LOADS["electric loads<br>house + EV + standby + heat pump"]
GEN --> LOADS
LOADS -- "deficit" --> DIS["battery discharge<br>to reserve floor"]
DIS -- "still short" --> IMP["grid import"]
LOADS -- "surplus" --> CHG["battery charge<br>to 100%"]
CHG -- "SOC ≥ threshold" --> MIN["miner at COP 1<br>shared hourly budget"]
MIN --> MBUF["buffers"]
MIN --> POOL["pool, up to T max"]
CHG -- "remainder" --> EXP["export"]
CHG -. "miner gated: heat pump<br>may top up buffers" .-> BUF
classDef impc fill:#b04a4a,stroke:#b04a4a,color:#ffffff
classDef expc fill:#8aa86b,stroke:#8aa86b,color:#ffffff
classDef minc fill:#caa3d6,stroke:#caa3d6,color:#1f1f1f
class IMP impc
class EXP expc
class MIN,MBUF,POOL mincThe deliberate decisions: the battery charges to full before the miner may run, so the miner can never trigger an import — that ordering is the difference between "heating with surplus" and "mining with grid power". The miner is modelled as a COP-1 heat source and nothing else; mining economics are out of scope on purpose. Thermal buffers decouple midday surplus from morning demand. The pool closes the balance: it absorbs overflow heat under a temperature cap, and in practice it is what keeps the miner running once the buffers fill. All accounting stays in one unit, electric kWh — heat-pump heat enters divided by COP. The reasoning behind each rule is in docs/METHODOLOGY.md.
Python 3.10+, standard library only for the engine and tests. matplotlib for the optional charts. No other dependencies.
Built for my own design study and published as a working reference, not as a polished installable package.
git clone <this repo> && cd hashheat-dispatch
pip install -r requirements.txt # matplotlib, charts only
# synthetic demo year (or download a real hourly series from PVGIS)
python -m hashheat make-weather --out examples/synthetic_year.csv --seed 7
python -m hashheat simulate --weather examples/synthetic_year.csv \
--config examples/config_miner_cascade.json --out examples/outputs --charts
python -m hashheat sweep-battery --weather examples/synthetic_year.csv \
--config examples/config_miner_cascade.json --caps 8,12,16,20,24
python -m unittest discover testsTo model a real site, request the hourly series for your coordinates from PVGIS and point --weather at the download. Replace the load shapes in the config with your metered curve if you have one.
Everything below comes from the committed synthetic year (seed 7, generic 37° N placeholder climate) and examples/config_miner_cascade.json — an 18 kWp array, 16 kWh battery, 5 kW hydro miner, heat pump at COP 3.5, buffers and a covered pool. Fictional house, real engine.
| Annual figure | kWh |
|---|---|
| Generation | 23,978 |
| Grid import | 1,719 |
| Grid export | 7,363 (31% of generation) |
| Miner → buffers / pool | 3,821 / 1,690 |
Switch the miner off in the same house and export rises to 45% of generation while import barely moves. That asymmetry — the miner eats export, not import — held in my real design study too, and it is the single most useful thing the model taught me.
Monthly balance
The winter week below shows why import survives: the battery is empty before sunrise on short days, and a midday heat source can't help with that. The miner (violet) only fires when a bright day fills the battery first.
Winter week dispatch
Full generated reports are in examples/outputs/.
Working prototype: it sized one real system and its figures survived an architect's review. Next, in order:
The code was written with Claude under my direction; I specified the model rules, reviewed the maths, and operate and maintain it. The weather file format follows PVGIS (© European Union) for compatibility — no PVGIS data is bundled, you download your own. The miner-as-boiler concept owes a debt to the bitcoin mining heat-reuse literature, in particular Braiins' Bitcoin Mining Heat Reuse book. All examples are synthetic; no real location, consumption data, or hardware inventory appears in this repository. This is a design-exploration tool — it does not replace the licensed engineer who signs your installation project, and the payback sweep is not financial advice.
Built by David Sanz Cañal.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.