{"id":45635412,"url":"https://github.com/bvweerd/battery_controller","last_synced_at":"2026-04-07T11:01:07.326Z","repository":{"id":338593290,"uuid":"1153842627","full_name":"bvweerd/battery_controller","owner":"bvweerd","description":"Home Assistant custom integration for smart battery controller management and optimization","archived":false,"fork":false,"pushed_at":"2026-03-31T18:25:14.000Z","size":964,"stargazers_count":2,"open_issues_count":2,"forks_count":0,"subscribers_count":1,"default_branch":"dev","last_synced_at":"2026-04-02T07:39:21.227Z","etag":null,"topics":["battery","custom-integration","energy-management","hacs","home-assistant"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bvweerd.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-09T18:21:46.000Z","updated_at":"2026-03-31T18:25:18.000Z","dependencies_parsed_at":null,"dependency_job_id":"31d21d8e-cbce-429c-8852-701227d4903c","html_url":"https://github.com/bvweerd/battery_controller","commit_stats":null,"previous_names":["bvweerd/battery_controller"],"tags_count":17,"template":false,"template_full_name":null,"purl":"pkg:github/bvweerd/battery_controller","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bvweerd%2Fbattery_controller","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bvweerd%2Fbattery_controller/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bvweerd%2Fbattery_controller/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bvweerd%2Fbattery_controller/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bvweerd","download_url":"https://codeload.github.com/bvweerd/battery_controller/tar.gz/refs/heads/dev","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bvweerd%2Fbattery_controller/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31509941,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-07T03:10:19.677Z","status":"ssl_error","status_checked_at":"2026-04-07T03:10:13.982Z","response_time":105,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["battery","custom-integration","energy-management","hacs","home-assistant"],"created_at":"2026-02-24T01:47:07.450Z","updated_at":"2026-04-07T11:01:07.264Z","avatar_url":"https://github.com/bvweerd.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Battery Controller\n\n**Home battery cost optimization for Home Assistant**\n\n[![GitHub Release](https://img.shields.io/github/release/bvweerd/battery_controller.svg?style=flat-square)](https://github.com/bvweerd/battery_controller/releases)\n[![License](https://img.shields.io/github/license/bvweerd/battery_controller.svg?style=flat-square)](LICENSE)\n[![hacs](https://img.shields.io/badge/HACS-Default-orange.svg?style=flat-square)](https://hacs.xyz)\n\n---\n\n\u003e **⚠️ This integration is aimed at technically experienced users.**\n\u003e It requires setting up and tuning a number of parameters (efficiency, degradation costs, price sensors, control mode) and connecting it to your inverter via automations. Incorrect configuration will not damage your battery, but may result in suboptimal scheduling or no control at all. If you are not comfortable reading diagnostics data and interpreting optimizer output, this integration may not be the right fit — yet.\n\n---\n\n## 🔍 Diagnostics Analyzer\n\n**Upload your `diagnostics.json` to the online analyzer for a full breakdown of your configuration, optimizer schedule, profitability analysis, and improvement tips — no installation required.**\n\n**[▶ Open Battery Controller Analyzer](https://bvweerd.github.io/battery_controller/)**\n\n[![Battery Controller Analyzer screenshot](assets/analyzer.png)](https://bvweerd.github.io/battery_controller/)\n\nThe analyzer runs entirely in your browser. It visualizes the current schedule, re-runs the DP optimizer with your data, and explains every charge/discharge decision. To generate a diagnostics file: **Settings → Devices \u0026 Services → Battery Controller → three-dot menu → Download diagnostics**.\n\n---\n\n## What is Battery Controller?\n\n\u003e **For a detailed, step-by-step explanation of the optimization algorithm**, see [ALGORITHM.md](ALGORITHM.md).\n\n\nThis Home Assistant custom integration optimizes your home battery to minimize electricity costs. It uses **dynamic programming** (backward induction) to calculate the optimal charge/discharge schedule based on:\n\n- **Electricity price forecasts** (Nordpool, ENTSO-E, or any price sensor with forecast attributes like the [Dynamic Energy Contract Calculator](https://github.com/bvweerd/dynamic_energy_contract_calculator))\n- **PV production forecasts** (from open-meteo.com solar radiation data)\n- **Household consumption patterns** (learned from historical energy meter data)\n- **Battery characteristics** (capacity, power limits, round-trip efficiency, degradation)\n- **Historical price model** (fallback and horizon extension when day-ahead prices are not yet published)\n\nBattery Controller works with any battery inverter and electricity meter — it is a calculated integration that reads sensors and writes setpoints. It does not communicate directly with hardware.\n\n![Battery Controller dashboard showing electricity prices, battery charge/discharge schedule and SoC](assets/battery-with-prediction.png)\n\n### Key Features\n\n- **Price arbitrage**: Charge during cheap hours, discharge during expensive hours\n- **Multi-battery support**: Configure multiple batteries with independent sensors; the optimizer treats them as one aggregate while distributing setpoints proportionally\n- **Multi-array PV**: Add any number of PV arrays with independent orientation/tilt (e.g. south + east + west)\n- **Pre-day-ahead price model**: Optimize before day-ahead prices are published (typically before 13:00 CET) using a self-learning historical model that improves over time\n- **Horizon extension**: When live day-ahead prices cover less than 24 hours, the remaining hours are filled with the historical model automatically\n- **PV self-consumption**: Maximize use of solar energy, minimize feed-in\n- **DC-coupled PV support**: Higher efficiency for panels directly on the battery inverter's DC bus (hybrid inverters)\n- **Zero-grid control**: Real-time battery control to minimize grid exchange\n- **Degradation-aware**: Accounts for battery wear in optimization decisions\n- **Multiple control modes**: Zero-grid, follow schedule, hybrid, or manual\n- **Negative price handling**: Suggests PV curtailment or maximum power consumption during negative prices\n\n---\n\n## How It Works\n\n### Architecture\n\nThe integration runs three cascading coordinators:\n\n1. **Weather Coordinator** (every 30 min): Fetches solar radiation and wind speed forecasts from open-meteo.com\n2. **Forecast Coordinator** (every 15 min): Calculates PV production and consumption forecasts\n3. **Optimization Coordinator** (every 15 min): Runs the DP optimizer and zero-grid controller (see [ALGORITHM.md](ALGORITHM.md) for full algorithmic details)\n\n#### Subentry Structure\nBattery Controller uses **subentries** to manage hardware flexibly:\n- **Battery subentries**: Each contains its own capacity, power limits, SoC sensor, and optional power sensor.\n- **PV Array subentries**: Each contains its own peak power, orientation, tilt, and coupling type.\n\nThe optimizer aggregates all battery subentries into a single virtual battery for planning. When executing the schedule, the required power is split across the physical batteries proportional to their available headroom (charging) or stored energy (discharging).\n\n### Historical Price Model (Pre-Day-Ahead Fallback)\n\nDay-ahead electricity prices (Nordpool, ENTSO-E) are published around 13:00 CET. Before that, the integration uses a **self-learning historical price model** so the optimizer can still run on a reasonable forecast.\n\nThe model also **extends the planning horizon** when live prices cover less than 24 hours. It builds lookup tables from data in the HA recorder using hour, weekday, solar irradiance (GHI), and wind speed.\n\n### Learning period — give the optimizer time to calibrate\n\n\u003e **Allow at least 2–4 weeks of operation before judging the optimizer's performance.**\n\nThe optimizer uses a technique called *rolling-horizon dynamic programming*. On each run it calculates not only the optimal schedule, but also a **shadow price** (λ) — the marginal value of one extra kWh stored in the battery, given the current price forecast.\n\nThat shadow price is fed back as the **terminal condition** of the next run: it tells the optimizer what stored energy will be worth *after* the planning horizon ends. A well-calibrated shadow price makes consecutive schedules consistent with each other and prevents the optimizer from over-charging or over-discharging near the horizon boundary.\n\nOn the **first run** there is no shadow price yet, so the optimizer falls back to the average sell price at the end of the forecast. As runs accumulate (every 15 minutes), the shadow price converges toward the true marginal value of storage for your household's typical price and consumption pattern. During this convergence period you may notice:\n\n- The schedule changing more noticeably between runs.\n- The optimizer occasionally charging or discharging more aggressively than expected.\n- Estimated savings that appear lower than the long-run optimum.\n\nBoth the **historical price model** and the **shadow price** build up from HA recorder data, so the longer the integration runs, the better the forecasts and the more stable the resulting schedule.\n\n---\n\n## Installation\n\n### Prerequisites\n\n- **Home Assistant 2025.1 or later**\n- A dynamic electricity price sensor with forecast attributes (e.g., Nordpool, ENTSO-E, or the [Dynamic Energy Contract Calculator](https://github.com/bvweerd/dynamic_energy_contract_calculator)).\n- Battery SoC sensor(s) from your inverter integration.\n- [HACS](https://hacs.xyz/) installed in your Home Assistant (recommended).\n\n### Verifying your price sensor\n\nThe integration reads forecast data from the **forecast attributes** of your price sensor. Before setting up, verify that your sensor exposes the required attributes in Home Assistant's Developer Tools.\n\nGo to **Developer Tools → States**, find your price sensor (e.g. `sensor.nordpool_kwh_nl_eur_3_10_21`) and check that the attributes contain a list of future prices. The integration supports several common formats:\n\n**Nordpool / ENTSO-E style** — attributes contain `raw_today` and `raw_tomorrow`, each a list of objects with a `value` key:\n```yaml\nraw_today:\n  - start: \"2026-03-16T00:00:00+01:00\"\n    end:   \"2026-03-16T01:00:00+01:00\"\n    value: 0.1234\n  - ...\nraw_tomorrow:\n  - start: \"2026-03-17T00:00:00+01:00\"\n    value: 0.2345\n  - ...\n```\n\n**Generic forecast list** — attributes contain a `forecast` key with a list of objects:\n```yaml\nforecast:\n  - datetime: \"2026-03-16T12:00:00+00:00\"\n    price: 0.1580\n  - datetime: \"2026-03-16T13:00:00+00:00\"\n    price: 0.2210\n  - ...\n```\n\n**Dynamic Energy Contract Calculator** — exposes `prices_today` and `prices_tomorrow` as plain lists of floats (one per hour), or a combined `price_forecast` list.\n\nIf your sensor's state is the current price but its attributes contain no forecast list, the optimizer will run with a flat price forecast and cannot perform meaningful arbitrage. In that case use a different sensor or add the [Dynamic Energy Contract Calculator](https://github.com/bvweerd/dynamic_energy_contract_calculator) on top of your existing sensor.\n\n### Via HACS (Recommended)\n\n1. Navigate to HACS -\u003e Integrations -\u003e Three dots -\u003e Custom repositories.\n2. Add `https://github.com/bvweerd/battery_controller` as an **Integration**.\n3. Install the \"Battery Controller\" integration and restart Home Assistant.\n\n### Manual Installation\n\n1. Copy `custom_components/battery_controller` to your `custom_components` directory.\n2. Restart Home Assistant.\n\n### Setup\n\nAfter installation, add the integration through the UI:\n**Settings → Devices \u0026 Services → Add Integration → Battery Controller**\n\nOnce the main integration is added, you MUST add your hardware as subentries:\n1. Go to **Settings → Devices \u0026 Services → Battery Controller**.\n2. Click **Add Subentry**.\n3. Select **Battery** or **PV Array** and follow the instructions.\n\n---\n\n## Configuration\n\n### Main Integration\n\nThe main configuration covers global sensors and advanced settings, organised in collapsible sections.\n\n**Sensors (required)**\n\n| Parameter | Description |\n|-----------|-------------|\n| Electricity price sensor | Price sensor with forecast attributes |\n\n**Optional Sensors**\n\n| Parameter | Description |\n|-----------|-------------|\n| Feed-in price sensor | Separate feed-in/export price sensor |\n| Power consumption sensors | Real-time grid import power sensors (W) for zero-grid control |\n| Power production sensors | Real-time grid export power sensors (W) for zero-grid control |\n| Energy consumption sensors | Cumulative kWh sensors for consumption pattern learning |\n| Energy production sensors | Cumulative kWh sensors for production pattern learning |\n| PV production sensors | Cumulative kWh sensors from PV inverters (used to reconstruct gross consumption) |\n\n**Advanced**\n\n| Parameter | Default | Description |\n|-----------|---------|-------------|\n| Optimization interval | 15 min | How often the DP optimizer runs |\n| Fixed feed-in price | €0.04/kWh | Fallback feed-in price when no sensor is available |\n| Zero grid enabled | true | Enable real-time zero-grid balance control |\n| Zero grid response time | 10 s | Expected battery response delay; limits setpoint update rate |\n| Max grid power | 0 kW | Grid connection cap (0 = unlimited) |\n\n### Battery Subentry\n\n| Parameter | Default | Description |\n|-----------|---------|-------------|\n| Name (opt) | — | Display name for this battery |\n| Capacity (kWh) | 10.0 | Total battery capacity |\n| Max charge power (kW) | 5.0 | Maximum charge rate |\n| Max discharge power (kW) | 5.0 | Maximum discharge rate |\n| Round-trip efficiency | 0.90 | Battery round-trip efficiency (0.5–1.0) |\n| Min SoC (%) | 10.0 | Lower operating limit for optimization |\n| Max SoC (%) | 90.0 | Upper operating limit for optimization |\n| SoC sensor | — | State-of-charge sensor (% or kWh) |\n| Power sensor (opt) | — | Real-time battery power sensor (W or kW) |\n| DC PV efficiency (opt) | 0.97 | Efficiency of DC-coupled PV on this inverter's DC bus |\n\n### PV Array Subentry\n\n| Parameter | Default | Description |\n|-----------|---------|-------------|\n| Name (opt) | — | Display name for this array |\n| Peak power (kWp) | 1.0 | Array peak output |\n| Orientation (°) | 180 | Compass bearing: 0 = north, 90 = east, 180 = south, 270 = west |\n| Tilt (°) | 35 | Panel tilt angle from horizontal |\n| Efficiency factor (opt) | 0.85 | Derating for shading, soiling, inverter losses (AC-coupled) |\n| DC-coupled | false | Enable if this array is on the battery inverter's DC bus |\n\n---\n\n## Entities Created\n\n**Convention**: All power sensors use **positive for discharge** and **negative for charge**.\n\n### Sensors\n\n**Optimization**\n\n| Entity | Unit | Description |\n|--------|------|-------------|\n| Optimal Power | W | Battery power recommended by the DP optimizer for the current 15-min slot |\n| Optimal Mode | — | Current mode: `charging`, `discharging`, `idle`, `zero_grid` |\n| Schedule | — | Full schedule summary; detailed schedule available in attributes. Disabled by default to reduce recorder load. |\n\n**Battery State**\n\n| Entity | Unit | Description |\n|--------|------|-------------|\n| Total State of Charge | % | Combined SoC across all batteries (capacity-weighted average) |\n| Total Battery Power | kW | Combined battery power across all batteries |\n| Battery Setpoint | W | Combined real-time power target sent to all batteries (~5s updates) |\n| Battery Setpoint [Name] | W | Per-battery power setpoint (split from the combined setpoint) |\n| State of Charge [Name] | % | Per-battery state of charge |\n\n**Financial**\n\n| Entity | Unit | Description |\n|--------|------|-------------|\n| Shadow Price of Storage | EUR/kWh | Marginal value of 1 kWh stored right now, derived from the DP value function. Use as a charge/discharge decision threshold. |\n| Estimated Savings | EUR | Cumulative financial benefit vs. doing nothing (running total) |\n\n**Forecast**\n\n| Entity | Unit | Description |\n|--------|------|-------------|\n| PV Forecast | kW | Current expected PV output; full hourly forecast in attributes |\n| Consumption Forecast | kW | Current expected household consumption; full forecast in attributes |\n| Net Grid Forecast | kW | Expected grid exchange without battery (consumption − PV); full forecast in attributes |\n| PV Forecast [Name] | kW | Per-array PV forecast. Diagnostic, disabled by default. |\n\n**Diagnostics** (disabled by default)\n\n| Entity | Unit | Description |\n|--------|------|-------------|\n| Solar Irradiance | W/m² | Current GHI from open-meteo; logged to recorder for price model training |\n| Wind Speed | m/s | Current wind speed from open-meteo; logged to recorder for price model training |\n| Current Grid Power | kW | Measured grid exchange used by the zero-grid controller |\n| Control Mode | — | Active control mode (diagnostic mirror of the select entity) |\n| Optimization Status | — | Optimizer health: `ok`, `stale`, `failed`, `disabled`, or `initializing` |\n\n### Binary Sensors\n\n| Entity | Description |\n|--------|-------------|\n| PV Curtailment Suggested | ON when feed-in price is negative and the battery can no longer absorb excess PV production (SoC at maximum, or actual charge power significantly below setpoint) |\n| Use Maximum Power Suggested | ON when the grid buy price is negative — signals that consuming as much as possible (battery charge, flexible loads) is beneficial |\n\n### Switch\n\n| Entity | Description |\n|--------|-------------|\n| Optimization Enabled | Pause/resume the optimizer without changing any other settings. State is restored on HA restart. |\n\n### Select\n\n| Entity | Options | Description |\n|--------|---------|-------------|\n| Control Mode | `zero_grid`, `follow_schedule`, `hybrid`, `manual` | Active battery control strategy |\n\n### Number Entities\n\n| Entity | Range | Default | Description |\n|--------|-------|---------|-------------|\n| Degradation Cost | 0–0.20 EUR/kWh | 0.03 | Battery wear cost per kWh throughput; included in the optimizer's cost function |\n| Minimum Price Spread | 0–0.50 EUR/kWh | 0.05 | Minimum buy/sell spread required before arbitrage is scheduled |\n| Zero Grid Deadband | 0–500 W | 50 W | Grid power tolerance; setpoints are not updated within this band |\n| Manual Power Setpoint | ±max power W | 0 W | Target power in `manual` mode (positive = discharge, negative = charge) |\n\n---\n\n## Control Modes\n\n- **Zero Grid**: Minimize grid exchange in real-time using the battery.\n- **Follow Schedule**: Execute the DP-optimized schedule exactly. When the commitment\n  filter keeps an active charge/discharge locked within the same price period, that\n  lock applies to the published controller setpoint too, not just the diagnostic\n  `optimal_power` value.\n- **Hybrid** (recommended): DP schedule for arbitrage, zero-grid for self-consumption.\n- **Manual**: Target power set via `number.battery_controller_manual_power_setpoint`.\n\nChange the active mode with the **Control Mode** select entity, or use a service call in an automation.\n\n---\n\n## Controlling Your Battery\n\nUse an automation to read the controller power target and send commands to your inverter.\nIn `follow_schedule`, `optimal_power` is the optimizer recommendation and\n`battery_setpoint` is the actual published controller target; under normal operation\nthey match, and if the commitment filter locks a charge/discharge step within the\nsame price period, the lock is reflected in `battery_setpoint` as well.\n\n| Control Mode | Optimal Mode | Power Sensor to Use |\n|-------------|-------------|---------------------|\n| `follow_schedule` | `charging` / `discharging` | `sensor.battery_controller_battery_setpoint` (W) |\n| `hybrid` / `zero_grid` | `charging` / `discharging` / `zero_grid` | `sensor.battery_controller_battery_setpoint` (W) |\n\n### Example Automation\n\n```yaml\nautomation:\n  - alias: \"Battery Controller - Inverter Control\"\n    trigger:\n      - platform: state\n        entity_id: sensor.battery_controller_optimal_mode\n      - platform: state\n        entity_id: sensor.battery_controller_battery_setpoint\n    action:\n      - variables:\n          mode: \"{{ states('sensor.battery_controller_optimal_mode') }}\"\n          power_w: \"{{ states('sensor.battery_controller_battery_setpoint') | float }}\"\n      - choose:\n          - conditions: \"{{ power_w \u003c -50 }}\" # Charging\n            sequence:\n              - service: number.set_value\n                target: { entity_id: number.inverter_charge_power }\n                data: { value: \"{{ power_w | abs }}\" }\n          - conditions: \"{{ power_w \u003e 50 }}\" # Discharging\n            sequence:\n              - service: number.set_value\n                target: { entity_id: number.inverter_discharge_power }\n                data: { value: \"{{ power_w }}\" }\n        default:\n          - service: select.select_option\n            target: { entity_id: select.inverter_mode }\n            data: { option: \"Auto\" }\n```\n\n---\n\n## Removal\n\nTo remove the Battery Controller integration:\n\n1. Go to **Settings → Devices \u0026 Services → Battery Controller**.\n2. Click the three-dot menu and select **Delete**.\n3. Confirm the deletion — this removes the integration, all subentries, and all associated entities.\n4. Restart Home Assistant to ensure all entities are fully removed from the registry.\n\nIf entities remain after deletion, go to **Settings → Devices \u0026 Services → Entities**, filter by \"battery_controller\", and delete any remaining entries manually.\n\n---\n\n## Known Limitations\n\n- **Optimization horizon**: The DP optimizer uses a rolling 24–36 hour horizon. Decisions near the end of the horizon depend on the shadow price (marginal value of stored energy) rather than explicit future prices. The shadow price converges over several days of operation.\n- **Price forecast dependency**: The optimizer requires a price sensor with forecast attributes. Without future prices, only the historical model is used, which reduces arbitrage accuracy.\n- **Day-ahead gap**: Day-ahead prices are typically published around 13:00 CET. Before that, the integration uses the self-learning historical price model. During this window the schedule may be less optimal.\n- **Single aggregate battery**: Multiple batteries are aggregated into one virtual battery for optimization. Setpoints are then split proportionally. Batteries with very different chemistries or SoC ranges may not be split optimally.\n- **LFP assumptions**: The battery efficiency model assumes flat efficiency across the SoC range (as is typical for LFP cells). Other chemistries with significant SoC-dependent efficiency variation are not modeled.\n- **No direct hardware communication**: Battery Controller writes setpoints to HA sensor/number entities only. You are responsible for connecting these to your inverter via automations.\n- **Consumption pattern learning**: The consumption model requires several weeks of kWh sensor history to build accurate patterns. During the initial period, forecasts may be less accurate.\n\n---\n\n## Troubleshooting\n\n### No charge/discharge schedule is generated\n\n- Check **Settings → System → Logs** for errors from `battery_controller`.\n- Verify the **Optimization Status** sensor is `ok` (not `failed` or `initializing`).\n- Confirm your price sensor has forecast attributes: **Developer Tools → States** → find your sensor → check attributes for `raw_today`, `raw_tomorrow`, or `forecast`.\n\n### Battery is not charging or discharging as expected\n\n- Check the **Control Mode** select entity — it must not be `manual` unless you intend that.\n- Verify your automation is reading `sensor.battery_controller_battery_setpoint` (not `optimal_power`).\n- In `follow_schedule` mode: the commitment filter may be holding an earlier setpoint within the same price period. Check the **Schedule** sensor attributes for the committed action.\n\n### Optimizer always schedules idle / no arbitrage\n\n- Ensure the price spread between cheap and expensive hours exceeds the **Minimum Price Spread** number entity (default 0.05 EUR/kWh) plus twice the **Degradation Cost** (default 0.03 EUR/kWh).\n- Check that the feed-in price is not equal to the grid price — if they are the same, arbitrage is less profitable. Configure a separate feed-in price sensor or set the fixed feed-in price.\n\n### Entities are unavailable\n\n- The integration marks entities unavailable when a coordinator fails to update. Check the logs for HTTP errors from open-meteo.com.\n- If the SoC sensor is unavailable, the optimizer falls back to the last known SoC. Check that the SoC sensor entity is working correctly.\n\n### PV forecast is always zero\n\n- Verify your PV array subentry has the correct peak power, orientation, and tilt configured.\n- Check that open-meteo.com is reachable from your Home Assistant instance.\n\n### Diagnostics and analyzer\n\nDownload diagnostics via **Settings → Devices \u0026 Services → Battery Controller → three-dot menu → Download diagnostics** and upload to the [online analyzer](https://bvweerd.github.io/battery_controller/) for a full breakdown of your configuration, schedule, and optimizer decisions.\n\n---\n\n## License\n\nSee [LICENSE](LICENSE) for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbvweerd%2Fbattery_controller","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbvweerd%2Fbattery_controller","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbvweerd%2Fbattery_controller/lists"}