Complete guide for installing and configuring BESS Battery Manager for Home Assistant.
- Home Assistant OS, Container, or Supervised
Growatt MIC/MIN/MOD/MID via Growatt Server (cloud)
- A Growatt AC-coupled inverter with battery storage
- The Growatt Server integration installed in Home Assistant
⚠️ Token authentication is required. The integration supports both username/password and token-based auth, but BESS needs thenumber.*andswitch.*entities and service calls that are only available with token auth. Username/password auth will not expose these, and BESS will not work correctly without them.
Growatt SPH via Growatt Server (cloud)
- A Growatt SPH (DC-coupled) inverter with battery storage
- The Growatt Server integration with token auth
Growatt MIC/MIN/MOD/MID via solax_modbus (local Modbus)
- A Growatt AC-coupled inverter with battery storage
- The homeassistant-solax-modbus HACS integration with the Growatt plugin enabled
- Provides local Modbus control — no cloud dependency
- Requires Growatt plugin with TOU time slot entities (entity_id:
select.*_time_1_active, unique_id suffix:time_1_enabled). Slots 4-9 are disabled by default in HA and must be enabled manually.
SolaX via solax_modbus (local Modbus)
- A SolaX inverter with battery storage
- The homeassistant-solax-modbus integration (available via HACS) installed in Home Assistant
- BESS controls the inverter via VPP active-power commands
- Auto-detection uses the HA entity registry (
platformandunique_idfields), which are immutable and unaffected by entity renaming. If you have renamed entity IDs and removed the original suffixes, use the setup wizard to map them manually
For detailed entity requirements per platform, see docs/INVERTER_PLATFORMS.md.
One of:
- Nordpool integration — for Nordic and European spot price markets
- Octopus Energy integration — for UK market (via HACS)
BESS works without solar panels or a solar forecast. If you have PV and want solar-aware optimization:
- Only Solcast (available via HACS) is supported
- The built-in Home Assistant solar forecast integration is not supported — it does not provide hourly predictions for today and tomorrow, which BESS requires
- Open your Home Assistant web interface
- Go to Settings → Add-ons (in the left sidebar, click Settings, then Add-ons)
- Click the Add-on Store button (bottom-right)
- Click the overflow menu (⋮) in the top-right corner, then Repositories
- Paste the repository URL and click Add:
https://github.com/johanzander/bess-manager - Close the dialog — BESS Battery Manager now appears in the store
- Click it, then click Install
BESS uses InfluxDB to store and retrieve historical energy sensor data. Without it, the system loses all historical context when restarted and cannot backfill the energy balance chart after startup. It is not required for optimization to work, but strongly recommended.
- Go to Settings → Add-ons → Add-on Store
- Search for InfluxDB and install it
- Start the add-on and open the web UI
Home Assistant needs its own user with WRITE access to push sensor data into InfluxDB.
- Open the InfluxDB web UI (from the add-on page, click Open Web UI)
- Go to Settings → Users
- Create a user, for example
homeassistant, with a password - Grant it WRITE access to the
homeassistantdatabase
Note: This is a separate user from the BESS read-only user created in step 2d below. HA writes data; BESS reads it. Keep them separate so BESS cannot accidentally modify data.
Add the following to your configuration.yaml:
influxdb:
host: localhost
port: 8086
database: !secret influxdb_database
username: !secret influxdb_username
password: !secret influxdb_password
max_retries: 3
include:
domains:
- sensorAnd add the corresponding entries to secrets.yaml:
influxdb_database: homeassistant
influxdb_username: homeassistant
influxdb_password: your_ha_writer_passwordAfter restarting Home Assistant, sensor states will start being written to InfluxDB.
Note: In the InfluxDB UI under Configuration, you should see the connection listed as
http://localhost:8086— CONNECTED. The databasehomeassistantappears under Explore.
BESS only needs read access to InfluxDB. Create a dedicated user:
- Open the InfluxDB web UI (from the add-on page, click Open Web UI)
- Go to Settings → Users (InfluxDB 1.x admin UI at
http://homeassistant.local:8086) - Create a new user, for example
bess, with a password - Grant it READ access to the
homeassistantdatabase
Add the following to your BESS add-on configuration:
influxdb:
url: "http://homeassistant.local:8086/api/v2/query"
bucket: "homeassistant/autogen"
username: "bess"
password: "your_password_here"
⚠️ The bucket name is not just the database name. InfluxDB 1.x organises data as<database>/<retention_policy>. The default retention policy isautogen, so the bucket must be set tohomeassistant/autogen— not justhomeassistant. This is the most common misconfiguration.
URL note: Use
http://homeassistant.local:8086/api/v2/queryif BESS runs on the same machine as Home Assistant. If InfluxDB is on a separate host, replace the hostname with the IP address, e.g.http://192.168.1.100:8086/api/v2/query.
After starting BESS, go to Settings → System in the web interface. The
Historical Data Access component should show OK. If it shows a warning like
"returned no valid data", the most likely cause is an incorrect bucket name — double-check
that you have used homeassistant/autogen and not just homeassistant.
BESS needs a forecast of your home consumption to plan the battery schedule.
This is selected with the consumption_strategy setting in the BESS Manager
web interface (Settings → Home). Four strategies are available.
Recommended: ha_statistics. It is the most accurate option that needs no
manual sensor setup — see below.
| Strategy | Accuracy | What you must configure |
|---|---|---|
ha_statistics ✅ recommended |
High — real home consumption (incl. solar self-use), time-of-day shaped | Nothing beyond selecting it. Needs the inverter's lifetime load-consumption sensor (auto-discovered) and ~7 days of HA history |
influxdb_7d_avg |
High — same data source, 15-min resolution | Requires an InfluxDB instance (Step 2) and the local_load_power sensor |
fixed |
Low — a single flat number, does not adapt | Manually enter a kWh/hour value (home.default_hourly) |
sensor (legacy) |
Low — grid-import proxy that ignores solar self-consumption, so it under-estimates load on sunny days | Requires a hand-written template sensor in configuration.yaml (see below) |
Builds a 24-hour consumption profile from Home Assistant's built-in Recorder long-term statistics for the inverter's load-consumption sensor, averaged over the past 7 days (with outlier trimming to absorb occasional EV/heat-pump spikes). This reflects actual household consumption — including the part covered by your own solar — and varies by time of day (e.g. higher in the evening, lower overnight).
To enable it, just set consumption_strategy to ha_statistics in the web
interface. No template sensor, no configuration.yaml edits, no InfluxDB. Until
HA has accumulated enough statistics, BESS temporarily falls back to the fixed
home.default_hourly value and tells you so in the UI.
Requirement: the inverter's load-consumption sensor must be set up correctly in Home Assistant's Energy dashboard (Settings → Dashboards → Energy), so HA records the long-term statistics this strategy reads. If consumption is not configured there, no statistics exist to query and BESS stays on the fixed fallback. Allow ~7 days after setup for enough history to accumulate.
Same idea, but reads the local_load_power sensor from InfluxDB at 15-minute
resolution. Equally accurate; choose this over ha_statistics only if you
already run InfluxDB and want the finer resolution. Requires Step 2 and the
local_load_power sensor configured.
Uses a single flat kWh/hour value (home.default_hourly). Simple fallback for
very predictable homes; does not adapt to actual usage.
Not recommended. This is the original strategy. It approximates consumption from grid import power and therefore does not account for solar self-consumption — on sunny days it under-estimates real consumption. It also requires a hand-written template sensor. Prefer
ha_statistics. Note that it produces a flat 24-hour profile likefixed: BESS reads the single current value of the 48h-average sensor and applies it to every period in the horizon, so it has no time-of-day shape.
If you still want it, BESS reads a sensor named *48h_avg*grid_import*
(auto-discovered by name). Create it in configuration.yaml:
template:
- sensor:
- name: "Filtered Grid Import Power"
unique_id: filtered_grid_import_power
unit_of_measurement: "W"
state: >
{% if states('sensor.rkm0d7n04x_battery_1_charging_w') | float < 400 and
states('sensor.rkm0d7n04x_battery_1_discharging_w') | float < 400 %}
{{ states('sensor.rkm0d7n04x_import_power') | float }}
{% else %}
{{ states('sensor.filtered_grid_import_power') | float(0) }}
{% endif %}
sensor:
- platform: statistics
name: "48h Average Grid Import Power"
unique_id: grid_import_power_48h_avg
entity_id: sensor.filtered_grid_import_power
state_characteristic: mean
max_age:
hours: 48Note: Replace
rkm0d7n04x_battery_1_charging_w,rkm0d7n04x_battery_1_discharging_w, andrkm0d7n04x_import_powerwith your actual sensor entity IDs from your inverter integration. The filter holds the previous value while the battery is active (>400 W) so the 48h average reflects pure home consumption.
Tip — average measured home load instead of grid import. If you want to stay on the
sensorstrategy but avoid the solar blind spot, point the 48h statistics average directly at your inverter's home load power sensor (e.g.local_load_power) rather than the grid-import template. That value is already true home consumption (solar self-use included) and needs no battery filter — drop thetemplate:block entirely and setentity_id: sensor.<your_local_load_power>on the statistics sensor. Keep the friendly name containing48handgrid importso BESS still auto-discovers it. This is the cleaner way to do it if you must usesensor.
Battery, pricing, home, and sensor settings are all configured through the web interface.
The only add-on configuration setting is influxdb (see Step 2).
When you open the web interface for the first time, a Setup Wizard will launch automatically. It scans Home Assistant for connected integrations and fills in sensor entity IDs automatically. Walk through the wizard to:
- Auto-discover your inverter (Growatt or SolaX), Nordpool, Solcast and other integrations
- Review and adjust any detected sensor entity IDs
- Confirm the configuration — BESS applies it immediately without a restart
If you need to re-run the wizard later, click Auto-Configure on the Settings → Sensors tab.
All settings are available under the Settings page in the top navigation. There are five tabs:
- Integrations — Inverter platform selection and sensor entity IDs for each integration
- Electricity Pricing — Nordpool/Octopus provider, price area, VAT, markup, additional costs, tax reduction
- Battery — Capacity, power limits, SOC range, cycle cost
- Home — Consumption, currency, fuse size, voltage, phase count, safety margin
- System — Demo mode, AI analyst, diagnostics and debug export
The sections below describe the key values you need to fill in.
Nordpool prices are VAT-exclusive spot prices. The buy price is calculated as:
buy_price = (spot_price + markup_rate) × vat_multiplier + additional_costs
Set vat_multiplier to your country's VAT rate and additional_costs to your fixed per-kWh
charges (grid fee, energy tax, etc.) already including VAT:
| Country | VAT | vat_multiplier |
|---|---|---|
| Sweden, Norway, Denmark, Finland | 25% | 1.25 |
| Netherlands | 21% | 1.21 |
| Germany | 19% | 1.19 |
Example for Sweden:
electricity_price:
area: "SE3"
markup_rate: 0.08 # Supplier markup in SEK/kWh (ex-VAT) — e.g. Tibber charges 8 öre/kWh
vat_multiplier: 1.25 # 25% VAT applied to spot + markup
additional_costs: 1.03 # Grid fee + energy tax in SEK/kWh (VAT-inclusive total)
tax_reduction: 0.0 # Swedish skattereduktion removed as of Jan 1 2026How the raw spot price is converted to your buy and sell prices:
Buy price = (raw spot + markup) × VAT multiplier + additional costs
Sell price = raw spot + tax reduction
Note: The markup is applied before VAT (it's ex-VAT), but the additional costs are already VAT-inclusive.
Explaining each field:
markup_rate— Energy provider's margin/management fee charged per kWh (ex-VAT before VAT is applied). Example: Tibber 0.08 (8 öre/kWh), Ellevio ~0.15.
vat_multiplier— The VAT tax factor. Set to 1.25 for 25% VAT (Sweden, Norway, Denmark, Finland), 1.20 for 20% (UK, some EU), etc.
additional_costscovers fixed per-kWh charges such as grid tariff and energy tax. The code adds this value directly to the buy price, so you must configure it as your final total additional cost per kWh (VAT included).How to calculate
additional_costsfrom your E.ON bill (or similar Swedish invoice):Your invoice shows charges ex-VAT, then applies 25% VAT to the total. Calculate as follows:
Component From your bill Amount per kWh Grid transfer fee (Elöverföring) ex-VAT 0.2584 Energy tax (Energiskatt) ex-VAT 0.3600 Subtotal ex-VAT 0.6184 VAT 25% 25% of 0.6184 0.1546 Total additional_costs(inc. VAT)0.7730 Then configure
additional_costs: 0.77in your settings (round as needed).Your grid transfer fee and energy tax amounts vary by network operator and region. Find these values on your electricity bill and recalculate as shown above.
tax_reduction(labeled as "Export Compensation" in the UI) is the per-kWh payment you receive from the grid operator when selling energy back to the grid. The Swedish skattereduktion (tax reduction) was removed Jan 1 2026. What remains is Nätnytta (grid export benefit).Check your E.ON or other network operator invoice under "Producent/Självfaktura" (Producer/Self-invoice). The section shows what you're paid for exported electricity (typically ex-VAT, no tax on exports).
Example from E.ON invoice:
- Nätnytta (Grid export benefit): -19.88 öre/kWh → set
tax_reduction: 0.1988- This is the per-kWh payment E.ON provides for exporting surplus solar/battery electricity to the grid.
If you're using Octopus Energy (UK), set provider: "octopus" under energy_provider: and configure the entity IDs.
1. Find your entity IDs in Developer Tools > States, search for octopus_energy_electricity:
octopus:
import_today_entity: "event.octopus_energy_electricity_<MPAN>_<SERIAL>_current_day_rates"
import_tomorrow_entity: "event.octopus_energy_electricity_<MPAN>_<SERIAL>_next_day_rates"
export_today_entity: "event.octopus_energy_electricity_<MPAN>_<SERIAL>_export_current_day_rates"
export_tomorrow_entity: "event.octopus_energy_electricity_<MPAN>_<SERIAL>_export_next_day_rates"2. Adjust electricity_price settings - Octopus prices are already VAT-inclusive in GBP/kWh:
home:
currency: "GBP"
electricity_price:
area: "UK"
markup_rate: 0.0
vat_multiplier: 1.0
additional_costs: 0.0
tax_reduction: 0.0 # Adjust if you receive SEG payments3. Set cycle_cost in GBP (see notes below).
CRITICAL: Set
cycle_costin your local currency for correct operation.
Understanding cycle_cost:
This represents the battery wear/degradation cost per kWh charged (excluding VAT). Every time the battery charges 1 kWh, this cost is added to account for battery degradation.
- Purpose: Accounts for battery degradation in optimization calculations
- Impact: Higher values = more conservative battery usage (battery used less frequently)
- Typical range: 0.05-0.09 EUR/kWh (0.50-0.90 SEK/kWh)
How to calculate your cycle_cost:
The formula is simple: Battery Cost ÷ Total Lifetime Throughput = Cost per kWh
Example with Growatt batteries (30 kWh system, EUR):
| Battery Model | Warranty Cycles | DoD | Throughput | Battery Cost | Calculated cycle_cost |
|---|---|---|---|---|---|
| ARK LV | 6,000+ | 90% | 180,000 kWh | 15,000 EUR | 0.083 EUR/kWh |
| APX | 6,000+ | 90% | 180,000 kWh | 15,000 EUR | 0.083 EUR/kWh |
Calculation: 6,000 cycles × 30 kWh = 180,000 kWh total throughput → 15,000 EUR ÷ 180,000 kWh = 0.083 EUR/kWh
Choosing your cycle_cost value:
The calculated value (0.083 EUR/kWh) is a good starting point, but you may want to adjust based on your preferences:
-
Conservative (0.07-0.09 EUR): Use calculated warranty value or slightly lower
- Accounts for full battery replacement cost
- Suitable if you want to preserve battery life
- Battery cycled only when clearly profitable
-
Moderate (0.05-0.07 EUR): Assumes battery exceeds warranty
- Modern LFP batteries often achieve 8,000+ cycles
- Accounts for residual battery value
- Balanced approach for most users
-
Aggressive (0.04-0.05 EUR): Maximum utilization
- Assumes best-case battery longevity
- Maximum system ROI but more battery wear
- Only if you're confident in long battery life
About Depth of Discharge (DoD):
The Min/Max SOC limits you set in Settings → Battery are the master values. BESS syncs them to the inverter on startup and the optimizer stays within this range.
- You configure in Settings → Battery: Set min/max SOC (e.g. 10–100% = 90% usable capacity)
- BESS syncs to inverter: Limits are written to the inverter automatically
- Optional adjustment: Use more conservative limits if you want to reduce battery wear (e.g. 15–90% = 75% DoD)
The DoD is already factored into the warranty cycle count, so you don't need to manually adjust the cycle_cost calculation based on DoD.
- Start BESS Manager
- Open the web interface via Ingress (Settings → Add-ons → BESS Manager → Open Web UI)
- The Setup Wizard launches automatically on first boot — follow it to configure sensors
- Check add-on logs for any errors if the wizard does not appear
Problem: Optimization not working
Solution: Verify all required sensors are configured and returning valid data
Problem: Missing consumption data
Solution: Check your consumption forecast strategy (Step 3). For ha_statistics, allow ~7 days for HA to accumulate statistics; for the legacy sensor strategy, check the 48h_avg_grid_import template sensor is working.
Problem: Battery charges during expensive hours, discharges during cheap hours
Solution: Check cycle_cost is in correct currency (see Step 4)
If the Historical Data Access health check shows WARNING or the energy balance chart is empty, follow these steps in order.
Open the InfluxDB web UI and go to Explore. Navigate as follows:
- Set the database to homeassistant/autogen
- In the Measurement dropdown you should see entries like
%,W,kWh(sensor units) - Select one, then pick a Field — you should see sensor names and recent values
If you can browse sensors here, HA is writing correctly and the data is ready for BESS to read.
Alternatively, check the Home Assistant logs for any InfluxDB write errors:
- Go to Settings → System → Logs
- Search for
influxdb - Errors here mean HA cannot reach InfluxDB or the writer credentials are wrong
If no data appears in InfluxDB at all, check:
- The
influxdb:block is present inconfiguration.yamland HA has been restarted - The writer username and password in
secrets.yamlare correct - The writer user has WRITE access to the
homeassistantdatabase
Run the following curl command from the machine running Home Assistant (or any machine that can
reach InfluxDB). Replace <influxdb-host>, <db>, and <password> with your values:
curl -s -o /dev/null -w "HTTP %{http_code}\n" -X POST "http://<influxdb-host>:8086/api/v2/query" -u "bess:<password>" -H "Content-type: application/vnd.flux" -H "Accept: application/csv" --data 'from(bucket: "<db>/autogen") |> range(start: -1h) |> limit(n: 1)'This uses the same endpoint and query language as BESS, so it is an exact connectivity test.
Expected responses:
HTTP 200— working correctlyHTTP 401— wrong username or passwordHTTP 403— Flux query language is not enabled in your InfluxDB configuration
If you get a connection error, replace homeassistant.local with the IP address of your Home
Assistant instance (e.g. 192.168.1.100).
The most common misconfiguration is the bucket name. In the BESS add-on configuration, it must be:
bucket: "homeassistant/autogen"Not homeassistant, not home_assistant — it must include /autogen.
Go to Settings → System in the BESS web interface to verify all sensors are working correctly. The health tab shows OK / WARNING / ERROR for each integration and lets you export debug data.
For troubleshooting, check the add-on logs:
- Go to Settings → Add-ons → BESS Manager
- Click on the Log tab
- Review logs for errors or warnings
Logs provide detailed information about sensor data, optimization decisions, and system operations.
When reporting issues on GitHub:
- Check the add-on logs (see above)
- Include relevant log excerpts showing the error
- Provide your configuration (sensors, battery specs, price settings)
- Describe expected vs actual behavior
Report issues at: https://github.com/johanzander/bess-manager/issues
- Review User Guide to understand the interface