This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Predbat is a Home Assistant AppDaemon app that predicts and optimizes home battery charging/discharging based on electricity rates, solar forecasts, and historical load data. It supports inverters from GivEnergy, Solis, Huawei, SolarEdge, and Sofar, and integrates with energy providers like Octopus Energy, Kraken (EDF/E.ON), and Axle Energy VPP.
See .github/copilot-instructions.md for more details.
Tests live in apps/predbat/tests/ and are run from the coverage/ directory:
# First-time setup (creates venv and installs deps):
cd coverage
source setup.csh
# Run all tests:
./run_all
# Skip slow tests (used by CI):
./run_all --quick
# Run a specific test by name:
./run_all --test basic_rates
# Run multiple specific tests:
./run_all --test basic_rates --test units
# Run tests matching a keyword:
./run_all -k octopus_
# List all available test names:
./run_all --list
# Coverage analysis:
./run_cov --quick
# Then open htmlcov/index.htmlThe run_all script is a thin wrapper; you can run unit_test.py directly from the coverage/ directory (it needs to be the working directory so relative paths resolve).
All checks are enforced via pre-commit and must pass before merging:
./run_pre_commitKey constraints:
- Line length: 256 chars (Black), 250 chars (Flake8)
- Docstrings: 100% coverage required (
interrogate) for all functions and classes - Spell checking: British English (
en-gb) via CSpell; add valid unknown words to.cspell/custom-dictionary-workspace.txt(file is auto-sorted alphabetically on commit, so re-stage after running pre-commit) - Variable naming:
lower_case_with_underscores - pre-commit.ci will auto-commit fixable issues (trailing whitespace, etc.) back to your PR branch — run
git pullafter pushing to avoid divergence
PredBat in predbat.py is the main class and uses multiple inheritance to compose its behaviour:
class PredBat(hass.Hass, Octopus, Energidataservice, Fetch, Plan, Execute, Output, UserInterface):The main loop (update_pred()) runs every 5 minutes: fetch data → run optimization → execute plan → publish results.
| Module | Role |
|---|---|
plan.py |
Optimization engine — multi-threaded search across thousands of charge/discharge window scenarios |
predict.py / prediction.py |
Battery SOC prediction models, PV generation, load forecasting |
fetch.py |
Pulls PV forecasts, historical load, rate data, and inverter state |
execute.py |
Sends charge/discharge/reserve commands to inverters |
output.py |
Creates and updates Home Assistant sensors, switches, selects |
inverter.py |
Multi-inverter abstraction layer (GivEnergy, Solis, Huawei, SolarEdge, Sofar) |
config.py |
Defines CONFIG_ITEMS (all user settings) and APPS_SCHEMA (YAML validation) |
ha.py |
WebSocket + REST communication with Home Assistant |
userinterface.py |
Manages HA input entities (switches, selects, input_numbers) |
components.py |
Plugin registry and component lifecycle management |
component_base.py |
Abstract base class for all pluggable components |
components.py defines a registry of 18 pluggable components (DB, HA, Web, MCP, GECloud, Octopus, Fox, Solax, Solis, Axle, Ohme, Kraken, etc.). Each component:
- Inherits from
ComponentBase - Has
api_start()/api_stop()lifecycle methods - Can be independently enabled/disabled
- Has health monitoring with exponential backoff
- Routes HA events via entity prefix filtering
Fetchretrieves rates (Octopus/Kraken API), solar forecasts (Solcast), historical load (HA history), and live inverter statePlanruns a search algorithm to find the optimal set of charge/discharge windows over a 48-hour horizonExecutesends the resulting commands to the inverterOutputpublishes the plan and metrics as HA sensor states
unit_test.py uses TestHAInterface (from tests/test_infra.py) to mock the Home Assistant connection. Tests call create_predbat() which builds a full PredBat instance against the mock. Individual test modules in tests/ follow the naming convention test_<feature>.py with an exported run_<feature>_tests() or test_<feature>() function registered in TEST_REGISTRY in unit_test.py.
Documentation source lives in docs/ and is built with MkDocs:
mkdocs serve # Live preview at http://localhost:8000When adding a new doc page, add it to mkdocs.yml. The published site at https://springfall2008.github.io/batpred/ is built automatically from main via GitHub Actions.