Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
81 commits
Select commit Hold shift + click to select a range
bf83ac3
Add `fill_first` parameter to discharging
fboundy Aug 13, 2025
e25dfb5
Fix bugs in the tariff to_df method
fboundy Aug 14, 2025
b8b2633
Fix bugs in the tariff to_df method
fboundy Aug 14, 2025
e739624
Cost Fixes
fboundy Aug 16, 2025
596afc8
Cost Fixes
fboundy Aug 16, 2025
9fee29c
Fix logging bug
fboundy Aug 18, 2025
fb9dd04
Merge pull request #433 from stevebuk1/main
stevebuk1 Apr 5, 2026
9b1b672
Merge branch 'main' into dev
stevebuk1 Apr 6, 2026
0259933
Merge pull request #438 from stevebuk1/main
stevebuk1 Apr 7, 2026
19fe4c4
Modify to allow same file to be used in App/AddOn repo
stevebuk1 Apr 9, 2026
a9b313d
Update version to Beta-1 for Dev branch
stevebuk1 Apr 9, 2026
448a8d7
AddOn mods, find existing config.yaml
stevebuk1 Apr 11, 2026
d30b36d
Tidyup config.yaml file
stevebuk1 Apr 15, 2026
f5859dc
Merge branch 'patch' into dev
stevebuk1 Apr 15, 2026
70d4643
Update minor version to 1.
stevebuk1 Apr 15, 2026
00fe8a0
Merge branch 'patch' into dev
stevebuk1 Apr 19, 2026
99be771
Improve logging for consumption
stevebuk1 Apr 19, 2026
f308434
Improve logging for consumption history Part 2
stevebuk1 Apr 20, 2026
7acf675
Improvements to consumption logging, general update to dashboard
stevebuk1 Apr 20, 2026
006b912
Fixes for #424, allow read of empty entities
stevebuk1 Apr 20, 2026
d10f6fc
Fixes for #424
stevebuk1 Apr 21, 2026
dc633ad
Add logging for #424
stevebuk1 Apr 21, 2026
7bb90f4
Fix for #424
stevebuk1 Apr 22, 2026
f81e81a
Put interim slot summary in pvpy.py behind a logging switch
stevebuk1 Apr 22, 2026
466504d
#424, add debug line
stevebuk1 Apr 23, 2026
4b17044
#424 - introduce support for 2nd Sunsynk integration
stevebuk1 Apr 24, 2026
41ceb53
#424, add Sunsynk API reads to sunynk.py
stevebuk1 Apr 24, 2026
44401c0
#424, get inverter_sn from config.yaml
stevebuk1 Apr 24, 2026
1c77a73
Update pv_opt.py for App use to log to specific log file
stevebuk1 Apr 25, 2026
b5a653a
Update __main__ in pv_opt.py to suite use in new App repo
stevebuk1 Apr 26, 2026
46385ae
Update to __main__ for App use
stevebuk1 Apr 26, 2026
641886b
#424 - fix issues introduced when converting to classes
stevebuk1 Apr 26, 2026
4cbf87a
Auto-format code with Black and isort
github-actions[bot] Apr 26, 2026
8384c55
Add code for App/Addon reuse.
stevebuk1 Apr 27, 2026
ee186c9
Auto-format code with Black and isort
github-actions[bot] Apr 27, 2026
22f0f23
Reinstantiate comments removed in last commit
stevebuk1 Apr 27, 2026
b4a3c14
Merge branch 'dev' of https://github.com/stevebuk1/pv_opt into dev
stevebuk1 Apr 27, 2026
e398013
Merge b4a3c1429b2cf52a2d84d02ff00cf167c227d039 into 346eeca44b6dde7d6…
stevebuk1 Apr 27, 2026
87eb5c9
Update version to v5.1.0
github-actions[bot] Apr 27, 2026
e08c065
Reinstantiate correct version number
stevebuk1 Apr 27, 2026
d7dd917
Merge e08c065fa2e20cdaa9d06740aa04202818925f3b into 346eeca44b6dde7d6…
stevebuk1 Apr 27, 2026
09661e0
Update version to v5.1.0
github-actions[bot] Apr 27, 2026
2d5d7c7
Add logging for App version
stevebuk1 Apr 27, 2026
ded613f
Merge branch 'dev' of https://github.com/stevebuk1/pv_opt into dev
stevebuk1 Apr 27, 2026
728e2c1
Auto-format code with Black and isort
github-actions[bot] Apr 27, 2026
66ac171
Update pv_opt.py
stevebuk1 Apr 27, 2026
aa0e7d2
Changes for App/AddOn
stevebuk1 Apr 27, 2026
e471184
Fix for FutureWarnings in write_cost()
stevebuk1 Apr 27, 2026
47c9eb2
Fix FutureWarning
stevebuk1 Apr 27, 2026
695e2c7
2nd try at fixing futurewarning, roll version to Beta-5
stevebuk1 Apr 28, 2026
c545b32
Bugfix for #446
stevebuk1 Apr 28, 2026
1b5b157
Reformat version logging, update version to Beta-6
stevebuk1 Apr 28, 2026
65842cc
Merge branch 'main' into dev
stevebuk1 Apr 28, 2026
74c6728
Merge branch 'main' into dev
stevebuk1 Apr 29, 2026
1cf6719
Update version (additional bugfix for #418)
stevebuk1 Apr 29, 2026
bfbbf44
Update config.yaml to introduce new Sunsynk integration "Solar SunSynk".
stevebuk1 Apr 29, 2026
8924d99
Tidy up config.yaml, remove sjoubert solarman integration
stevebuk1 Apr 29, 2026
528e7fc
#450 add "Charge to 100%" functionality
stevebuk1 Apr 29, 2026
6c37ae6
#450: Change logging text.
stevebuk1 Apr 30, 2026
38911b5
Remove dead code, tidy up logging
stevebuk1 Apr 30, 2026
ece724e
Update soc_hold method
stevebuk1 May 2, 2026
40d45df
Fix bug in disable charge/disable discharge
stevebuk1 May 2, 2026
a50934f
#427, first code for Axle Energy
stevebuk1 May 2, 2026
7e78620
#427, fix bugs in Axle download routines
stevebuk1 May 4, 2026
f92cfbc
#424 change axle code to use forced export instead of synthetic consu…
stevebuk1 May 4, 2026
0054380
#424 load export price as soon as event is available.
stevebuk1 May 5, 2026
f5b77e0
restore axle_writes_suspended to previous state
stevebuk1 May 5, 2026
64dc822
#427: Add overrides to config.yaml, add option to allow Pv_opt to wri…
stevebuk1 May 5, 2026
1412dec
#444: algorithm changes for charge to 100%, #427 update config.yaml
stevebuk1 May 7, 2026
6469aa6
Fix bug in entity names, update readme with Fill First and Axle.
stevebuk1 May 8, 2026
7370d53
#451 - remove Optimised Pv Export as user selectable
stevebuk1 May 8, 2026
b7dec47
#451, bug fixes
stevebuk1 May 8, 2026
acee79e
#444- integrate fill_first into main code.
stevebuk1 May 8, 2026
e8513fd
remove trigger_entity, remove fill_first from readme
stevebuk1 May 8, 2026
ce4276b
Remove needless inverter update if hold mode already set, disable deb…
stevebuk1 May 9, 2026
85a0807
Logging enhancements, fix bug in Octopus Tariffs
stevebuk1 May 9, 2026
e8058cd
Fix bug in Fill First code
stevebuk1 May 9, 2026
46384ce
Correct bug in max discharge power - it should be inverter limited an…
stevebuk1 May 10, 2026
7753b34
#444, bugfixes.
stevebuk1 May 10, 2026
872bb4d
#451: improve logging
stevebuk1 May 10, 2026
8e53e74
Delete Battery_Temp from optimisation summary logging.
stevebuk1 May 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 6 additions & 58 deletions .dashboards/pv_opt_dashboard.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ views:
name: Solar
max: 5000
icon: mdi:solar-power-variant
- entity: sensor.solis_total_load
- entity: sensor.solis_house_load
name: Load
max: 5000
- entity: sensor.solis_battery_soc
Expand Down Expand Up @@ -54,7 +54,7 @@ views:
name: Time
state: >-
{{(as_local(as_datetime(states('sensor.date_time_iso')))|string)[11:16]}}
- entity: button.solis_sync_rtc
- entity: button.solis_inverter_sync_rtc
- type: markdown
content: >
<h3><u>Cost Summary (GBP)</u></h3>
Expand Down Expand Up @@ -142,61 +142,6 @@ views:
{{as_local(as_datetime(a['end_local'])).strftime(tf)}}
|{%endfor%}
title: Car Charging
- type: conditional
conditions:
- condition: state
entity: sensor.pvopt_tariff
state: agile
- condition: state
entity: select.pvopt_ev_charger
state_not: None
card:
type: markdown
content: >

<h3><u> Candidate Car Charging Plan</u></h3>


| Start | | | End ||| Energy ||| Price (p) |

|:--------|--|--|:-----------|--|--|:------------------:|--|--|:-----|{%
for a in state_attr('sensor.pvopt_candidate_car_slots',
'windows') %}

{% set tf = '%H:%M'%} |
{{as_local(as_datetime(a['start_local'])).strftime(tf)}} |||
{{as_local(as_datetime(a['end_local'])).strftime(tf)}} |||
{{a['charge_in_kwh'] | float | round(2)}}kWh ||| {{a['import'] |
float | round(2)}}p |{%endfor%}


Total Charge Added (kWh) =
{{'%8.2f'|format(state_attr('sensor.pvopt_candidate_car_slots','ev_total_charge')
| float)}}

Total Cost (p) =
{{'%8.0f'|format(state_attr('sensor.pvopt_candidate_car_slots','ev_total_cost')
| float)}}

<h3><u> Active Car Charging Plan</u></h3>


| Start | | | End ||| Energy ||| Price (p) |

|:--------|--|--|:-----------|--|--|:------------------:|--|--|:-----|{%
for a in state_attr('sensor.pvopt_car_slots', 'windows') %}

{% set tf = '%H:%M'%} |
{{as_local(as_datetime(a['start_local'])).strftime(tf)}} |||
{{as_local(as_datetime(a['end_local'])).strftime(tf)}} |||
{{a['charge_in_kwh'] | float | round(2)}}kWh ||| {{a['import'] |
float | round(2)}}p |{%endfor%}


Total Charge to add =
{{'%8.2f'|format(state_attr('sensor.pvopt_car_slots','ev_total_charge')
| float)}} kWh
title: Car Charging
- type: custom:stack-in-card
title: Optimised Charging
cards:
Expand Down Expand Up @@ -237,6 +182,8 @@ views:
entities:
- entity: switch.pvopt_allow_cyclic
name: Allow Cyclic Charge/Discharge
- entity: switch.pvopt_fill_first
name: Fill First during Optimise Discharge
- type: markdown
content: <h3>Solar
- type: entities
Expand Down Expand Up @@ -365,7 +312,7 @@ views:
span:
start: day
series:
- entity: sensor.solis_total_load
- entity: sensor.solis_house_load
float_precision: 0
extend_to: now
group_by:
Expand Down Expand Up @@ -720,3 +667,4 @@ views:
show:
legend_value: false
extend_to: false

40 changes: 27 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# PV Opt: Home Assistant Solar/Battery Optimiser v5.0.1
# PV Opt: Home Assistant Solar/Battery Optimiser v5.1.0

- [Introduction](#Introduction)
- [Pre-requisites](#pre-requisites)
Expand Down Expand Up @@ -67,6 +67,9 @@ PV Opt supports EV charging:
- If on the Agile tariff, PV Opt can calculate a car charging plan which can be used to control your EV charger/car via external HA automation scripts.
- If necessary Pv_opt automatically prevents house battery discharge during EV Charging.

Octopus Free Electricity and Octopus Saving sessions are fully supported (requires Octopus HA integration, see below)

Axle Energy export events are also supported (requires Axle VPP HA integration, see below)

<h2>Pre-requisites</h2>

Expand All @@ -85,9 +88,10 @@ This app is not stand-alone it requires the following:
| Studio Code Server | Alternative | Alternative to using `File Editor` to edit config files. Not convered in this guide. |
| <u><b>Integrations</b></u> | | |
| Solcast PV Solar Integration | Required | Retrieves solar forecast from Solcast into Home Assistant. |
| Octopus Energy | Optional | Used to retrieve tariff information and Octopus Saving Session details. For users on Intelligent Octopus Go, this is required for any addtional slots outside of the 6 hour period to be taken into account in the charge/discharge plan. |
| Octopus Energy | Optional | Used to retrieve tariff information and Octopus Saving Session / Free Electricity details. For users on Intelligent Octopus Go, this is required for any addtional slots outside of the 6 hour period to be taken into account in the charge/discharge plan. |
| Solax Modbus | Optional | Used to control Solis inverter directly. Support for two other integrations is now available (see below). Support inverter brands is possible using the API described below. |
| MyEnergi | Optional | For Intelligent Octopus Go users using a Zappi charger, used by Pv_opt to detect EV plugin and supply EV consumption history. |
| Axle VPP | Optional | For Axle Energy VPP users, allows PV_opt to take account of Axle discharge events by including the Axle export price in its calulations |

<h2>Step by Step Installation Guide</h2>

Expand Down Expand Up @@ -181,17 +185,17 @@ https://github.com/stevebuk1/pv_opt/blob/main/files/solis_hybrid.yaml

Follow instructions here: https://github.com/home-assistant/addons/blob/master/configurator/README.md

Navigate to Settings -> Addons -> File editor -> Configuration and set "Enforce Basepath" to "off".
Navigate to Settings -> Apps -> File editor -> Configuration and set "Enforce Basepath" to "off".

<h3>9. Install Samba Share and/or Studio Code Server Add-ons If Required</h3>
<h3>9. Install Samba Share and/or Studio Code Server Apps If Required</h3>

Both of these add-ons make it easier to edit text files on your HA Install but aren't strictly necessary. `Samba Share` also makes it easier to access the AppDaemon log files.
Both of these Apps make it easier to edit text files on your HA Install but aren't strictly necessary. `Samba Share` also makes it easier to access the AppDaemon log files.

<h3>10. Install AppDaemon</h3>

The <b>PV_Opt</b> python script currently runs under AppDaemon.

AppDaemon is a loosely coupled, multi-threaded, sandboxed python execution environment for writing automation apps for home automation projects, and any environment that requires a robust event driven architecture. The simplest way to install it on Home Assistantt is using the dedicated add-on:
AppDaemon is a loosely coupled, multi-threaded, sandboxed python execution environment for writing automation apps for home automation projects, and any environment that requires a robust event driven architecture. The simplest way to install it on Home Assistant is using the dedicated App:

1. Click the Home Assistant My button below to open the add-on on your Home Assistant instance:

Expand Down Expand Up @@ -258,7 +262,7 @@ And add the `client_user` and `client_password` keys to `secrets.yaml` like this
date_format: '%H:%M:%S'
format: '{asctime} {levelname:>8s}: {message}'

4. Open the AppDaemon Add-On via Settings: http://homeassistant.local:8123/hassio/addon/a0d7b954_appdaemon/info
4. Open the AppDaemon App via Settings: http://homeassistant.local:8123/hassio/addon/a0d7b954_appdaemon/info

5. Click on <b>Configuration</b> at the top

Expand Down Expand Up @@ -320,7 +324,7 @@ And add the `client_user` and `client_password` keys to `secrets.yaml` like this
06/04, 20:33:01 INFO AppDaemon: App initialization complete
```

That's it. AppDaemon is up and running. There is futher documentation for the [Add-on](https://github.com/hassio-addons/addon-appdaemon/blob/main/appdaemon/DOCS.md) and for [AppDaemon](https://appdaemon.readthedocs.io/en/latest/)
That's it. AppDaemon is up and running. There is futher documentation for the [App](https://github.com/hassio-addons/addon-appdaemon/blob/main/appdaemon/DOCS.md) and for [AppDaemon](https://appdaemon.readthedocs.io/en/latest/)

<h3>12. Install PV Opt from HACS</h3>

Expand All @@ -334,7 +338,7 @@ That's it. AppDaemon is up and running. There is futher documentation for the [A
Once downloaded AppDaemon should see the app and attempt to load it using the default configuration. Go back to the AppDaemon logs and this time open pv_opt.log. You should see:

```
16:53:23 INFO: ******************* PV Opt v5.0.0 *******************
16:53:23 INFO: ******************* PV Opt v5.1.0 *******************
16:53:23 INFO:
16:53:23 INFO: Time Zone Offset: 0.0 minutes
16:53:23 INFO: Reading arguments from YAML:
Expand All @@ -349,7 +353,7 @@ Once downloaded AppDaemon should see the app and attempt to load it using the de

<h3>13. Add an Automation to Restart AppDAemon when HA Restarts (Optional)</h3>

Restarts between Home Assistant and Add-Ons are not synchronised so it is helpful to set up an Automation to restart AppDAemon if HA is restarted. An example is shown below and included in this repo as `ha_restart_automation.yaml`. The `wait_template` section ensures that key integrations (in this case Solcast and Solax) have numeric values before AppDaemon is started.
Restarts between Home Assistant and Apps are not synchronised so it is helpful to set up an Automation to restart AppDaemon if HA is restarted. An example is shown below and included in this repo as `ha_restart_automation.yaml`. The `wait_template` section ensures that key integrations (in this case Solcast and Solax) have numeric values before AppDaemon is started.

alias: Restart AppDaemon on HA Restart
description: ""
Expand Down Expand Up @@ -398,7 +402,8 @@ The `config.yaml` file also includes all the other configuration used by PV Opt.

overwrite_ha_on_restart: true

<b><i>PV_Opt</b></i> needs to know the size of your battery and the power of your inverter: both when inverting battery to AC power and when chargingh tha battery. It will attempt to work these out from the data it has loaded (WIP) but you should check the following enitities in Home Assistant:
<b><i>PV_Opt</b></i> needs to know the size of your battery and the power of your inverter: both when inverting battery to AC power and when charging the battery. These are best set in
config.yaml. Check the following enitities in Home Assistant match your system:

<h3>System Parameters</h3>

Expand All @@ -417,9 +422,10 @@ These are the main parameters that will control how PV Opt runs:

| Parameter | Units | Entity | Default | Description |
| :----------------------- | :--------: | :---------------------------------------- | :-----: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Read Only Mode | `on`/`off` | `switch.pvopt_read_only` | On | Controls whether the app will actually control the inverter. Start with this on until you are happy the charge/discharge plan makes sense. |
| Read Only Mode | `on`/`off` | `switch.pvopt_read_only` | On | Controls whether the app will actually control the inverter. Start with this on until you are happy the charge/discharge plan makes sense. |
| Optimise Charging | `on`/`off` | `switch.pvopt_forced_charge` | On | Controls whether the app will calculate an Optimised plan. If `off` only the Base forecast will be updated. |
| Optimise Discharging | `on`/`off` | `switch.pvopt_forced_discharge` | On | Controls whether the app will allow for forced discharge as well as charge |
| Charge to 100% | `on`/`off` | `switch.pvopt_charge_to_100` | Off | Charge to 100% in the cheap rate, keeping as low a charge rate as possible such that 100% is reached smoothly by the end of the cheap rate period. During Winter, on a tariff that has a defined cheap rate (Go, IOG, Cosy etc) theres little benefit to be gained by optimizing battery use to achieve a battery SOC of "flat" just before the cheap rate begins, as errors in consumption and solcast mean that "flat" can happen early with a large consequential cost. Any benefit of leaving room for solar to fill the battery (which Pv_opt will normally do) is largely neglible in winter. Note: this mode is ultimately an overide of the prime aim of Pv_opt, which is to optimise based on cost, but is provided where error margins can lead to a frequent flat battery. It has no effect for Octopus Agile users nor if Optimise Discharging is selected (see below) |
| Optimise Discharging | `on`/`off` | `switch.pvopt_forced_discharge` | On | Controls whether the app will allow for forced discharge as well as charge |
| Allow Cyclic | `on`/`off` | `switch.pvopt_allow_cyclic` | On | Controls whether the app will allow cycles of alternating charge/discharge |
| Use Solar | `on`/`off` | `switch.pvopt_use_solar` | On | Controls whether the app will use the Solcast solar forecast. If set to Off no solar will be used but battery charging can still be optimised for a time-of use tariff. |
| Solcast Confidence Level | `number` | `number.pvopt_solcast_confidence_level` | Solcast | Selects which the Confidence Level for the Solcast forecast. Levels between 10% and 50% are weighted from the Solcast 10% and 50% forecasts. Levels between 50% and 90% are weighted from the Solcast 50% and 10% forecasts. |
Expand Down Expand Up @@ -498,6 +504,14 @@ Import and/or export tarifs can be set manually as follows. These can be combine
- period_start: "14:00"
price: 0.0

<h4>Axle Energy Information</h4>

| Parameter | Units | Entity | Default | Description |
| :------------------------- | :--------: | :--------------------------- | :-----: | :------------------------------------------------------------------------------------------------------- |
| Pv_opt control during Axle events | `True/False` | `axle_allow_pvopt_writes` | | Allow Pv_opt to write to inverter during Axle Energy events. Axle controls your inverter during an event but has been known to start late or not at all. Until Axle fix this it is recommended that Pv_opt should also control your inverter, which given the current export price will almost certainly schedule an export event. |
| Axle Energy export price | pence | `axle_export_rate_p` | | Price for Axle Energy Export events. Defaults to 100p which is the current price Axle offer for all events. Change it here if it changes. |


<h3>Tuning Parameters</h3>
These parameters will tweak how PV Opt runs:

Expand Down
Loading