diff --git a/.claude/skills/release/SKILL.md b/.claude/skills/release/SKILL.md new file mode 100644 index 00000000..2dd36b91 --- /dev/null +++ b/.claude/skills/release/SKILL.md @@ -0,0 +1,87 @@ +--- +name: release +description: Cut a new batcontrol release. Use when asked to prepare, create, or finalize a release, bump the version for a release, or write release notes. Covers the prepare-release workflow, release notes, tagging, and the follow-up steps (next dev version, HA add-on promotion). +--- + +# Release batcontrol + +Versioning: `X.Y.Zdev` during development, `X.Y.Z` for releases. The version lives in +`src/batcontrol/__pkginfo__.py` (`__version__`) and is mirrored in +`pyproject.toml` `[tool.bumpversion] current_version`. All version changes go through +`bump-my-version` (config in `pyproject.toml`) — never edit the version by hand, the two +locations would drift. + +Execution policy: run the preparation steps directly, but STOP and ask for confirmation +before every irreversible/outward-facing action — triggering the Prepare Release workflow, +pushing a tag, and publishing the GitHub release. + +## Step 1 — Preconditions (check, do not skip) + +1. Current version is a `dev` version (`grep __version__ src/batcontrol/__pkginfo__.py`). +2. `main` is green: latest runs of the `pytest` and `pylint` workflows passed. +3. All PRs intended for the release are merged; nothing release-critical open. +4. `./run_tests.sh` passes locally. +5. Docs for new features exist under `docs/` (they publish independently via `docs.yml`). + +## Step 2 — Draft the release notes + +The Prepare Release workflow creates the GitHub release body with a placeholder — the notes +are written by hand. Draft them now: + +1. Find the previous release tag: `git tag --sort=-v:refname | head -1` (or the GitHub + releases page if the local clone has no tags). +2. Collect merged changes since then: `git log ..main --oneline --merges` or the + GitHub compare view; group by category the way the HA add-on changelog does + (Major New Features / Enhancements / Technical Updates / Breaking Changes), one bullet + per change with PR number `(#NNN)`. +3. Keep the draft ready — it is pasted into the draft release in Step 4. + +## Step 3 — Run the Prepare Release workflow [CONFIRM FIRST] + +Preferred path — trigger `prepare-release.yml` (workflow_dispatch, on `main`) via the GitHub +MCP actions tools or the GitHub UI (Actions -> "Prepare Release" -> Run workflow). It: + +- runs `bump-my-version bump release --commit` (drops the `dev` suffix, e.g. + `0.8.1dev` -> `0.8.1`), +- builds wheel + sdist with `uv build --no-sources`, +- creates a **draft** GitHub release with tag `X.Y.Z` and both artifacts attached, +- opens a PR `prepare-release-` -> `main` with the version bump commit. + +Fallback (workflow unavailable): do the same steps locally on a branch — +`uv pip install bump-my-version && bump-my-version bump release --commit`, `uv build +--no-sources .`, push branch, open PR, create the draft release with the artifacts manually. + +## Step 4 — Finalize [CONFIRM before tag push and publish] + +1. Review and merge the release PR into `main`. +2. Tag the merged commit: `git tag X.Y.Z && git push origin X.Y.Z`. + The tag push triggers `docker-image.yml` (multi-arch Docker build). +3. Paste the release notes from Step 2 into the draft release, verify the wheel asset is + named exactly `batcontrol-X.Y.Z-py3-none-any.whl` (the HA add-on Dockerfile downloads it + under that name), then publish the release. +4. Verify the Docker image workflow run succeeds. + +## Step 5 — Open the next development cycle + +Bump `main` to the next dev version: trigger the `Bump version` workflow +(`bump_version.yml`) with bump-type `patch` — bump-my-version rolls `X.Y.Z` to +`X.Y.(Z+1)dev` and opens a `bump-version/...` PR. Merge it. + +## Step 6 — Promote into the Home Assistant add-on + +The stable HA add-on (`MaStr/batcontrol_ha_addon`, directory `batcontrol/`) pins its +`version` to a release tag and downloads that release wheel. Switch to that repo and run its +`release-addon` skill to promote this release (options/schema sync, changelog, version). + +## Checklist + +``` +[ ] main green, tests pass locally, dev version confirmed +[ ] Release notes drafted (PRs since last tag, categorized) +[ ] Prepare Release workflow run (confirmed by user) +[ ] Release PR merged +[ ] Tag X.Y.Z pushed (confirmed by user) -> Docker build green +[ ] Draft release: notes added, wheel asset name verified, published (confirmed by user) +[ ] Next dev version bumped on main (bump-type: patch) +[ ] HA add-on promotion done (release-addon skill in MaStr/batcontrol_ha_addon) +``` diff --git a/CLAUDE.md b/CLAUDE.md index c420b06c..888645b5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,6 +1,20 @@ # CLAUDE.md — batcontrol -Primary contribution guidelines: [`.github/copilot-instructions.md`](.github/copilot-instructions.md) +batcontrol charges a home battery when grid prices are cheap and preserves it for expensive +hours, based on dynamic tariffs, solar forecast, and consumption forecast. Python 3.9-3.13 +(primary target: 3.11). Full contribution guidelines: +[`.github/copilot-instructions.md`](.github/copilot-instructions.md) + +## Commands + +```bash +./run_tests.sh # full suite + coverage (creates .venv via uv) +uv venv --python 3.13 --allow-existing # setup only +uv pip install -e '.[test]' pylint autopep8 # lint tools are not part of the test extras +uv run pytest tests/ -k # single test / subset +uv run pylint src/batcontrol # target score >= 9.0 (10 if achievable) +uv run autopep8 --in-place # PEP8 formatting +``` ## Module Map @@ -16,20 +30,57 @@ src/batcontrol/ scheduler.py # Main loop mqtt_api.py # State publishing + runtime config overrides evcc_api.py # evcc integration +config/batcontrol_config_dummy.yaml # Reference config — every parameter documented here +tests/ # pytest suite (mirrors package layout) +docs/ # MkDocs user docs -> https://mastr.github.io/batcontrol/ +scripts/ # Standalone verification/helper scripts (committed) +tmp/ # Throwaway experiments — NEVER committed ``` ## Architecture -- **Logic types:** `default` (price-based) and `next` (price-based + peak shaving), selected via `battery_control.type` in config. -- **Factory pattern:** inverter, tariff, and forecast providers all use `*_interface.py` base classes with a factory in `.py`. -- **Expert tuning:** `battery_control_expert` config block sets attributes directly on logic instances. -- **MQTT API:** publishes state and accepts runtime overrides (min/max SoC, charge rate) via retained topics. -- **Interval resolution:** 15-minute internally — see `interval_utils.py` and `docs/15-min-transform.md`. +- **Logic types:** `default` (price-based) and `next` (price-based + peak shaving), selected via + `battery_control.type` in config. +- **Factory pattern:** inverter, tariff, and forecast providers all use `*_interface.py` base + classes with a factory in `.py`. New providers: implement the interface, register in + the factory, add config keys. +- **Expert tuning:** `battery_control_expert` config block sets attributes directly on logic + instances. +- **MQTT API:** publishes state and accepts runtime overrides (min/max SoC, charge rate) via + retained topics. +- **Interval resolution:** 15-minute internally — see `interval_utils.py` and + `docs/development/15-min-transform.md`. + +## Change Checklist + +1. New/changed config parameter -> add it to `config/batcontrol_config_dummy.yaml` with an + explanatory comment. +2. New functionality -> add pytest in `tests/`; bug fix -> add a regression test for the bug. +3. User-facing behavior -> update or add a page under `docs/` and register new pages in + `mkdocs.yml`. +4. Run `./run_tests.sh` and pylint before committing. +5. Config parameters must also be mirrored into the Home Assistant add-on repo + (`MaStr/batcontrol_ha_addon`: `options:` + `schema:` in the add-on `config.yaml`). That repo + ships a `port-batcontrol-change` skill which automates the steps. + +## Releasing + +Use the `release` skill (`.claude/skills/release/SKILL.md`). Short version: versions are +managed by bump-my-version (`pyproject.toml` + `src/batcontrol/__pkginfo__.py`, never edit by +hand); the `Prepare Release` workflow drops the `dev` suffix, builds the wheel, and creates a +draft GitHub release + version-bump PR; the pushed tag triggers the Docker build; afterwards +`main` is bumped to the next `dev` version and the release is promoted into the HA add-on repo +(`release-addon` skill there). ## Known Pitfalls -- ASCII-only in source code — no umlauts, special chars, emoji, even in log messages. Does not apply to documentation in `docs/`. +- ASCII-only in source code — no umlauts, special chars, emoji, even in log messages. Does not + apply to documentation in `docs/`. - Peak shaving config is nested inside calculation parameters (not top-level). - `§14a EnWG` dynamic network fees live in `dynamictariff/network_fees.py`. - `resilient_wrapper.py` wraps inverter calls — test with the wrapper, not the raw backend. - Never commit anything from `tmp/`. +- The HA add-on Dockerfiles in `MaStr/batcontrol_ha_addon` copy `entrypoint_ha.sh`, + `config/load_profile_default.csv`, and the `config/` folder from this repo by path — renaming + or moving these files breaks the add-on build. +- Branch names: `copilot/feature-name` or `copilot/bugfix-name` (unless the harness assigns one).