Nou, how does this work?
I ordered a HomeWizard battery, but my HomeWizard Energy app on my phone is - due to the p1 meter connected to Home Assistant - not connected to the HomeWizard cloud functionality.
Is this battery still implementable within HA, if it’s not adoptable through my phone? How does the setup works within Home Assistant?
@stefan1982 Lots of info on the battery and using it with HomeAssistant can be found on the dutch Tweakers forum in the start-topic (first message). https://gathering.tweakers.net/forum/list_messages/2285226
I have only the battery connected to Home Asisstant and that gives me all the sensors as readings. It was automatically found by HA.
If I’m correct you still need the HW-P1 meter for actively steering the battery.
Sorry wrong terminology.
I want to know when the update will be available for all users.
I would like to setup a delayed charging schedule.
Gotcha,
If you have a Plug in Battery, then your P1 meter should have the latest firmware which has the mode selector.
If not, please send me a PM with your HomeWizard account (an e-mail) and the serial ID’s of your P1 meter and battery (they start with 5C or 3C).
I would like an option to stop or pause discharging (without turning off the complete battery)
I would like to use this option for HA to pause the batter when my EV starts charging. The EV will drain the battery in a short time.
You can already do this
Thanks, i missed this info.
Thanks I have found it.
Problem was that I was looking for an entity in the battery device, not in the P1 meter.
Exactly my idea Maikel! Maybe some integration with Zonneplan and their dynamic prices?
I would really like this feature, too - the ability to see the state that the battery is in (idle, charging, discharging) would really be great as well. The ability to manually have the ability to set it to idle, manually charge and then discharge based on time of day, season, solar generation capacity etc. Really, really great work that you all are doing, thank you.
Received my battery beginning of september. Works as designed, plug and play. Its strength is that it prioritises keeping the meter at zero, so I really don’t feel the need to go futzing with its behavior. I would like to see it being picked up by HA, so I can integrate it into my HA energy dashboard.
(I already have a homewizard display, but let’s just say I’m a nerd when it comes to metering and monitoring
)
it… is? See Supported devices
Hi all,
I have two Homewizard batteries installed at my home, and I’m looking to create a smart automation for charging them. My goal is to check, using Solcast or a similar solar forecast service, whether the expected solar generation for the day will be less than the combined capacity of my batteries. If that’s the case, I want my automation to then look up the cheapest moments during the day to buy grid power, so that the batteries can be fully charged in advance. This way, I can maximize the use of cheaper energy throughout the evening and night, instead of having to draw expensive power from the grid.
I’m curious:
- Has anyone already built something like this (with Homewizard batteries or a comparable setup)?
- Are there any examples, templates, or best practices available for automations that combine solar forecasting with dynamic energy pricing for battery charging?
- I also think this kind of feature would be a fantastic addition to the Homewizard app itself. Is there any official roadmap or community project working on this?
Any pointers, code samples, or experience sharing would be really appreciated!
Thanks in advance!
Robin
@rtjdamen: I haven’t exactly build automations using solar forecast, but here is my config using dynamic pricing and charging. Hope it gets you started.
I received my Plugin Battery (PIB) last week and have been experimenting for a few days now to get the automation working. I assume you already got the API token for the P1 meter (see Authorization | HomeWizard Energy API Documentation)
I added this code to my configuration.yaml. With this you can change the mode of the Battery/Batteries by passing it as a variable.
</s> <s>rest_command:</s> <s> homewizard_set_pib_mode:</s> <s> url: https://<IP_ADDRESS_P1_METER>/api/batteries</s> <s> method: PUT</s> <s> verify_ssl: false</s> <s> headers:</s> <s> Authorization: "Bearer <TOKEN_P1_METER>"</s> <s> X-Api-Version: 2</s> <s> payload: '{"mode":"{{ mode }}"}'</s> <s> timeout: 10</s> <s>
P.S. I didn’t got the secure way via !secrets working yet
Edit: Thanks to @DCSBL for pointing to this option.
You can change the battery mode by using the entity select.home_wizard_batterijgroepmodus (disabled by default)
You can test this action with this code in Developer Tools / Actions
action: select.select_option
target:
entity_id: select.home_wizard_batterijgroepmodus
data:
option: zero
I use Nord Pool - Home Assistant to import dynamic pricing. The Nord Pool entity has an attribute low_price that I use to in my PIB automations.
Here are two examples, but you can extend the principle to your own situation (i.e. set mode to “to_full” during cheapest price of the day and the sun isn’t shining and … etc.)
Example 1: standby at 50%
alias: HomeWizard PIB - standy at 50%
description: ""
triggers:
- type: battery_level
device_id: <DEVICE_ID>
entity_id: <ENTITY_ID>
domain: sensor
trigger: device
below: 50
conditions:
- condition: sun
before: sunrise
actions:
- action: select.select_option
metadata: {}
data:
option: standby
target:
entity_id: select.home_wizard_batterijgroepmodus
mode: single
Example 2: zero during high prices in the morning
alias: HomeWizard PIB - Reduce electricity import in the morning
description: "Use PIB power during high prices in the morning"
triggers:
- trigger: state
entity_id:
- sensor.nordpool_kwh_nl_eur_3_10_021
attribute: low_price
from: "true"
to: "false"
conditions:
- condition: sun
after: sunset
- condition: time
after: "06:00:00"
actions:
- action: select.select_option
metadata: {}
data:
option: zero
target:
entity_id: select.home_wizard_batterijgroepmodus
mode: single
I don’t use any other automations yet.
These are my first experiences. Hope it helps.
Hello, i have some questions about plug in battery, i do not know why plug in battery recently so popular, cause for NL people, you have net metering and your electrical price is not too high only 0.25 euro, why so many people wanna bug the bat?
You can set the mode directly in Home Assistant without needing to configure the REST API. You can automate this by calling the select.select_option action.
Thanks… The documentation is a bit fragmented so here is my step-by-step for others.
Got it working by enabling the Batterijgroepmodus entity. (Settings / Devices & Services / Entities → Remove filter for Enabled entities and search and open batterijgroepmodus → click the settings wheel and enable the entity)
After this you can use the entity in an automation like this:
<Automation triggers
and
conditions>
actions:
- action: select.select_option
metadata: {}
data:
option: to_full
target:
entity_id: select.home_wizard_batterijgroepmodus
valid options are: zero, standby, to_full.
I will update my previous post with this information.
Here are some points that were for me the reason to purchase a plugin battery:
- Home automation as a hobby - some people collect stamps, play golf, visit festivals, or buy the latest MacBook. I bought a PIB as a low entry energy storage device without extra investments.
- Energy efficiency as a challenge - Try to minimize our energy import at the lowest price possible - using solarpanels is the cheapest option. Not just “Nul op de meter” but also “Nul op de rekening”.
- Nice benefit is a lower energy bill. I won’t have a 100% Return-on-Investment, but that 's not the goal. At least it’s a hobby that returns some of the money.
- Be prepared when Net metering is stopped in 2027.
- Lower our CO2 footprint.
Hi All! Based on the input here i have worked on an automation to make HomeWizard plug-in batteries charge only when it’s economically smart and solar won’t be enough. It:
• Prefers your own PV where possible
• Only charges during the day’s lowest price hour (or stops when it isn’t the cheapest anymore)
• Accounts for a forecast window (next N hours) and the remaining PV today vs your expected consumption
• Avoids “micro-charging” when the battery is almost full
• Adds a small hysteresis margin to prevent flip-flopping
• Sends notifications only when the mode actually changes
• Runs every 30 minutes (tunable)
Replace the fictional entities below with your own (search for RENAME_ME: comments).
⸻
Variables (quick guide)
• battery_capacity_kwh — your combined battery capacity
• price_epsilon — tolerance to treat current price ≈ minimum price
• micro_threshold_kwh — minimum energy still needed to even consider charging (anti micro-charge)
• stop_margin_kwh — hysteresis margin (stop earlier to avoid flip-flop near full)
• use_dynamic_window — if true, set window length around the PV peak time; else use forecast_window_hours_static
• min_window_h / max_window_h — bounds for the dynamic window
• use_fixed_base_load & base_load_kw — either use a fixed “house idle” load or a dynamic estimate
• avg_use_kw — expected consumption per hour used for forecasts (derived from either the fixed base or dynamic house-load)
• reserved_headroom_kwh — solar expected in the window minus expected use in the window (how much battery space we want to keep for the next hours’ solar)
• solar_remaining_net_kwh — remaining solar today after the window minus expected use (net surplus for the rest of the day)
• need_to_charge_effective — core decision: charge only if needed beyond headroom and above micro threshold
• Battery mode select options assumed: zero, standby, to_full
⸻
Full automation (YAML)
alias: HomeWizard Battery Smart Charging (kWh window + net day-solar) [example]
description: >
Smart charge based on lowest price + PV forecast. Uses a kWh window and net day surplus,
avoids micro-charging, adds hysteresis, and supports fixed or dynamic load.
mode: single
trigger:
- platform: time_pattern
minutes: "/30" # run every 30 minutes
condition: []
action:
- variables:
# ====================== CONFIG ======================
battery_capacity_kwh: 8.9
price_epsilon: 0.0001
micro_threshold_kwh: 0.25
stop_margin_kwh: 0.10
use_dynamic_window: true
forecast_window_hours_static: 3
min_window_h: 1
max_window_h: 6
# Use fixed base load for safety (e.g. your home's idle ~400W)
use_fixed_base_load: true
base_load_kw: 0.4
# ====================== TIME & PRICE ======================
current_hour: "{{ now().hour | int }}"
hours_left_today: "{{ 24 - (now().hour | int) }}"
# RENAME_ME: price sensors (e.g. EnergyZero/any price provider)
current_price: "{{ states('sensor.energy_today_current_hour_price') | float(0) }}"
min_price: "{{ states('sensor.energy_today_min_price') | float(0) }}"
# Treat current price as the min-price hour if within epsilon
cond_now_is_min: "{{ (current_price - min_price) | abs <= price_epsilon }}"
# ====================== BATTERY STATE ======================
# RENAME_ME: 3 battery SoC sensors (or adapt to one)
battery_avg_pct: >-
{% set b1 = states('sensor.hw_batt1_soc') | float(0) %}
{% set b2 = states('sensor.hw_batt2_soc') | float(0) %}
{% set b3 = states('sensor.hw_batt3_soc') | float(0) %}
{{ ((b1 + b2 + b3) / 3) | round(1) }}
kwh_needed: "{{ (((100 - battery_avg_pct) / 100) * battery_capacity_kwh) | round(2) }}"
battery_full: "{{ battery_avg_pct >= 99.5 }}"
# ====================== SOLCAST (normalize to kWh) ======================
# RENAME_ME: your Solcast entities
_this_raw: "{{ states('sensor.solcast_this_hour') | float(0) }}"
_this_unit: "{{ (state_attr('sensor.solcast_this_hour','unit_of_measurement')|string|lower) if (state_attr('sensor.solcast_this_hour','unit_of_measurement') is not none) else 'kwh' }}"
forecast_this_hour_kwh: >-
{% set raw=_this_raw|float(0) %}{% set u=_this_unit %}
{{ (raw/1000.0) if (u in ['wh','watt-hour','watt hours'] or raw > 50) else raw | round(3) }}
_next_raw: "{{ states('sensor.solcast_next_hour') | float(0) }}"
_next_unit: "{{ (state_attr('sensor.solcast_next_hour','unit_of_measurement')|string|lower) if (state_attr('sensor.solcast_next_hour','unit_of_measurement') is not none) else 'kwh' }}"
forecast_next_hour_kwh: >-
{% set raw=_next_raw|float(0) %}{% set u=_next_unit %}
{{ (raw/1000.0) if (u in ['wh','watt-hour','watt hours'] or raw > 50) else raw | round(3) }}
_rem_raw: "{{ states('sensor.solcast_remaining_today') | float(0) }}"
_rem_unit: "{{ (state_attr('sensor.solcast_remaining_today','unit_of_measurement')|string|lower) if (state_attr('sensor.solcast_remaining_today','unit_of_measurement') is not none) else 'kwh' }}"
remaining_solar_today_kwh: >-
{% set raw=_rem_raw|float(0) %}{% set u=_rem_unit %}
{{ (raw/1000.0) if (u in ['wh','watt-hour','watt hours'] or raw > 200) else raw | round(3) }}
# PV peak time today (for dynamic window)
_peak_raw: "{{ states('sensor.solcast_peak_time_today') }}"
peak_ts: >-
{% set r=_peak_raw %}
{% if r in ['unknown','unavailable','none',''] %}
{{ none }}
{% else %}
{{ as_timestamp(r) }}
{% endif %}
hours_to_peak: >-
{% if peak_ts is none %}
3
{% else %}
{% set diff=(peak_ts - as_timestamp(now())) / 3600 %}
{{ diff | float(3) | round(1) }}
{% endif %}
forecast_window_hours_dynamic: >-
{% set h = [hours_to_peak | float(3), min_window_h] | max %}
{{ [h, max_window_h] | min | round(0) }}
forecast_window_hours: >-
{% if use_dynamic_window %}{{ forecast_window_hours_dynamic }}{% else %}{{ forecast_window_hours_static }}{% endif %}
# ====================== HOUSE LOAD (CONSUMPTION) ======================
# Dynamic estimate (net grid + PV - batteries), used only if use_fixed_base_load=false
# RENAME_ME: p1 net power (W), PV power (W), and each battery power (W)
_dyn_use_kw: >-
{% set g = states('sensor.p1_net_power_w') | float(0) %}
{% set b1 = states('sensor.hw_batt1_power_w') | float(0) %}
{% set b2 = states('sensor.hw_batt2_power_w') | float(0) %}
{% set b3 = states('sensor.hw_batt3_power_w') | float(0) %}
{% set b = b1 + b2 + b3 %}
{% set pv = states('sensor.pv_power_now_w') | float(0) %}
{% set load_w = g + pv - b %}
{{ ([load_w, 0] | max / 1000.0) | round(2) }}
# Final expected average consumption per hour
avg_use_kw: >-
{% if use_fixed_base_load %}
{{ base_load_kw | float(0) }}
{% else %}
{{ [base_load_kw | float(0), _dyn_use_kw | float(0)] | max }}
{% endif %}
# ====================== WINDOW & DAY SURPLUS ======================
solar_kwh_window: >-
{% set N = [forecast_window_hours | int, 0] | max %}
{% set thish = forecast_this_hour_kwh | float(0) %}
{% set nexth = forecast_next_hour_kwh | float(0) %}
{% set rem = remaining_solar_today_kwh | float(0) %}
{% if N <= 0 %}
0
{% elif N == 1 %}
{{ thish }}
{% elif N == 2 %}
{{ (thish + nexth) | round(2) }}
{% else %}
{% set exact2 = (thish + nexth) %}
{% set after2 = [rem - exact2, 0] | max %}
{% set hrs_left_after2 = [ (24 - now().hour) - 2, 1 ] | max %}
{% set extra_hours = [N - 2, 0] | max %}
{% set approx_extra = after2 * (extra_hours / hrs_left_after2) %}
{{ (exact2 + approx_extra) | round(2) }}
{% endif %}
use_kwh_window: "{{ (avg_use_kw * (forecast_window_hours | float(0))) | round(2) }}"
reserved_headroom_kwh: "{{ [solar_kwh_window - use_kwh_window, 0] | max | round(2) }}"
solar_remaining_kwh: "{{ remaining_solar_today_kwh | float(0) }}"
hours_after_window: "{{ [ (24 - now().hour) - (forecast_window_hours | int), 0 ] | max }}"
use_kwh_after_window: "{{ (avg_use_kw * (hours_after_window | float(0))) | round(2) }}"
solar_remaining_net_kwh: "{{ [solar_remaining_kwh - use_kwh_after_window, 0] | max | round(2) }}"
# ====================== DECISION FLAGS ======================
need_to_charge_kwh_based: "{{ kwh_needed > reserved_headroom_kwh }}"
need_to_charge_effective: "{{ (kwh_needed > reserved_headroom_kwh) and (kwh_needed > micro_threshold_kwh) }}"
cond_total_solar_insufficient: "{{ (solar_remaining_net_kwh + reserved_headroom_kwh) < kwh_needed }}"
# RENAME_ME: battery mode selector with options: zero | standby | to_full
battery_mode: "{{ states('select.hw_battery_group_mode') }}"
# Optional debug log
- service: logbook.log
data:
name: "Battery Smart Charging [example]"
message: >
{{ now().strftime('%H:%M') }} |
price curr={{ current_price }}, min={{ min_price }}, is_min={{ cond_now_is_min }} |
window={{ forecast_window_hours }}h (dyn={{ use_dynamic_window }}) :
solar={{ solar_kwh_window }} kWh, use={{ use_kwh_window }} kWh, reserved={{ reserved_headroom_kwh }} kWh |
today_solar={{ solar_remaining_kwh }} kWh, after_use={{ use_kwh_after_window }} kWh, today_net={{ solar_remaining_net_kwh }} kWh |
batt={{ battery_avg_pct }}% (need={{ kwh_needed }} kWh) |
mode={{ battery_mode }} | avg_use_kw={{ avg_use_kw }}
- choose:
# (A) Full + still lowest hour → go STANDBY (only if changing)
- conditions:
- condition: template
value_template: "{{ battery_full | bool }}"
- condition: template
value_template: "{{ cond_now_is_min | bool }}"
- condition: template
value_template: "{{ battery_mode != 'standby' }}"
sequence:
- service: select.select_option
target: { entity_id: select.hw_battery_group_mode } # RENAME_ME
data: { option: standby }
- service: notify.notify
data:
title: "Battery Standby"
message: "Battery full; standby during the lowest-price hour."
# (B) START charging (only from ZERO) when:
# cheapest hour + need beyond headroom + net solar today is insufficient
- conditions:
- condition: template
value_template: "{{ cond_now_is_min | bool }}"
- condition: template
value_template: "{{ need_to_charge_effective | bool }}"
- condition: template
value_template: "{{ cond_total_solar_insufficient | bool }}"
- condition: template
value_template: "{{ battery_mode == 'zero' }}"
sequence:
- service: select.select_option
target: { entity_id: select.hw_battery_group_mode } # RENAME_ME
data: { option: to_full }
- service: notify.notify
data:
title: "Battery Charging"
message: >
Start: need {{ kwh_needed }} kWh; reserved {{ reserved_headroom_kwh }} kWh;
today_net {{ solar_remaining_net_kwh }} kWh → not enough combined.
# (C) Lowest hour but (reserved + net today) is enough → go ZERO (only if changing)
- conditions:
- condition: template
value_template: "{{ cond_now_is_min | bool }}"
- condition: template
value_template: "{{ not cond_total_solar_insufficient | bool }}"
- condition: template
value_template: "{{ battery_mode != 'zero' }}"
sequence:
- service: select.select_option
target: { entity_id: select.hw_battery_group_mode } # RENAME_ME
data: { option: zero }
- service: notify.notify
data:
title: "Battery Zero"
message: >
No charge: reserved {{ reserved_headroom_kwh }} + today_net {{ solar_remaining_net_kwh }}
≥ need {{ kwh_needed }}.
# (D) While charging → STOP if not cheapest anymore, or hysteresis reached, or net solar is enough
- conditions:
- condition: template
value_template: "{{ battery_mode == 'to_full' }}"
- condition: template
value_template: >-
{{ (not cond_now_is_min | bool)
or (kwh_needed <= (reserved_headroom_kwh - stop_margin_kwh))
or (not cond_total_solar_insufficient | bool) }}
sequence:
- service: select.select_option
target: { entity_id: select.hw_battery_group_mode } # RENAME_ME
data: { option: zero }
- service: notify.notify
data:
title: "Battery Zero"
message: >
Stopped (hysteresis / end of cheapest hour / enough net solar):
need={{ kwh_needed }} kWh, reserved={{ reserved_headroom_kwh }} kWh, margin={{ stop_margin_kwh }} kWh.
# (E) Safety: outside the cheapest hour always go ZERO (only if changing)
- conditions:
- condition: template
value_template: "{{ (not cond_now_is_min | bool) and (battery_mode != 'zero') }}"
sequence:
- service: select.select_option
target: { entity_id: select.hw_battery_group_mode } # RENAME_ME
data: { option: zero }
- service: notify.notify
data:
title: "Battery Zero"
message: "Outside the lowest-price hour (safety rule)."
⸻
Notes / prerequisites
• Entities to provide (rename in YAML):
• sensor.energy_today_current_hour_price, sensor.energy_today_min_price (your price provider)
• sensor.solcast_this_hour, sensor.solcast_next_hour, sensor.solcast_remaining_today, sensor.solcast_peak_time_today (Solcast)
• sensor.p1_net_power_w (grid net power +import/−export, W)
• sensor.pv_power_now_w (PV generation now, W)
• sensor.hw_batt1_soc/_power_w, sensor.hw_batt2_soc/_power_w, sensor.hw_batt3_soc/_power_w (SoC %, power W)
• select.hw_battery_group_mode with options: zero, standby, to_full
• Tuning tips
• If you see switching near full, increase stop_margin_kwh and/or run less frequently (e.g. every 30–60 mins).
• If you want to be conservative, keep use_fixed_base_load: true and adjust base_load_kw.
• If your Solcast sensors report in Wh, the unit-fix paths auto-convert.
Hope this helps! Feedback & improvements welcome it’s a first version so it needs tweaking, but i believe it is a good start!
@duco: any timeline on extra modes like ‘charge only’? especially useful now in Wintertime as I want to use my battery power during early evening price peaks, not in the afternoon when prices are still low ?
