🌞🔋 Since I last blogged about Home Assistant automations, I have taken a big step forward in sustainable living by installing a photo voltaic (PV) system with an energy storage unit. 🌱 This exciting upgrade has unlocked incredible opportunities for automating and optimizing energy usage in my home, making it smarter and greener than ever! 🏡✨
PV installation
I have a 6.3 kWp PV system with a 10 kWh battery storage unit installed in my utility room.
At the moment I am buying electricity at a flat rate of 1.2 PLN/kWh (0.28 €/kWh) including all taxes and fees. I can also sell excess energy back to the grid at dynamic hourly rates. The selling price depends on the time of the day and the current demand for energy. Charts below show the hourly rates for today and tomorrow, in PLN/MHh (to get the price in EUR or USD, just divide by ~4.3).
Requirements
To make the most of my solar energy system, I have outlined the following key requirements for efficient energy management and automation:
⚡️ Prioritize self-consumption over exporting energy to the grid, as it’s the most cost-effective approach for my setup.
🌍 Export energy to the grid only when the current and forecasted household consumption is fully covered by on-site generation.
🚫 Limit energy export to the grid when hourly electricity prices are zero or negative. Exporting under such conditions provides no financial benefit and only generates unnecessary heat in the inverter.
Inverter remote control
The most important part of the setup is the ability to control the inverter remotely. I have a Solis hybrid inverter, which is compatible with the Solis Cloud API. Unfortunatelly, the existing solis-sensor integration covers only the monitoring part, the control part is experimental and unstable, see #437.
This project also provided an excellent opportunity to develop my own custom integration for Home Assistant, tailored specifically for controlling the Solis inverter. You can find the source code and detailed documentation here:
Solis Cloud Control Integration
At the time of writing, the integration allows for controlling the inverter in the following ways:
Energy Production Forecasting
An essential component of my setup is the ability to accurately forecast energy production. By integrating Solcast with Home Assistant, I can access detailed energy production forecasts tailored to my PV installation.
This integration provides valuable insights into expected solar generation, enabling better planning and optimization of energy usage and storage.
Solis Inverter Modes of Operation
Solis inverters support three primary modes of operation:
🔋 Self-Use: In this mode, the inverter prioritizes using energy generated by the PV system to power the home and charge the battery. Any excess energy is exported to the grid.
⚡ In-Feed Priority: This mode prioritizes selling energy to the grid. The battery will neither charge nor discharge unless “Time Charging” is enabled and properly configured.
🌐 Off-Grid: Designed for installations without grid power, this mode isn’t relevant to this project.
Automation for inverter mode
By default, my installation operates in Self-Use mode, which effectively handles most scenarios. However, under specific conditions, automation switches the inverter to In-Feed Priority mode to maximize energy export. These conditions include:
- The house is in “away mode,” indicating no one is home and excess energy is available.
- The sun is above the horizon, ensuring sufficient energy production.
- Hourly electricity prices are favorable and exceed the minimum price of the day.
- The remaining energy production forecast for the day is sufficient to fully recharge the battery, ensuring optimal energy storage for later use.
- Today’s temperature is high enough to minimize excessive power consumption for heating, considering the current state of charge (SOC) of the battery.
- The current household power consumption is low, which aligns with the current battery SOC to optimize energy usage.
In all other scenarios, the inverter reverts to Self-Use mode to optimize energy usage and storage.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
- alias: Solar - mode optimization
id: solar_01
triggers:
- trigger: state
entity_id: input_boolean.away_mode
- trigger: time_pattern
minutes: 0
seconds: 30 # to get up-to-date hourly statistics
- trigger: time_pattern
minutes: 30
variables:
# Battery SOC [%]
battery_soc: "{{ states('sensor.solis_remaining_battery_capacity') | float }}"
battery_reserve: "{{ states('sensor.inverter_control_battery_reserve_soc') | float }}"
battery_max_charge: "{{ states('sensor.inverter_control_battery_max_charge_soc') | float }}"
battery_threshold_low: "{{ [battery_reserve | float + 10, battery_max_charge] | min }}"
battery_threshold_high: "{{ [battery_reserve | float + 40, battery_max_charge] | min }}"
# Prices [PLN/kWh]
price: "{{ states('sensor.solar_electricity_price_hourly_rate') | float }}"
price_min: "{{ states('sensor.solar_electricity_price_hourly_rate_min') | float }}"
price_valley_threshold: "{{ states('input_number.solar_electricity_price_valley_threshold') | float }}"
price_threshold: "{{ [price_min, price_valley_threshold] | max }}"
# PV forecast [kWh]
pv_forecast: "{{ states('sensor.solcast_pv_forecast_forecast_remaining_today') | float }}"
pv_forecast_threshold_low: 15
pv_forecast_threshold_high: 25
# Temperature forecast [°C]
temp_forecast: "{{ states('sensor.weather_temperature_today') | float }}"
temp_forecast_threshold: 5
# Power consumption [W]
power_consumption: "{{ states('sensor.solar_load_power') | float }}"
power_consumption_threshold: 500
# Export power [W]
export_power_capped: 1000
export_power_nominal: 13200
export_power: "{{ export_power_nominal if price > 0.01 else export_power_capped }}"
actions:
choose:
- conditions:
- condition: state
entity_id: input_boolean.away_mode
state: "on"
- condition: state
entity_id: sun.sun
state: above_horizon
- condition: template
alias: If price is decent
value_template: "{{ price > price_threshold }}"
- condition: template
alias: If PV forecast meets battery SOC criteria
value_template: >
{{ (pv_forecast > pv_forecast_threshold_high and battery_soc > battery_threshold_low) or
(pv_forecast > pv_forecast_threshold_low and battery_soc > battery_threshold_high) }}
- condition: template
alias: If temperature meets battery SOC criteria
value_template: >
{{ (temp_forecast > temp_forecast_threshold and battery_soc > battery_threshold_low) or
(temp_forecast <= temp_forecast_threshold and battery_soc > battery_threshold_high) }}
- condition: template
alias: If power consumption meets battery SOC criteria
value_template:
"{{ (power_consumption < power_consumption_threshold and battery_soc > battery_threshold_low) or
(power_consumption >= power_consumption_threshold and battery_soc > battery_threshold_high) }}"
sequence:
- action: select.select_option
entity_id: select.inverter_control_storage_mode
data:
option: Feed-In Priority
- action: number.set_value
entity_id: number.inverter_control_max_export_power
data:
value: "{{ export_power }}"
default:
- action: select.select_option
entity_id: select.inverter_control_storage_mode
data:
option: Self-Use
- action: number.set_value
entity_id: number.inverter_control_max_export_power
data:
value: "{{ export_power }}"
Selling energy during peak hour
The second automation schedules the inverter to sell energy during peak hour, when the following conditions are met:
- The house is working in eco mode. I have excess energy only when there is no one at home, or we’re going to leave soon.
- The price is decent and maximum of the day.
- Energy production forecast for tommorrow is excellent.
- Temperature forecast for tomorrow is high enough to minimize excessive power consumption for heating.
- Baterry is fully charged.
- There will be enough energy for sale considering typical nighttime consumption.
The most tricky part of the automation is to calculate the amount of energy that can be sold. Ask LLM if you need more details about the alghoritm 😜
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
- alias: Solar - schedule discharge slot
id: solar_02
triggers:
- trigger: state
entity_id: input_boolean.eco_mode
- trigger: time
at: "15:30:00" # when prices for the next day are available
- trigger: time
at: "16:00:00" # backup call
variables:
# Battery SOC [%]
battery_soc: "{{ states('sensor.solis_remaining_battery_capacity') | float }}"
battery_reserve: "{{ states('sensor.inverter_control_battery_reserve_soc') | float }}"
battery_max_charge: "{{ states('sensor.inverter_control_battery_max_charge_soc') | float }}"
battery_threshold_low: "{{ [battery_reserve | float + 15, battery_max_charge] | min }}"
battery_threshold_high: "{{ [battery_max_charge | float - 10, battery_threshold_low] | max }}"
battery_capacity: 10000 # Wh
battery_voltage: "{{ states('sensor.solis_battery_voltage') | float }}" # V
# PV forecast [kWh]
pv_forecast: "{{ states('sensor.solcast_pv_forecast_forecast_tomorrow') | float }}"
pv_forecast_threshold: 25
# Temperature forecast [°C]
temp_forecast: "{{ states('sensor.weather_temperature_tomorrow') | float }}"
temp_forecast_threshold: 5
# Peak price and time
peak_raw: "{{ states('sensor.solar_electricity_price_hourly_rate_max_next_raw') | from_json }}"
peak_price: "{{ peak_raw.price }}"
peak_price_threshold: >
{{ states('input_number.solar_electricity_price_peak_threshold') | float }}
peak_time_hours: 1
peak_time: >
{% set from = '%02d:00' | format(peak_raw.hour) %}
{% set to = '%02d:00' | format((peak_raw.hour + peak_time_hours) % 24) %}
{{ from ~ "-" ~ to }}
# Export energy [#Wh]
energy_to_export_threshold: 1000
energy_to_export: >
{% set energy_available = (battery_soc - battery_threshold_low) / 100 * battery_capacity %}
{% set sunset = as_timestamp(state_attr('sun.sun', 'next_setting')) %}
{% set sunrise = as_timestamp(state_attr('sun.sun', 'next_rising')) %}
{% if sunset > sunrise %}
{% set sunset = sunset - 86400 %}
{% endif %}
{% set night_duration = sunrise - sunset %}
{% set night_avg_consumption = 300 %}
{% set energy_night_consumption = (night_duration / 3600) * night_avg_consumption %}
{{ [energy_available - energy_night_consumption, 0] | max }}
# Export current [A]
export_current_max: 100
export_current: "{{ [energy_to_export / battery_voltage / peak_time_hours, export_current_max] | min }}"
actions:
choose:
- conditions:
- condition: state
entity_id: input_boolean.eco_mode
state: "on"
- condition: template
alias: If price is decent
value_template: "{{ peak_price > peak_price_threshold }}"
- condition: template
alias: If PV forecast is good
value_template: "{{ pv_forecast > pv_forecast_threshold }}"
- condition: template
alias: If temperature forecast is good
value_template: "{{ temp_forecast > temp_forecast_threshold }}"
- condition: template
alias: If battery SOC is high
value_template: "{{ battery_soc > battery_threshold_high }}"
- condition: template
alias: If there is enough energy to export
value_template: "{{ energy_to_export > energy_to_export_threshold }}"
sequence:
- action: text.set_value
entity_id: text.inverter_control_slot1_discharge_time
data:
value: "{{ peak_time }}"
- action: number.set_value
entity_id: number.inverter_control_slot1_discharge_current
data:
value: "{{ export_current }}"
- action: switch.turn_on
entity_id: switch.inverter_control_slot1_discharge
default:
- action: switch.turn_off
entity_id: switch.inverter_control_slot1_discharge
Expose excess energy mode
The third automation introduces an “excess energy” mode, which can be utilized by other automations to determine when surplus energy is available for consumption. For instance, a bathroom heater automation can leverage this mode to activate the electric heater only when excess energy is detected.
Excess power sensor is calculated as follows:
1
2
3
4
5
6
7
8
9
10
11
12
template:
- sensor:
- name: Solar Excess Power
unit_of_measurement: W
device_class: power
icon: mdi:lightning-bolt-outline
state: >
{% set pv = states('sensor.solar_pv_power') | float %}
{% set battery = states('sensor.solar_battery_power') | float %}
{% set load = states('sensor.solar_load_power') | float %}
{% set excess = pv - (load + battery) if battery >= 0 else 0 %}
{{ [excess, 0] | max }}
Automation for setting the excess energy mode is triggered by the following conditions:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
- alias: Solar - set excess energy mode "on"
id: solar_03
triggers:
- trigger: numeric_state
entity_id: sensor.solar_electricity_price_hourly_rate
below: input_number.solar_electricity_price_valley_threshold
- trigger: numeric_state
entity_id: sensor.solar_excess_power
above: 500
for:
minutes: 10
conditions:
- condition: numeric_state
entity_id: sensor.solar_electricity_price_hourly_rate
below: input_number.solar_electricity_price_valley_threshold
actions:
- action: input_boolean.turn_on
entity_id: input_boolean.solar_excess_energy_mode
- alias: Solar - set excess energy mode "off"
id: solar_04
triggers:
- trigger: numeric_state
entity_id: sensor.solar_electricity_price_hourly_rate
above: input_number.solar_electricity_price_valley_threshold
- trigger: numeric_state
entity_id: sensor.solar_excess_power
below: 100
for:
minutes: 10
conditions:
- condition: numeric_state
entity_id: sensor.solar_electricity_price_hourly_rate
above: input_number.solar_electricity_price_valley_threshold
actions:
- action: input_boolean.turn_off
entity_id: input_boolean.solar_excess_energy_mode
Summary
With a PV system of this size, I could have simply left the inverter in “Self-Use” mode, and the overall results wouldn’t differ significantly. However, I approached this entire experiment as an excellent opportunity to learn and have fun. By diving into the intricacies of energy management and automation, I gained valuable insights and hands-on experience that not only enhanced my technical skills but also made my home smarter and more efficient.
Don’t forget to add a ⭐️ to my project on GitHub if you find it useful! https://github.com/mkuthan/solis-cloud-control
Comments