GithubHelp home page GithubHelp logo

pv_opt's Introduction

PV Opt: Home Assistant Solar/Battery Optimiser v3.15.2

Solar / Battery Charging Optimisation for Home Assistant. This appDaemon application attempts to optimise charging and discharging of a home solar/battery system to minimise cost electricity cost on a daily basis using freely available solar forecast data from SolCast. This is particularly beneficial for Octopus Agile but is also benefeficial for other time-of-use tariffs such as Octopus Flux or simple Economy 7.

The application will integrate fully with Solis inverters which are controlled using any of:

Once installed it should require miminal configuration. Other inverters/integrations can be added if required or can be controlled indirectly using automations.

It has been tested primarily with Octopus tariffs but other tariffs can be manually implemented.

Don't Buy Me a Beer or a Coffee...

Although I'm very partial to both, a better home for anything you might like to contribute would be the Mountain Rescue Team that I am currently a probationer (trainee) with. The Team comprises 40 unpaid volunteers and is on call 24/7 365 days a year in all weathers. You can donate via JustGiving by clicking on this link:

Pre-requisites

This app is not stand-alone it requires the following:

Websites
Solcast Hobby Account Required Provides solar PV generation forecasts up to 10 times per day,
Add-ons
HACS Required Home Assistant Community Store - used to distribute the app and updates
AppDaemon Required Python execution environment
Mosquitto MQTT Broker Required Used to create Home Assistant Entities using MQTT Discovery .
File Editor Required Used to edit the appdaemon.yaml and config.yaml files. Alternatively you could use Samba Share or Studio Code Server
Samba Share Alternative Alternative to using File Editor to edit config files. Not convered in this guide.
Studio Code Server Alternative Alternative to using File Editor to edit config files. Not convered in this guide.
Integrations
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
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.

Step by Step Installation Guide

1. Get a Solcast Hobby Account

PV_Opt relies on solar forecasts data from Solcast. You can sign up for a Private User account here. This licence gives you 10 (it used to be 50 🙁) API calls a day.

2. Install HACS

  1. Install HACS: https://hacs.xyz/docs/setup/download
  2. Enable AppDaemon in HACS: https://hacs.xyz/docs/categories/appdaemon_apps/

3. Install the Solcast PV Solar Integration (v4.0.x)

  1. Install the integation via HACS: https://github.com/oziee/ha-solcast-solar
  2. Add the Integration via Settings: http://homeassistant.local:8123/config/integrations/dashboard
  3. Once installed configure using your Solcast API Key from (1) .
  4. Set up an automation to update according to your desired schedule. Once every 3 hours will work.

4. Install the Octopus Energy Integration (If Required)

This excellent integration will pull Octopus Price data in to Home Assistant. Solar Opt pulls data from Octopus independently of this integration but will extract current tariff codes from it if they are avaiable. If not it will either use account details supplied in secrets.yaml or explicitly defined Octopus tariff codes set in config.yaml.

5. Install the Integration to Control Your Inverter

At present this app only works directly with Solis hybrid inverters using either the Solax Modbus integration (https://github.com/wills106/homeassistant-solax-modbus) or the HA Core Modbus as described here: https://github.com/fboundy/ha_solis_modbus. Support for the Solarman integration (https://github.com/StephanJoubert/home_assistant_solarman) is in test. At the moment writing to the inverter is disabled pending further testing by Solarman users.

Solax Modbus:

  1. Install the integration via HACS: https://github.com/wills106/homeassistant-solax-modbus
  2. Add the Integration via Settings: http://homeassistant.local:8123/config/integrations/dashboard
  3. Configure the connection:
    Prefix solis
    Interface TCP/Ethernet
    Inverter Type solis
    IP Address IP of your datalogger
    TCP Port 502
    Protocol Modbus TCP
  4. Check that you have comms with the inverter and the various entities in the integration are populated with data

HA Core Modbus

Follow the Github instructions here: https://github.com/fboundy/ha_solis_modbus

Solarman

Follow the Github instructions here: https://github.com/StephanJoubert/home_assistant_solarman

6. Install the MQTT Integraion in Home Assistant

  1. Click on the button below to add the MQTT integration:

7. Install Mosquitto MQTT Broker

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

  2. Click on Install

  3. Configure the Add-On as per the documentation: http://homeassistant.local:8123/hassio/addon/core_mosquitto/documentation

  4. Either save the MQTT username and password in your secrets.yaml file or make a note of them for later.

8. Install File Editor

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

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.

10. Install AppDaemon

The PV_Opt 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:

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

  2. Click on Install

  3. Turn on Auto update

11. Configure AppDaemon

  1. Use File Editor (or one of the alternatives) to open /addon_configs/a0d7b954_appdaemon/appdaemon.yaml.

  2. The suggested configuration is as follows. This assumes that you are using secrets.yaml for your password information. If not then the secrets entry can be deleted and the MQTT client_user and client password will need to be entered explicitly.

     secrets: /homeassistant/secrets.yaml
     appdaemon:
       latitude: 54.729
       longitude: -2.991
       elevation: 175
       time_zone: Europe/London
       thread_duration_warning_threshold: 45
       app_dir: /homeassistant/appdaemon/apps
       plugins:
         HASS:
           type: hass
         MQTT:
           type: mqtt
           namespace: mqtt #
           verbose: True
           client_host: core-mosquitto
           client_port: 1883
           client_id: localad
           event_name: MQTT_MESSAGE 
           client_topics: NONE
           client_user: !secret mqtt-user
           client_password: !secret mqtt-password
    
     http:
       url: http://127.0.0.1:5050
     admin:
     api:
     hadashboard:
    
  3. It is also recommended that you add the following entries to appdaemon.yaml to improve AppDaemon logging. These settings assume that you have a /share/logs folder setup using Samba Share.

     logs:
       main_log:
         filename: /share/logs/main.log
         date_format: '%H:%M:%S'
       error_log:
         filename: /share/logs/error.log
         date_format: '%H:%M:%S'    
       pv_opt_log:
         name: PV_Opt
         filename: /share/logs/pv_opt.log
         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

  5. Click on Configuration at the top

  6. Click the 3 dots and Edit in YAML to add pandas and numpy as Python packages. Note that numpy has to be set to version 1.26.4 due to an unresolved compatability issue between Home Assistant and 2.0.0:

    init_commands: []
    python_packages:
      - pandas
      - numpy==1.26.4
    system_packages: []
    
    
  7. Go back to the Info page and click on Start

  8. Click on Log. Appdaemon will download and install numpy and pandas. Click on Refresh until you see:

     s6-rc: info: service init-appdaemon successfully started
     s6-rc: info: service appdaemon: starting
     s6-rc: info: service appdaemon successfully started
     s6-rc: info: service legacy-services: starting
     [12:54:30] INFO: Starting AppDaemon...
     s6-rc: info: service legacy-services successfully started
    
  9. Either click on Info followed by OPEN WEB UI and then Logs or open your main_log file from the location specified in step (3) above. You should see:

    13:16:24 INFO AppDaemon: AppDaemon Version 4.4.2 starting
    13:16:24 INFO AppDaemon: Python version is 3.11.6
    13:16:24 INFO AppDaemon: Configuration read from: /config/appdaemon.yaml
    13:16:24 INFO AppDaemon: Added log: AppDaemon
    13:16:24 INFO AppDaemon: Added log: Error
    13:16:24 INFO AppDaemon: Added log: Access
    13:16:24 INFO AppDaemon: Added log: Diag
    13:16:24 INFO AppDaemon: Added log: PV_Opt
    13:16:25 INFO AppDaemon: Loading Plugin HASS using class HassPlugin from module hassplugin
    13:16:25 INFO HASS: HASS Plugin Initializing
    13:16:25 WARNING HASS: ha_url not found in HASS configuration - module not initialized
    13:16:25 INFO HASS: HASS Plugin initialization complete
    13:16:25 INFO AppDaemon: Loading Plugin MQTT using class MqttPlugin from module mqttplugin
    13:16:26 INFO MQTT: MQTT Plugin Initializing
    13:16:26 INFO MQTT: Using 'localad/status' as Will Topic
    13:16:26 INFO MQTT: Using 'localad/status' as Birth Topic
    13:16:26 INFO AppDaemon: Initializing HTTP
    13:16:26 INFO AppDaemon: Using 'ws' for event stream
    13:16:26 INFO AppDaemon: Starting API
    13:16:26 INFO AppDaemon: Starting Admin Interface
    13:16:26 INFO AppDaemon: Starting Dashboards
    13:16:26 INFO HASS: Connected to Home Assistant 2023.11.1
    13:16:26 INFO AppDaemon: Starting Apps with 0 workers and 0 pins
    13:16:26 INFO AppDaemon: Running on port 5050
    13:16:26 INFO MQTT: Connected to Broker at URL core-mosquitto:1883
    13:16:26 INFO AppDaemon: Got initial state from namespace mqtt
    13:16:26 INFO MQTT: MQTT Plugin initialization complete
    13:16:26 INFO HASS: Evaluating startup conditions
    13:16:26 INFO HASS: Startup condition met: hass state=RUNNING
    13:16:26 INFO HASS: All startup conditions met
    13:16:26 INFO AppDaemon: Got initial state from namespace default
    13:16:28 INFO AppDaemon: Scheduler running in realtime
    13:16:28 INFO AppDaemon: Adding /homeassistant/appdaemon/apps to module import path
    13:16:28 INFO AppDaemon: App initialization complete
    

That's it. AppDaemon is up and running. There is futher documentation for the on the Add-on and for AppDaemon

12. Install PV Opt from HACS

  1. Make sure HACS "Enable AppDaemon apps discovery & tracking" is enabled - under integrations in HA https://hacs.xyz/docs/categories/appdaemon_apps/
  2. Go to HACS
  3. Select Automation
  4. Click on the 3 dots top right and Add Custom Repository
  5. Add this repository https://github.com/fboundy/pv_opt and select AppDaemon as the Category
  6. Download the app

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 v3.0.1 *******************
16:53:23     INFO: 
16:53:23     INFO: Time Zone Offset: 0.0 minutes
16:53:23     INFO: Reading arguments from YAML:
16:53:23     INFO: -----------------------------------
16:53:23     INFO: 
16:53:23     INFO: Checking config:
16:53:23     INFO: -----------------------
16:53:23  WARNING:     forced_charge       = True   Source: system default. Not in YAML.
16:53:23  WARNING:     forced_discharge    = True   Source: system default. Not in YAML.
16:53:23  WARNING:     read_only           = True   Source: system default. Not in YAML.

13. Add an Automation to Restart AppDAemon when HA Restarts (Optional)

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.

alias: Restart AppDaemon on HA Restart
description: ""
trigger:
  - event: start
    platform: homeassistant
condition: []
action:
  - service: hassio.addon_stop
    data:
      addon: a0d7b954_appdaemon
  - delay:
      hours: 0
      minutes: 1
      seconds: 0
      milliseconds: 0
  - wait_template: >
      {{(states('sensor.solcast_pv_forecast_forecast_today')| float(-1)>0) and
      (states('sensor.solis_battery_soc')| float(-1)>0)}}
    continue_on_timeout: true
  - service: hassio.addon_start
    data:
      addon: a0d7b954_appdaemon
mode: single

Configuration

If you have the Solcast, Octopus and Solax integrations set up as specified above, there should be minimal configuration required.

If you are running a different integration or inverter brand you will need to edit the config.yaml file to select the correct inverter_type. You may also need to change the device_name. This is the name given to your inverter by your integration. The default is solis but this can also be changed in config.yaml.

inverter_type: SOLIS_CORE_MODBUS
device_name: solis

The config.yaml file also includes all the other configuration used by PV Opt. If you are using the default setup you shouldn't need to change this but you can edit anything by un-commenting the relevant line in the file. The configuration is grouped by inverter/integration and should be self-explanatory. Once PV Opt is installed the config is stored within entities in Home Assistant. It you want these over-ritten please ensure that overwrite_ha_on_restart is set to true:

overwrite_ha_on_restart: true

PV_Opt 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:

System Parameters

Parameter Units Entity Default Value
Battery Capacity Wh number.pvopt_batter_capacity_wh 10000
Inverter Power W number.pvopt_inverter_power_watts 3600
Charger Power W number.pvopt_charger_power_watts 3500
Inverter Efficiency % number.pvopt_inverter_efficiency 97%
Charger Efficiency % number.pvopt_charger_efficiency 91%

There are then only a few things to control the optimisation process. These have been grouped as follows:

Control Parameters

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.
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
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.
Optimser Frequency minutes number.pvopt_optimise_frequency_minutes 10 Frequency of Optimiser calculation

Consumption Parameters

These parameters will define how PV Opt estimates daily consumption:
Parameter Units Entity Default Description
Use Consumption History on/off switch.pvopt_use_consumption_history On Toggles whether to use actual consumption history or an estimated daily consumption
Consumption History Parameters
Load History Days days number.pvopt_consumption_history_days 7 Number of days of consumption history to use when predicting future load
Load Margin % number.pvopt_consumption_margin 10% Margin to add to historic load for forecast (safety factor)
Weekday Weighting fraction number.pvopt_day_of_week_weighting 0.5 Defines how much weighting to give to the day of the week when averaging the load. 0.0 will use the simple average of the last n days based on load_history_days and 1.0 will just used the same day of the week within that window. Values inbetween will weight the estimate accordingly. If every day is the same use a low number. If your usage varies daily use a high number.
Daily Consumption Parameters
Daily Consumption kWh number.pvopt_daily_consumption_kwh 17 Estimated daily consumption to use when predicting future load
Shape Consumption Profile on/off switch.pvopt_shape_consumption_profile On Defines whether to shapoe the consumption to a typical daily profile (on) or to assume constant usage (off)

Tuning Parameters

These parameters will tweak how PV Opt runs:
Parameter Units Entity Default Description
Pass threshold % number.pvopt_pass_throshold_p 4p The incremental cost saving that each iteration of the optimiser needs to show to be included. Reducing the threshold may marginally reduce the predicted cost but have more marginal charge windows.
Discharge threshold % number.pvopt_discharge_throshold_p 5p The incremental cost saving that each iteration of the discharge optimiser needs to show to be included. Reducing the threshold may marginally reduce the predicted cost but have more marginal discharge windows.
Slot threshold % number.pvopt_slop_throshold_p 1p The incremental cost saving that each 30 minute slot of the optimiser needs to show to be included. Reducing the threshold may marginally reduce the predicted cost but have more marginal charge/discharge windows.
Power Resolution W number.pvopt_forced_power_group_tolerance 100 The resolution at which forced charging / discharging is reported. Changing this will change the reporting of the charge plan but not the actual detail of it.

Alternative Tariffs

PV Opt can also check what each day would have cost using any combination of Octopus tariffs. Run over time this can give you an idea of whether it would be worth switching. To enable this simply add a block like this to `config.yaml`:
id_daily_solar: sensor.{device_name}_power_generation_today
alt_tariffs:
  - name: Agile_Fix
    octopus_import_tariff_code: E-1R-AGILE-23-12-06-G
    octopus_export_tariff_code: E-1R-OUTGOING-FIX-12M-19-05-13-G

  - name: Eco7_Fix
    octopus_import_tariff_code: E-2R-VAR-22-11-01-G
    octopus_export_tariff_code: E-1R-OUTGOING-FIX-12M-19-05-13-G

  - name: Flux
    octopus_import_tariff_code: E-1R-FLUX-IMPORT-23-02-14-G
    octopus_export_tariff_code: E-1R-FLUX-EXPORT-23-02-14-G

In this example three alternatives are tested. For each tariff pair the Base and Optimised net cost for yesterday are calculated and saved to an entity called sensor.pvopt_opt_cost_name. The state of this entity is the optimised cost and the base cost is saved as the net_base attribute.

Output

The app always produces a Base forecast of future battery SOC and the associated grid flow based on the forecast solar performance, the expected consumption and prices with no forced charging or discharging from the grid.. The total cost for today and tomorrow is written to sensor.pvopt_base_cost and the associated SOC vs time is written to the attributes of this entity allowing it to be graphes using apex-charts.

If Optimise Charging is enabled, an optimsised charging plan is calculated and writtemt to sensor.pvopt_opt_cost. This will also include a list of forced charge and discharge windows.

The easiest way to control and visualise this is through the pvopt_dashboard.yaml Lovelace yaml file included in this repo. Note that you will need to manually paste this into a dashboard and edit the charts to use the correct Octopus Energy sensors:

Alt text

This dashboards uses a couple of template sensors and time which will need adding to /homeassistant/configuration.yaml:

template:
  - sensor:
    - name: "Solis Grid Export Power"
      unique_id: solis_grid_export_power
      unit_of_measurement: W
      device_class: power
      state_class: measurement
      state: >-
        {{max(states('sensor.solis_meter_active_power') | float(0),0)}}    

    - name: "Solis Grid Import Power"
      unique_id: solis_grid_import_power
      unit_of_measurement: W
      device_class: power
      state_class: measurement
      state: >-
        {{max(-(states('sensor.solis_meter_active_power') | float(0)),0)}}

sensor:
  - platform: time_date
    display_options:
      - 'time'
      - 'date'
      - 'date_time'
      - 'date_time_utc'
      - 'date_time_iso'    

The dashboards also depend on the following Frontend components from HACS:

  • template-entity-row
  • bar-card
  • card-mod
  • Stack In Card
  • layout-card
  • apexcharts-card

Development - Adding Additional Inverters: the PV Opt API

PV Opt is designed to be pluggable. A simple API is used to control inverters. This is defined as follows:

Inverter Type

Each inverter type is defined by a string in the config.yaml file. This should be of the format: BRAND_INTEGRATION for example SOLIS_SOLAX_MODBUS.

Inverter Module

PV Opt expects one module per inverter brand named brand.py which includes drives for all integrations/models associated with that brand. For example solis.py includes the drivers for SOLIS_SOLAX_MODBUS, SOLIS_CORE_MODBUS and SOLIS_SOLARMAN

Each module exposes the following:

Classes

The module exposes a single class InverterController(inverter_type, host). The two required initialisation parameters are:

Parameter Type Description
inverter_type str The inverter_type string from the config.yaml file
host PVOpt The instance of PV Opt that has instantiated the inverter class. This allows the class to, for instance, write to the main log file simply be setting self.log=host.log and then calling self.log()

Class Attributes

The InverterController class must expose the following:

Attribute Key Type Description Example from SOLIS_SOLAX_MODBUS
.config dict This dict contains all the names of all the entities that PV Opt requires to run plus a few other parameters that are common to all inverters. Some entries must be an entity_id: keys for these itesms start with id_. Others may be enitity_ids or numbers. entity_ids should ideally include {device_name} to allow for the subsititution of a defined device name where appropriate.
maximum_dod_percent str / int Maximum depth of discharge - nay be an entity_id or a number number.{device_name}_battery_minimum_soc
update_cycle_seconds int Time in seconds between HA updates 15
supports_hold_soc bool Flags whether the integration supports holding a fixed SOC true
id_battery_soc str entity_id of Battery State of Charge number.{device_name}_battery_soc
id_consumption_today str entity_id of Daily Consumption Total sensor.{device_name}_house_load_today
id_grid_import_today str entity_id of Daily Grid Import Total sensor.{device_name}_grid_import_today
id_grid_export_today str entity_id of Daily Grid Export Total sensor.{device_name}_grid_export_today
.brand_config dict This dict contains all the names of all the entities that this brand/integration requires. These are only exposed for logging purposes and to allow the plug-in to use methods from the main app that use query entity_ids such as .get_config(entity_id) . A limited number of examples are given as this will vary for each plug-in.
battery_voltage str / int Battery voltage for converting power to current - nay be an entity_id or a number sensor.{device_name}_battery_voltage
id_timed_charge_start_hours str entity_id of Timed Charge Start Hours number.{device_name}_timed_charge_start_hours
id_timed_charge_start_minutes str entity_id of Timed Charge Start Minutes number.{device_name}_timed_charge_start_minutes
id_timed_charge_end_hours str entity_id of Timed Charge End Hours number.{device_name}_timed_charge_end_hours
id_timed_charge_end_minutes str entity_id of Timed Charge End Minutes number.{device_name}_timed_charge_end_minutes
id_timed_charge_current str entity_id of Timed Charge Current number.{device_name}_timed_charge_current
.status dict This dict reports the current status of the inverter
charge dict Dict of the Timed Charge Status with the following keys: active: bool, start: datetime, end: datetime, power: float
discharge dict Dict of the Timed Discharge Status with the following keys: active: bool, start: datetime, end: datetime, power: float
hold_soc dict Dict of the Hold_SOC Status with the following keys: active: bool, soc: int

Methods

The InverterController class must expose the following:

Method Parameters Returns Description
.enable_timed_mode() - None Switches the inverter mode to support timed changing and discharging
.control_charge() enable: bool None Enable or disable timed charging
start: datetime, optional Start time of timed slot (default = don't set start)
end: datetime, optional End time of timed slot (default = don't set end)
power: float, optional Maximum power of timed slot (default = don't set power)
.control_discharge() enable: bool None Enable or disable timed discharging
start: datetime, optional Start time of timed slot (default = don't set start)
end: datetime, optional End time of timed slot (default = don't set end)
power: float, optional Maximum power of timed slot (default = don't set power)
.hold_soc() soc None Switch inverter mode to hold specified SOC (if supported)

PV Opt Methods Available to the Inverter

The following methods may be useful for the inverter to call. If self.host is initialised to host they can be called using self.host.method(). As PV Opt is a sub-class of hass.HASS it includes all the AppDAemon methods listed here: https://appdaemon.readthedocs.io/en/latest/AD_API_REFERENCE.html

Method Parameters Returns / Decsription
self.host.log string Write string to the log file.
level: str, optional Optionally set the error level (default = INFO)
self.host.get_state() entity_id Home Assitant state of the entity
attributes: str, optional If attributes is set the attribute rather than the state is returned. If attribute is set to all a dict of all attributes is returned.
self.host.set_state() state Set the Home Assitant state of the entity and optionally the attributes. Returns a dict of the new state
entity_id: str
attributes: dict, optional
self.host.entity_exists() entity_id: str bool that confirms whether an entity exists in Home Assistant
self.host.call_service() service: str Call service in Home Assistant
data: dict, optional Data to be supplied to the service e.g. for writing to the Solis Modbus registers: data={"hub": "solis", "slave": 1, "address": 43011, "value": 15}

pv_opt's People

Contributors

fboundy avatar stevebuk1 avatar szosszenet avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

pv_opt's Issues

Check Charge Times and Mode - Clip and Max Charge Current

Describe the bug
The app isn't fully checking that the charge/discharge times and inverter mode are correct on each run.

To Reproduce
Steps to reproduce the behavior:

  1. Go to '...'
  2. Click on '....'
  3. Scroll down to '....'
  4. See error

Expected behavior
A clear and concise description of what you expected to happen.

Screenshots
If applicable, add screenshots to help explain your problem.

Desktop (please complete the following information):

  • OS: [e.g. iOS]
  • Browser [e.g. chrome, safari]
  • Version [e.g. 22]

Smartphone (please complete the following information):

  • Device: [e.g. iPhone6]
  • OS: [e.g. iOS8.1]
  • Browser [e.g. stock browser, safari]
  • Version [e.g. 22]

Additional context
Add any other context about the problem here.

Unexpected error running initialize() for pv_opt

I'm terribly sorry, been trying to work out in the past two days what's going on, but now I'm at loss...

On version 3.6.0 and as far as loading data everything looks just fine:

20:08:33 INFO: ******************* PV Opt v3.6.0 *******************
20:08:33 INFO:
20:08:33 INFO: Local timezone set to GB
20:08:33 INFO: Time Zone Offset: 0.0 minutes
20:08:33 INFO: Inverter type: SOLIS_SOLAX_MODBUS: inverter module: solis.py
20:08:33 INFO: Reading arguments from YAML:
20:08:33 INFO: -----------------------------------
20:08:33 INFO: consumption_history_days = 2 2: value in YAML
20:08:33 INFO: battery_capacity_wh = 5000 5000: value in YAML
20:08:33 INFO: charger_power_watts = 3600 3600: value in YAML
20:08:33 INFO: inverter_power_watts = 3600 3600: value in YAML
20:08:33 INFO: id_solcast_today = sensor.solcast_pv_forecast_forecast_today 0.0: value(s) in YAML
20:08:33 INFO: id_solcast_tomorrow = sensor.solcast_pv_forecast_forecast_tomorrow 1.53035: value(s) in YAML
20:08:33 WARNING: consumption_margin = 10 10: system default. Unable to read from HA entities listed in YAML. No default in YAML.
20:08:33 INFO: octopus_account = ************* ******** YAML default value. No default defined.
20:08:33 INFO: octopus_api_key = ************************ s*********************: YAML default value. No default defined.
20:08:33 INFO: battery_voltage = sensor.solis_battery_voltage 52.1: value in YAML
20:08:33 INFO: update_cycle_seconds = 15 15: value in YAML
20:08:33 INFO: maximum_dod_percent = number.solis_battery_minimum_soc 15.0: value in YAML
20:08:33 INFO: id_consumption_today = sensor.solis_house_load_today 22.6: value(s) in YAML
20:08:33 INFO: id_grid_import_today = sensor.solis_grid_import_today 19.2: value(s) in YAML
20:08:33 INFO: id_grid_export_today = sensor.solis_grid_export_today 0.3: value(s) in YAML
20:08:33 INFO: id_battery_soc = sensor.solis_battery_soc 15.0: value(s) in YAML
20:08:33 INFO: id_timed_charge_start_hours = number.solis_timed_charge_start_hours 20.0: value(s) in YAML
20:08:33 INFO: id_timed_charge_start_minutes = number.solis_timed_charge_start_minutes 30.0: value(s) in YAML
20:08:33 INFO: id_timed_charge_end_hours = number.solis_timed_charge_end_hours 21.0: value(s) in YAML
20:08:33 INFO: id_timed_charge_end_minutes = number.solis_timed_charge_end_minutes 0.0: value(s) in YAML
20:08:33 INFO: id_timed_charge_current = number.solis_timed_charge_current 30.0: value(s) in YAML
20:08:33 INFO: id_timed_discharge_start_hours = number.solis_timed_discharge_start_hours 0.0: value(s) in YAML
20:08:33 INFO: id_timed_discharge_start_minutes = number.solis_timed_discharge_start_minutes 0.0: value(s) in YAML
20:08:33 INFO: id_timed_discharge_end_hours = number.solis_timed_discharge_end_hours 0.0: value(s) in YAML
20:08:33 INFO: id_timed_discharge_end_minutes = number.solis_timed_discharge_end_minutes 0.0: value(s) in YAML
20:08:33 INFO: id_timed_discharge_current = number.solis_timed_discharge_current 60.0: value(s) in YAML
20:08:33 INFO: id_timed_charge_discharge_button = button.solis_update_charge_discharge_times 2024-02-01T16:30:20.738670+00:00: value(s) in YAML
20:08:33 INFO: id_inverter_mode = select.solis_energy_storage_control_switch Timed Charge/Discharge: value(s) in YAML
20:08:33 INFO: id_daily_solar = sensor.solis_power_generation_today 2.9: value(s) in YAML
20:08:33 INFO:
20:08:33 INFO: Checking config:
20:08:33 INFO: -----------------------
20:08:33 WARNING: forced_charge = True True: system default. Not in YAML.
20:08:33 WARNING: forced_discharge = True True: system default. Not in YAML.
20:08:33 WARNING: read_only = True True: system default. Not in YAML.
20:08:33 WARNING: allow_cyclic = False False: system default. Not in YAML.
20:08:33 WARNING: optimise_frequency_minutes = 10 10: system default. Not in YAML.
20:08:33 WARNING: slot_threshold_p = 1.0 1.0: system default. Not in YAML.
20:08:33 WARNING: day_of_week_weighting = 0.5 0.5: system default. Not in YAML.
20:08:33 WARNING: pass_threshold_p = 4.0 4.0: system default. Not in YAML.
20:08:33 WARNING: octopus_auto = True True: system default. Not in YAML.
20:08:33 WARNING: inverter_efficiency_percent = 97 97: system default. Not in YAML.
20:08:33 WARNING: charger_efficiency_percent = 91 91: system default. Not in YAML.
20:08:33 WARNING: inverter_loss_watts = 100 100: system default. Not in YAML.
20:08:33 WARNING: solar_forecast = Solcast Solcast: system default. Not in YAML.
20:08:33 WARNING: consumption_grouping = mean mean: system default. Not in YAML.
20:08:33 WARNING: forced_power_group_tolerance = 100 100: system default. Not in YAML.
20:08:33 WARNING: id_battery_charge_power = sensor.solis_battery_input_energy 0.0: system default. Not in YAML.
20:08:33 WARNING: id_inverter_ac_power = sensor.solis_active_power -90.0: system default. Not in YAML.
20:08:33 WARNING: supports_hold_soc = True True: system default. Not in YAML.
20:08:33 WARNING: id_backup_mode_soc = number.solis_backup_mode_soc 100.0: system default. Not in YAML.
20:08:33 INFO:
20:08:33 INFO: Syncing config with Home Assistant:
20:08:33 INFO: -----------------------------------
20:08:33 INFO:
20:08:33 INFO: Config Item HA Entity Current State
20:08:33 INFO: ----------- --------- -------------
20:08:33 INFO: forced_charge switch.pvopt_forced_charge on
20:08:33 INFO: forced_discharge switch.pvopt_forced_discharge on
20:08:33 INFO: read_only switch.pvopt_read_only on
20:08:33 INFO: allow_cyclic switch.pvopt_allow_cyclic off
20:08:33 INFO: optimise_frequency_minutes number.pvopt_optimise_frequency_minutes 10
20:08:33 INFO: slot_threshold_p number.pvopt_slot_threshold_p 1.0
20:08:33 INFO: day_of_week_weighting number.pvopt_day_of_week_weighting 0.5
20:08:33 INFO: pass_threshold_p number.pvopt_pass_threshold_p 4.0
20:08:33 INFO: battery_capacity_wh number.pvopt_battery_capacity_wh 5000
20:08:33 INFO: inverter_efficiency_percent number.pvopt_inverter_efficiency_percent 97
20:08:33 INFO: charger_efficiency_percent number.pvopt_charger_efficiency_percent 91
20:08:33 INFO: charger_power_watts number.pvopt_charger_power_watts 3600
20:08:33 INFO: inverter_power_watts number.pvopt_inverter_power_watts 3600
20:08:33 INFO: inverter_loss_watts number.pvopt_inverter_loss_watts 100
20:08:33 INFO: solar_forecast select.pvopt_solar_forecast Solcast
20:08:33 INFO: consumption_history_days number.pvopt_consumption_history_days 2
20:08:33 INFO: consumption_margin number.pvopt_consumption_margin 10
20:08:33 INFO: consumption_grouping select.pvopt_consumption_grouping mean
20:08:33 INFO: forced_power_group_tolerance number.pvopt_forced_power_group_tolerance 100
20:08:33 INFO:
20:08:33 INFO: Loading Contract:
20:08:33 INFO: -----------------
20:08:33 INFO: Trying to auto detect Octopus tariffs:
20:08:33 INFO: Found import entity event.octopus_energy_electricity_current_day_rates
20:08:33 INFO: Found export entity event.octopus_energy_electricity
export_current_day_rates
20:08:34 INFO: Contract tariffs loaded OK
20:08:34 INFO: Import: E-1R-AGILE-FLEX-22-11-25-N Start: 2024-01-23 13:00:00+0000 End: 2024-02-02 23:00:00+0000
20:08:34 INFO: Export: E-1R-OUTGOING-FIX-12M-19-05-13-N Start: 2019-05-15 23:00:00+0000 End: N/A
20:08:34 INFO: AGILE tariff detected. Rates will update at 16:00 daily
20:08:34 INFO:
20:08:34 INFO:
20:08:34 INFO: Found Octopus Savings Events entity: event.octopus_energy_a
******_octoplus_saving_session_events
20:08:34 INFO:
20:08:34 INFO: No upcoming Octopus Saving Events detected or joined:
20:08:34 INFO: Finished loading contract
20:08:34 INFO:
20:08:34 INFO: Running initial Optimisation:
20:08:34 INFO:
20:08:34 INFO:
20:08:34 INFO: Found Octopus Savings Events entity: event.octopus_energy_a_a2720466_octoplus_saving_session_events
20:08:34 INFO:
20:08:34 INFO: No upcoming Octopus Saving Events detected or joined:
20:08:34 INFO:
20:08:34 INFO: Starting Opimisation with discharge enabled
20:08:34 INFO: -------------------------------------------
20:08:34 INFO:
20:08:34 INFO: Checking tariffs:
20:08:34 INFO: -----------------
20:08:34 INFO: Import: E-1R-AGILE-FLEX-22-11-25-N Start: 2024-01-23 13:00:00+0000 End: 2024-02-02 23:00:00+0000
20:08:34 INFO: Export: E-1R-OUTGOING-FIX-12M-19-05-13-N Start: 2019-05-15 23:00:00+0000 End: N/A
20:08:34 INFO: AGILE tariff detected. Rates will update at 16:00 daily
20:08:34 INFO: - Tariffs OK
20:08:34 INFO:
20:08:34 INFO: Solcast forecast loaded OK
20:08:34 INFO: Getting expected consumption data
20:08:34 INFO: - Estimated consumption from sensor.solis_house_load_today loaded OK
20:08:34 INFO: Initial SOC: 15.0
20:08:34 INFO: Calculating Base flows
20:08:35 INFO:
20:08:35 INFO: Retrieved day ahead forecast for period 25/01 23:00 - 02/02 22:00 for tariff E-1R-AGILE-FLEX-22-11-25-N
20:09:34 INFO: >>> Agile Callback Handler

and on the error log I see

20:08:35 WARNING pv_opt: ------------------------------------------------------------
20:08:35 WARNING pv_opt: Unexpected error running initialize() for pv_opt
20:08:35 WARNING pv_opt: ------------------------------------------------------------
20:08:35 WARNING pv_opt: Traceback (most recent call last):
File "/usr/lib/python3.11/site-packages/appdaemon/app_management.py", line 162, in initialize_app
await utils.run_in_executor(self, init)
File "/usr/lib/python3.11/site-packages/appdaemon/utils.py", line 304, in run_in_executor
response = future.result()
^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/concurrent/futures/thread.py", line 58, in run
result = self.fn(*self.args, **self.kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/homeassistant/appdaemon/apps/pv_opt/pv_opt.py", line 321, in initialize
self.optimise()
File "/usr/lib/python3.11/site-packages/appdaemon/adbase.py", line 35, in f_app_lock
return f(*args, **kw)
^^^^^^^^^^^^^^
File "/homeassistant/appdaemon/apps/pv_opt/pv_opt.py", line 1322, in optimise
self.base_cost = self.contract.net_cost(self.base)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/homeassistant/appdaemon/apps/pv_opt/pvpy.py", line 442, in net_cost
imp_df = self.imp.to_df(start, end, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/homeassistant/appdaemon/apps/pv_opt/pvpy.py", line 230, in to_df
newindex = pd.date_range(x.index[0], df.index[-1], freq="30T")
~~~~~~~~^^^^
File "/usr/lib/python3.11/site-packages/pandas/core/indexes/base.py", line 5385, in getitem
return getitem(key)
^^^^^^^^^^^^
File "/usr/lib/python3.11/site-packages/pandas/core/arrays/datetimelike.py", line 379, in getitem
result = cast("Union[Self, DTScalarOrNaT]", super().getitem(key))
^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/site-packages/pandas/core/arrays/_mixins.py", line 284, in getitem
result = self._ndarray[key]
~~~~~~~~~~~~~^^^^^
IndexError: index -1 is out of bounds for axis 0 with size 0

20:08:35 WARNING pv_opt: ------------------------------------------------------------

Tried deleting and reinstalling PV OPT unfortunately no difference...

PV_opt costs not displaying

AppDaemon Current version: 0.16.4
PV_OPT VERSION = "3.6.2"

  • PVOPT Results don't display on dashboard
  • sensor.pvopt_base_cost gives errors to display and errors in logs
  • FutureWarning: 'T' is deprecated and will be removed in a future version
  • FutureWarning: 'H' is deprecated and will be removed in a future version.

Any help appreciated. Do you need any more log files?
pv_opt.log
main.log

User configuration --- EDIT AWAY! ---

inverter_type: "SOLIS_SOLAX_MODBUS"

inverter_type: "SOLIS_CORE_MODBUS"

inverter_type: SOLIS_SOLARMAN

device_name: solis_modbus
consumption_history_days: 7

If true the current config in HA will be over-written with that in the config.yaml.

overwrite_ha_on_restart: true
redact_personal_data_from_log: true

Dashboard screenshot:
image

PV_OPT log:
I have attached the log files
01:00:02 INFO: Output written to sensor.pvopt_optimiser_elapsed
01:00:02 INFO: Average unit cost today: 0.00p/kWh
01:00:02 INFO: Output written to sensor.pvopt_unit_cost_today
01:00:02 WARNING: pv_opt: Entity sensor.pvopt_base_cost not found in namespace default
01:00:02 INFO: Output written to sensor.pvopt_base_cost
01:00:03 WARNING: pv_opt: Entity sensor.pvopt_opt_cost not found in namespace default
01:00:03 INFO: Output written to sensor.pvopt_opt_cost
01:00:03 INFO: Output written to sensor.pvopt_charge_start
01:00:03 INFO: Output written to sensor.pvopt_charge_end
01:00:03 INFO: Output written to sensor.pvopt_charge_current
01:00:03 INFO: Output written to sensor.pvopt_soc_h1
01:00:03 INFO: Output written to sensor.pvopt_soc_h4
01:00:03 INFO: Output written to sensor.pvopt_soc_h8
01:00:03 INFO: Output written to sensor.pvopt_soc_h12
01:00:03 INFO: Read only mode enabled. Not querying inverter.
01:10:00 INFO: Optimiser triggered by Scheduler

Main log
11:50:03 WARNING HASS: Error setting Home Assistant state default.sensor.pvopt_opt_cost, {'state': 0.0, 'attributes': {'friendly_name': 'PV Opt Optimised Cost', 'device_class': 'monetary', 'state_class': 'measurement', 'unit_of_measurement': 'GBP', 'cost_today': 0.0, 'cost_tomorrow': 0.0, 'soc': [{'period_start': '2024-02-06T11:30:00+00:00', 'soc': 68.5}, {'period_start': '2024-02-06T12:00:00+00:00', 'soc': .....
11:50:03 WARNING HASS: Code: 400, error: {"message":"Invalid JSON specified."}

Error log:

10:00:10 WARNING pv_opt: ------------------------------------------------------------ --   TypeError: 'NoneType' object is not subscriptable ~~~~^^^ df = pd.DataFrame(hist[0]).set_index("last_updated")["state"] File "/homeassistant/appdaemon/apps/pv_opt/pv_opt.py", line 251, in hass2df ^^^^^^^^^^^^^ df = self.hass2df( File "/homeassistant/appdaemon/apps/pv_opt/pv_opt.py", line 1892, in _get_hass_power_from_daily_kwh ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ df = self._get_hass_power_from_daily_kwh( File "/homeassistant/appdaemon/apps/pv_opt/pv_opt.py", line 1922, in load_consumption ^^^^^^^^^^^^^^^^^^^^^^ consumption = self.load_consumption( File "/homeassistant/appdaemon/apps/pv_opt/pv_opt.py", line 1316, in optimise ^^^^^^^^^^^^^^ return f(*args, **kw) File "/usr/lib/python3.11/site-packages/appdaemon/adbase.py", line 35, in f_app_lock self.optimise() File "/homeassistant/appdaemon/apps/pv_opt/pv_opt.py", line 1273, in optimise_time ^^^^^^^^^^^^^^ return f(*args, **kw) File "/usr/lib/python3.11/site-packages/appdaemon/adbase.py", line 35, in f_app_lock funcref(self.AD.sched.sanitize_timer_kwargs(app, args["kwargs"])) File "/usr/lib/python3.11/site-packages/appdaemon/threading.py", line 1022, in worker 10:00:10 WARNING pv_opt: Traceback (most recent call last): 10:00:10 WARNING pv_opt: ------------------------------------------------------------ 10:00:10 WARNING pv_opt: Worker Ags: {'id': '32579a1f996144f98f30b695d2032c1f', 'name': 'pv_opt', 'objectid': '5e948e0aa1314043a069275605dad6af', 'type': 'scheduler', 'function': >, 'pin_app': True, 'pin_thread': 0, 'kwargs': {'interval': 600, '__thread_id': 'thread-0'}} 10:00:10 WARNING pv_opt: Unexpected error in worker for App pv_opt: 10:00:10 WARNING pv_opt: ------------------------------------------------------------

APPDemon log:

/homeassistant/appdaemon/apps/pv_opt/pvpy.py:209: FutureWarning: 'T' is deprecated and will be removed in a future version. Please use 'min' instead of 'T'.
df.index[-1] + pd.Timedelta("30T"),
/homeassistant/appdaemon/apps/pv_opt/pvpy.py:210: FutureWarning: 'H' is deprecated and will be removed in a future version. Please use 'h' instead of 'H'.
df.index[-1] + pd.Timedelta("24H"),
/homeassistant/appdaemon/apps/pv_opt/pvpy.py:208: FutureWarning: 'T' is
deprecated and will be removed in a future version, please use 'min' instead.
extended_index = pd.date_range(
/homeassistant/appdaemon/apps/pv_opt/pvpy.py:209: FutureWarning: 'T' is deprecated and will be removed in a future version. Please use 'min' instead of 'T'.
df.index[-1] + pd.Timedelta("30T"),
/homeassistant/appdaemon/apps/pv_opt/pvpy.py:210: FutureWarning: 'H' is deprecated and will be removed in a future version. Please use 'h' instead of 'H'.
df.index[-1] + pd.Timedelta("24H"),
/homeassistant/appdaemon/apps/pv_opt/pvpy.py:208: FutureWarning: 'T' is deprecated and will be removed in a future version, please use 'min' instead.
extended_index = pd.date_range(
/homeassistant/appdaemon/apps/pv_opt/pvpy.py:209: FutureWarning: 'T' is deprecated and will be removed in a future version. Please use 'min' instead of 'T'.
df.index[-1] + pd.Timedelta("30T"),
/homeassistant/appdaemon/apps/pv_opt/pvpy.py:210: FutureWarning: 'H' is deprecated and will be removed in a future version. Please use 'h' instead of 'H'.
df.index[-1] + pd.Timedelta("24H"),

Charging time slots not aligned to cheapest

Version 3.4.5 B2.
This may be desired behaviour and me not understanding things or my setup isn't correct!

Parameter setup and output:
image

Todays negative Octopus Agile slots (they pay us for usage) would be the optimal charging time slots, however the Charging Plan doesn't seem to align. Similarly with SOC being 100% a lot of the time. Is the Opt Plan to run off battery and top up during cheap rates based on historic load pattern? Wondering if this usage pattern isn't correct as our usage can be sporadic although we try to shift loads to cheap night rates (just off Eco7 tariff).

Octopus Rates card highlights cheapest slots for a target charge pattern:
image

I'll keep looking at the Opt Plan outputs and tweak parameters to see how it behaves in ReadOnly mode.

Merry Christmas!

Redact Personal Data from log file by default.

Is your feature request related to a problem? Please describe.
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

Describe the solution you'd like
A clear and concise description of what you want to happen.

Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.

Additional context
Add any other context or screenshots about the feature request here.

PV_OPT starts ignoring parameters

Describe the bug

In: /homeassistant/appdaemon/apps/pv_opt/config/config.yaml

I have : consumption_history_days: 5

========================================

Plant parameters

========================================

All parameters can be a number or point to an entity UNLESS they start with 'id_'. All of

these are required but will be defaulted if not specified

battery_capacity_wh: 5000
inverter_efficiency_percent: 97 # Default: 97

charger_efficiency_percent: 91 # Default: 91

maximum_dod_percent: 15

charger_power_watts: 3600
inverter_power_watts: 3600

inverter_loss_watts: 100

switch.pvopt_read_only: off
switch.pvopt_allow_cyclic: on

However when PV_OPT starts on the dashboard I find:
switch.pvopt_read_only: on
switch.pvopt_allow_cyclic: off
charger_power_watts: 3000
battery_capacity_wh: 10000

To Reproduce
Steps to reproduce the behavior:
restart appdaemon / HA

Expected behavior
PV_OPT / AppDaemon to pick up the parameters correctly

Screenshots
If applicable, add screenshots to help explain your problem.

Desktop (please complete the following information):
App and Browser too

AppDaemon initialization error

New setup, not sure how to reproduce - on appdaemon I'm seeing with 3.5.2

2024-01-25 19:52:32.001905 WARNING pv_opt: ------------------------------------------------------------
2024-01-25 19:52:32.002290 WARNING pv_opt: Unexpected error running initialize() for pv_opt
2024-01-25 19:52:32.002418 WARNING pv_opt: ------------------------------------------------------------
2024-01-25 19:52:32.005096 WARNING pv_opt: Traceback (most recent call last):
File "/usr/lib/python3.11/site-packages/appdaemon/app_management.py", line 162, in initialize_app
await utils.run_in_executor(self, init)
File "/usr/lib/python3.11/site-packages/appdaemon/utils.py", line 304, in run_in_executor
response = future.result()
^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/concurrent/futures/thread.py", line 58, in run
result = self.fn(*self.args, **self.kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/homeassistant/appdaemon/apps/pv_opt/pv_opt.py", line 292, in initialize
self._compare_tariffs()
File "/homeassistant/appdaemon/apps/pv_opt/pv_opt.py", line 1879, in _compare_tariffs
consumption = self.load_consumption(start, end)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/homeassistant/appdaemon/apps/pv_opt/pv_opt.py", line 1866, in load_consumption
x = temp.to_numpy() + y.to_numpy()
~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~
ValueError: operands could not be broadcast together with shapes (48,) (13,)

2024-01-25 19:52:32.006090 WARNING pv_opt: ------------------------------------------------------------
2024-01-25 19:52:32.006778 INFO AppDaemon: App initialization complete

As a result pvopt sensors are missing

image

Describe the bug
A clear and concise description of what the bug is.

To Reproduce
Steps to reproduce the behavior:

  1. Go to '...'
  2. Click on '....'
  3. Scroll down to '....'
  4. See error

Expected behavior
A clear and concise description of what you expected to happen.

Screenshots
If applicable, add screenshots to help explain your problem.

Desktop (please complete the following information):

  • OS: [e.g. iOS]
  • Browser [e.g. chrome, safari]
  • Version [e.g. 22]

Smartphone (please complete the following information):

  • Device: [e.g. iPhone6]
  • OS: [e.g. iOS8.1]
  • Browser [e.g. stock browser, safari]
  • Version [e.g. 22]

Additional context
Add any other context about the problem here.

Battery Capacity Estimation Causing Problems

Describe the bug
A clear and concise description of what the bug is.

To Reproduce
Steps to reproduce the behavior:

  1. Go to '...'
  2. Click on '....'
  3. Scroll down to '....'
  4. See error

Expected behavior
A clear and concise description of what you expected to happen.

Screenshots
If applicable, add screenshots to help explain your problem.

Desktop (please complete the following information):

  • OS: [e.g. iOS]
  • Browser [e.g. chrome, safari]
  • Version [e.g. 22]

Smartphone (please complete the following information):

  • Device: [e.g. iPhone6]
  • OS: [e.g. iOS8.1]
  • Browser [e.g. stock browser, safari]
  • Version [e.g. 22]

Additional context
Add any other context about the problem here.

Missing entities

Hello, if you are able to provide some assistance, that would be very much appriciated. This isn't a bug I don't think, but the pvopt_dashboard.yaml has some errors for me, of which it reports these are not available:
sensor.solis_grid_import_power
sensor.solis_grid_export_power

How have you defined these? I don't seem to have them.

I'm connecting to my Solis inverter using a S2-WL-ST via:
alienatedsec RS485 to TCP
homeassistant-solax-modbus

Solax-Modbus is working fine and I manually update the charging times currently. I was on Octopus Eco7 but recently switched to Agile.

Thanks.

Discharge slots take estimated consumption into consideration ?

When I have a bit more time I will add some logs / screenshots but for context I am only using it in read only mode at the moment and using my own automations.
What I am seeing though for discharge slots is that it will schedule a slot during peak Import/Export costs at 4PM at full power (6000w). Because it doesn't appear to take the consumption into account this means it will drain my battery before the peak finishes at 7PM.
Is this intended ?
Thanks
Mark

AppDaemon Install Issue

In section 10. Install AppDaemon, para 1. the button to click to install doesn't seem to be visible to me. I've also tried finding AppDaemon in HACS and can't seem to find it there either.

image

The MQTT button is present though

image

Desktop (please complete the following information):

  • Windows
  • Opera One
  • Version 106.0.4998.19)

export tariff OUTGOING-LITE-FIX-12M-23-09-12 causes 400 error

Describe the bug
Im currently on the above export tariff. When I use this tariff pv_opt fails.

I suspect this is because the tariff has no end date.

To Reproduce

  1. Select the above tariff in the config
  2. Initialisation fails with 400 error

Expected behavior
Tariff to be treated as valid

Logs
The config:

  octopus_import_tariff_code: E-1R-GO-18-06-12-F                                                                                                                                                                                                  
  octopus_export_tariff_code: E-1R-OUTGOING-LITE-FIX-12M-23-09-12-F  

Detecting the config in the logs:

2024-02-04 15:31:46.858473 INFO pv_opt: Starting Opimisation with discharge enabled
2024-02-04 15:31:46.859185 INFO pv_opt: -------------------------------------------
2024-02-04 15:31:46.859864 INFO pv_opt: 
2024-02-04 15:31:46.860571 INFO pv_opt: Checking tariffs:
2024-02-04 15:31:46.861282 INFO pv_opt: -----------------
2024-02-04 15:31:46.866274 INFO pv_opt:   Import: E-1R-GO-18-06-12-F                       Start: 2023-05-31 23:30:00+0000 End: 2024-02-06 00:30:00+0000 
2024-02-04 15:31:46.867082 INFO pv_opt:   Export: E-1R-OUTGOING-LITE-FIX-12M-23-09-12-F    Start: 2023-09-11 23:00:00+0000 End: N/A 
2024-02-04 15:31:46.867813 INFO pv_opt:   - Tariffs OK
2024-02-04 15:31:46.868516 INFO pv_opt: 

The error:

2024-02-04 15:31:51.337563 WARNING HASS: Error setting Home Assistant state default.sensor.pvopt_opt_cost, {'state': 0.0, 'attributes': {'friendly_name': 'PV Opt Optimised Cost', 'device_class': 'monetary', 'state_class': 'measurement', 'unit_of_measurement': 'GBP', 'cost_today': 0.0, 'cost_tomorrow': 0.0, 'soc': [{'period_start': '2024-02-04T15:30:00+00:00', 'soc': 9.0}, {'period_start': '2024-02-04T16:00:00+00:00', 'soc': 10.0}, {'period_start': '2024-02-04T16:30:00+00:00', 'soc': 10.0}, {'period_start': '2024-02-04T17:00:00+00:00', 'soc': 10.0}, {'period_start': '2024-02-04T17:30:00+00:00', 'soc': 10.0}, {'period_start': '2024-02-04T18:00:00+00:00', 'soc': 10.0}, {'period_start': '2024-02-04T18:30:00+00:00', 'soc': 10.0}, {'period_start': '2024-02-04T19:00:00+00:00', 'soc': 10.0}, {'period_start': '2024-02-04T19:30:00+00:00', 'soc': 10.0}, {'period_start': '2024-02-04T20:00:00+00:00', 'soc': 10.0}, {'period_start': '2024-02-04T20:30:00+00:00', 'soc': 10.0}, {'period_start': '2024-02-04T21:00:00+00:00', 'soc': 10.0}, {'period_start': '2024-02-04T21:30:00+00:00', 'soc': 10.0}, {'period_start': '2024-02-04T22:00:00+00:00', 'soc': 10.0}, {'period_start': '2024-02-04T22:30:00+00:00', 'soc': 10.0}, {'period_start': '2024-02-04T23:00:00+00:00', 'soc': 10.0}, {'period_start': '2024-02-04T23:30:00+00:00', 'soc': 10.0}, {'period_start': '2024-02-05T00:00:00+00:00', 'soc': 10.0}, {'period_start': '2024-02-05T00:30:00+00:00', 'soc': 10.0}, {'period_start': '2024-02-05T01:00:00+00:00', 'soc': 21.35}, {'period_start': '2024-02-05T01:30:00+00:00', 'soc': 32.7}, {'period_start': '2024-02-05T02:00:00+00:00', 'soc': 44.04}, {'period_start': '2024-02-05T02:30:00+00:00', 'soc': 55.39}, {'period_start': '2024-02-05T03:00:00+00:00', 'soc': 66.74}, {'period_start': '2024-02-05T03:30:00+00:00', 'soc': 78.09}, {'period_start': '2024-02-05T04:00:00+00:00', 'soc': 89.44}, {'period_start': '2024-02-05T04:30:00+00:00', 'soc': 88.73}, {'period_start': '2024-02-05T05:00:00+00:00', 'soc': 86.46}, {'period_start': '2024-02-05T05:30:00+00:00', 'soc': 83.91}, {'period_start': '2024-02-05T06:00:00+00:00', 'soc': 81.78}, {'period_start': '2024-02-05T06:30:00+00:00', 'soc': 81.21}, {'period_start': '2024-02-05T07:00:00+00:00', 'soc': 80.65}, {'period_start': '2024-02-05T07:30:00+00:00', 'soc': 79.37}, {'period_start': '2024-02-05T08:00:00+00:00', 'soc': 76.96}, {'period_start': '2024-02-05T08:30:00+00:00', 'soc': 71.53}, {'period_start': '2024-02-05T09:00:00+00:00', 'soc': 67.04}, {'period_start': '2024-02-05T09:30:00+00:00', 'soc': 64.11}, {'period_start': '2024-02-05T10:00:00+00:00', 'soc': 62.37}, {'period_start': '2024-02-05T10:30:00+00:00', 'soc': 63.45}, {'period_start': '2024-02-05T11:00:00+00:00', 'soc': 64.35}, {'period_start': '2024-02-05T11:30:00+00:00', 'soc': 66.26}, {'period_start': '2024-02-05T12:00:00+00:00', 'soc': 67.61}, {'period_start': '2024-02-05T12:30:00+00:00', 'soc': 68.5}, {'period_start': '2024-02-05T13:00:00+00:00', 'soc': 68.85}, {'period_start': '2024-02-05T13:30:00+00:00', 'soc': 71.06}, {'period_start': '2024-02-05T14:00:00+00:00', 'soc': 71.34}, {'period_start': '2024-02-05T14:30:00+00:00', 'soc': 70.12}, {'period_start': '2024-02-05T15:00:00+00:00', 'soc': 70.63}, {'period_start': '2024-02-05T15:30:00+00:00', 'soc': 60.39}, {'period_start': '2024-02-05T16:00:00+00:00', 'soc': 48.5}, {'period_start': '2024-02-05T16:30:00+00:00', 'soc': 37.47}, {'period_start': '2024-02-05T17:00:00+00:00', 'soc': 34.28}, {'period_start': '2024-02-05T17:30:00+00:00', 'soc': 29.6}, {'period_start': '2024-02-05T18:00:00+00:00', 'soc': 22.23}, {'period_start': '2024-02-05T18:30:00+00:00', 'soc': 16.98}, {'period_start': '2024-02-05T19:00:00+00:00', 'soc': 10.0}, {'period_start': '2024-02-05T19:30:00+00:00', 'soc': 10.0}, {'period_start': '2024-02-05T20:00:00+00:00', 'soc': 10.0}, {'period_start': '2024-02-05T20:30:00+00:00', 'soc': 10.0}, {'period_start': '2024-02-05T21:00:00+00:00', 'soc': 10.0}, {'period_start': '2024-02-05T21:30:00+00:00', 'soc': 10.0}, {'period_start': '2024-02-05T22:00:00+00:00', 'soc': 10.0}, {'period_start': '2024-02-05T22:30:00+00:00', 'soc': 10.0}, {'period_start': '2024-02-05T23:00:00+00:00', 'soc': 10.0}, {'period_start': '2024-02-05T23:30:00+00:00', 'soc': 10.0}], 'forced': [{'period_start': '2024-02-04T15:30:00+00:00', 'forced': 0}, {'period_start': '2024-02-04T16:00:00+00:00', 'forced': 0}, {'period_start': '2024-02-04T16:30:00+00:00', 'forced': 0}, {'period_start': '2024-02-04T17:00:00+00:00', 'forced': 0}, {'period_start': '2024-02-04T17:30:00+00:00', 'forced': 0}, {'period_start': '2024-02-04T18:00:00+00:00', 'forced': 0}, {'period_start': '2024-02-04T18:30:00+00:00', 'forced': 0}, {'period_start': '2024-02-04T19:00:00+00:00', 'forced': 0}, {'period_start': '2024-02-04T19:30:00+00:00', 'forced': 0}, {'period_start': '2024-02-04T20:00:00+00:00', 'forced': 0}, {'period_start': '2024-02-04T20:30:00+00:00', 'forced': 0}, {'period_start': '2024-02-04T21:00:00+00:00', 'forced': 0}, {'period_start': '2024-02-04T21:30:00+00:00', 'forced': 0}, {'period_start': '2024-02-04T22:00:00+00:00', 'forced': 0}, {'period_start': '2024-02-04T22:30:00+00:00', 'forced': 0}, {'period_start': '2024-02-04T23:00:00+00:00', 'forced': 0}, {'period_start': '2024-02-04T23:30:00+00:00', 'forced': 0}, {'period_start': '2024-02-05T00:00:00+00:00', 'forced': 0}, {'period_start': '2024-02-05T00:30:00+00:00', 'forced': 2494}, {'period_start': '2024-02-05T01:00:00+00:00', 'forced': 2494}, {'period_start': '2024-02-05T01:30:00+00:00', 'forced': 2494}, {'period_start': '2024-02-05T02:00:00+00:00', 'forced': 2494}, {'period_start': '2024-02-05T02:30:00+00:00', 'forced': 2494}, {'period_start': '2024-02-05T03:00:00+00:00', 'forced': 2494}, {'period_start': '2024-02-05T03:30:00+00:00', 'forced': 2494}, {'period_start': '2024-02-05T04:00:00+00:00', 'forced': 0}, {'period_start': '2024-02-05T04:30:00+00:00', 'forced': 0}, {'period_start': '2024-02-05T05:00:00+00:00', 'forced': 0}, {'period_start': '2024-02-05T05:30:00+00:00', 'forced': 0}, {'period_start': '2024-02-05T06:00:00+00:00', 'forced': 0}, {'period_start': '2024-02-05T06:30:00+00:00', 'forced': 0}, {'period_start': '2024-02-05T07:00:00+00:00', 'forced': 0}, {'period_start': '2024-02-05T07:30:00+00:00', 'forced': 0}, {'period_start': '2024-02-05T08:00:00+00:00', 'forced': 0}, {'period_start': '2024-02-05T08:30:00+00:00', 'forced': 0}, {'period_start': '2024-02-05T09:00:00+00:00', 'forced': 0}, {'period_start': '2024-02-05T09:30:00+00:00', 'forced': 0}, {'period_start': '2024-02-05T10:00:00+00:00', 'forced': 0}, {'period_start': '2024-02-05T10:30:00+00:00', 'forced': 0}, {'period_start': '2024-02-05T11:00:00+00:00', 'forced': 0}, {'period_start': '2024-02-05T11:30:00+00:00', 'forced': 0}, {'period_start': '2024-02-05T12:00:00+00:00', 'forced': 0}, {'period_start': '2024-02-05T12:30:00+00:00', 'forced': 0}, {'period_start': '2024-02-05T13:00:00+00:00', 'forced': 0}, {'period_start': '2024-02-05T13:30:00+00:00', 'forced': 0}, {'period_start': '2024-02-05T14:00:00+00:00', 'forced': 0}, {'period_start': '2024-02-05T14:30:00+00:00', 'forced': 0}, {'period_start': '2024-02-05T15:00:00+00:00', 'forced': 0}, {'period_start': '2024-02-05T15:30:00+00:00', 'forced': 0}, {'period_start': '2024-02-05T16:00:00+00:00', 'forced': 0}, {'period_start': '2024-02-05T16:30:00+00:00', 'forced': 0}, {'period_start': '2024-02-05T17:00:00+00:00', 'forced': 0}, {'period_start': '2024-02-05T17:30:00+00:00', 'forced': 0}, {'period_start': '2024-02-05T18:00:00+00:00', 'forced': 0}, {'period_start': '2024-02-05T18:30:00+00:00', 'forced': 0}, {'period_start': '2024-02-05T19:00:00+00:00', 'forced': 0}, {'period_start': '2024-02-05T19:30:00+00:00', 'forced': 0}, {'period_start': '2024-02-05T20:00:00+00:00', 'forced': 0}, {'period_start': '2024-02-05T20:30:00+00:00', 'forced': 0}, {'period_start': '2024-02-05T21:00:00+00:00', 'forced': 0}, {'period_start': '2024-02-05T21:30:00+00:00', 'forced': 0}, {'period_start': '2024-02-05T22:00:00+00:00', 'forced': 0}, {'period_start': '2024-02-05T22:30:00+00:00', 'forced': 0}, {'period_start': '2024-02-05T23:00:00+00:00', 'forced': 0}, {'period_start': '2024-02-05T23:30:00+00:00', 'forced': 0}], 'import': [{'period_start': '2024-02-04T15:30:00+00:00', 'import': 12.93}, {'period_start': '2024-02-04T16:00:00+00:00', 'import': 12.93}, {'period_start': '2024-02-04T16:30:00+00:00', 'import': 12.93}, {'period_start': '2024-02-04T17:00:00+00:00', 'import': 12.93}, {'period_start': '2024-02-04T17:30:00+00:00', 'import': 12.93}, {'period_start': '2024-02-04T18:00:00+00:00', 'import': 12.93}, {'period_start': '2024-02-04T18:30:00+00:00', 'import': 12.93}, {'period_start': '2024-02-04T19:00:00+00:00', 'import': 12.93}, {'period_start': '2024-02-04T19:30:00+00:00', 'import': 12.93}, {'period_start': '2024-02-04T20:00:00+00:00', 'import': 12.93}, {'period_start': '2024-02-04T20:30:00+00:00', 'import': 12.93}, {'period_start': '2024-02-04T21:00:00+00:00', 'import': 12.93}, {'period_start': '2024-02-04T21:30:00+00:00', 'import': 12.93}, {'period_start': '2024-02-04T22:00:00+00:00', 'import': 12.93}, {'period_start': '2024-02-04T22:30:00+00:00', 'import': 12.93}, {'period_start': '2024-02-04T23:00:00+00:00', 'import': 12.93}, {'period_start': '2024-02-04T23:30:00+00:00', 'import': 12.93}, {'period_start': '2024-02-05T00:00:00+00:00', 'import': 12.93}, {'period_start': '2024-02-05T00:30:00+00:00', 'import': 5.0}, {'period_start': '2024-02-05T01:00:00+00:00', 'import': 5.0}, {'period_start': '2024-02-05T01:30:00+00:00', 'import': 5.0}, {'period_start': '2024-02-05T02:00:00+00:00', 'import': 5.0}, {'period_start': '2024-02-05T02:30:00+00:00', 'import': 5.0}, {'period_start': '2024-02-05T03:00:00+00:00', 'import': 5.0}, {'period_start': '2024-02-05T03:30:00+00:00', 'import': 5.0}, {'period_start': '2024-02-05T04:00:00+00:00', 'import': 5.0}, {'period_start': '2024-02-05T04:30:00+00:00', 'import': 12.93}, {'period_start': '2024-02-05T05:00:00+00:00', 'import': 12.93}, {'period_start': '2024-02-05T05:30:00+00:00', 'import': 12.93}, {'period_start': '2024-02-05T06:00:00+00:00', 'import': 12.93}, {'period_start': '2024-02-05T06:30:00+00:00', 'import': 12.93}, {'period_start': '2024-02-05T07:00:00+00:00', 'import': 12.93}, {'period_start': '2024-02-05T07:30:00+00:00', 'import': 12.93}, {'period_start': '2024-02-05T08:00:00+00:00', 'import': 12.93}, {'period_start': '2024-02-05T08:30:00+00:00', 'import': 12.93}, {'period_start': '2024-02-05T09:00:00+00:00', 'import': 12.93}, {'period_start': '2024-02-05T09:30:00+00:00', 'import': 12.93}, {'period_start': '2024-02-05T10:00:00+00:00', 'import': 12.93}, {'period_start': '2024-02-05T10:30:00+00:00', 'import': 12.93}, {'period_start': '2024-02-05T11:00:00+00:00', 'import': 12.93}, {'period_start': '2024-02-05T11:30:00+00:00', 'import': 12.93}, {'period_start': '2024-02-05T12:00:00+00:00', 'import': 12.93}, {'period_start': '2024-02-05T12:30:00+00:00', 'import': 12.93}, {'period_start': '2024-02-05T13:00:00+00:00', 'import': 12.93}, {'period_start': '2024-02-05T13:30:00+00:00', 'import': 12.93}, {'period_start': '2024-02-05T14:00:00+00:00', 'import': 12.93}, {'period_start': '2024-02-05T14:30:00+00:00', 'import': 12.93}, {'period_start': '2024-02-05T15:00:00+00:00', 'import': 12.93}, {'period_start': '2024-02-05T15:30:00+00:00', 'import': 12.93}, {'period_start': '2024-02-05T16:00:00+00:00', 'import': 12.93}, {'period_start': '2024-02-05T16:30:00+00:00', 'import': 12.93}, {'period_start': '2024-02-05T17:00:00+00:00', 'import': 12.93}, {'period_start': '2024-02-05T17:30:00+00:00', 'import': 12.93}, {'period_start': '2024-02-05T18:00:00+00:00', 'import': 12.93}, {'period_start': '2024-02-05T18:30:00+00:00', 'import': 12.93}, {'period_start': '2024-02-05T19:00:00+00:00', 'import': 12.93}, {'period_start': '2024-02-05T19:30:00+00:00', 'import': 12.93}, {'period_start': '2024-02-05T20:00:00+00:00', 'import': 12.93}, {'period_start': '2024-02-05T20:30:00+00:00', 'import': 12.93}, {'period_start': '2024-02-05T21:00:00+00:00', 'import': 12.93}, {'period_start': '2024-02-05T21:30:00+00:00', 'import': 12.93}, {'period_start': '2024-02-05T22:00:00+00:00', 'import': 12.93}, {'period_start': '2024-02-05T22:30:00+00:00', 'import': 12.93}, {'period_start': '2024-02-05T23:00:00+00:00', 'import': 12.93}, {'period_start': '2024-02-05T23:30:00+00:00', 'import': 12.93}], 'export': [{'period_start': '2024-02-04T15:30:00+00:00', 'export': 0.0}, {'period_start': '2024-02-04T16:00:00+00:00', 'export': 0.0}, {'period_start': '2024-02-04T16:30:00+00:00', 'export': 0.0}, {'period_start': '2024-02-04T17:00:00+00:00', 'export': 0.0}, {'period_start': '2024-02-04T17:30:00+00:00', 'export': 0.0}, {'period_start': '2024-02-04T18:00:00+00:00', 'export': 0.0}, {'period_start': '2024-02-04T18:30:00+00:00', 'export': 0.0}, {'period_start': '2024-02-04T19:00:00+00:00', 'export': 0.0}, {'period_start': '2024-02-04T19:30:00+00:00', 'export': 0.0}, {'period_start': '2024-02-04T20:00:00+00:00', 'export': 0.0}, {'period_start': '2024-02-04T20:30:00+00:00', 'export': 0.0}, {'period_start': '2024-02-04T21:00:00+00:00', 'export': 0.0}, {'period_start': '2024-02-04T21:30:00+00:00', 'export': 0.0}, {'period_start': '2024-02-04T22:00:00+00:00', 'export': 0.0}, {'period_start': '2024-02-04T22:30:00+00:00', 'export': 0.0}, {'period_start': '2024-02-04T23:00:00+00:00', 'export': 0.0}, {'period_start': '2024-02-04T23:30:00+00:00', 'export': 0.0}, {'period_start': '2024-02-05T00:00:00+00:00', 'export': 0.0}, {'period_start': '2024-02-05T00:30:00+00:00', 'export': 0.0}, {'period_start': '2024-02-05T01:00:00+00:00', 'export': 0.0}, {'period_start': '2024-02-05T01:30:00+00:00', 'export': 0.0}, {'period_start': '2024-02-05T02:00:00+00:00', 'export': 0.0}, {'period_start': '2024-02-05T02:30:00+00:00', 'export': 0.0}, {'period_start': '2024-02-05T03:00:00+00:00', 'export': 0.0}, {'period_start': '2024-02-05T03:30:00+00:00', 'export': 0.0}, {'period_start': '2024-02-05T04:00:00+00:00', 'export': 0.0}, {'period_start': '2024-02-05T04:30:00+00:00', 'export': 0.0}, {'period_start': '2024-02-05T05:00:00+00:00', 'export': 0.0}, {'period_start': '2024-02-05T05:30:00+00:00', 'export': 0.0}, {'period_start': '2024-02-05T06:00:00+00:00', 'export': 0.0}, {'period_start': '2024-02-05T06:30:00+00:00', 'export': 0.0}, {'period_start': '2024-02-05T07:00:00+00:00', 'export': 0.0}, {'period_start': '2024-02-05T07:30:00+00:00', 'export': 0.0}, {'period_start': '2024-02-05T08:00:00+00:00', 'export': 0.0}, {'period_start': '2024-02-05T08:30:00+00:00', 'export': 0.0}, {'period_start': '2024-02-05T09:00:00+00:00', 'export': 0.0}, {'period_start': '2024-02-05T09:30:00+00:00', 'export': 0.0}, {'period_start': '2024-02-05T10:00:00+00:00', 'export': 0.0}, {'period_start': '2024-02-05T10:30:00+00:00', 'export': 0.0}, {'period_start': '2024-02-05T11:00:00+00:00', 'export': 0.0}, {'period_start': '2024-02-05T11:30:00+00:00', 'export': 0.0}, {'period_start': '2024-02-05T12:00:00+00:00', 'export': 0.0}, {'period_start': '2024-02-05T12:30:00+00:00', 'export': 0.0}, {'period_start': '2024-02-05T13:00:00+00:00', 'export': 0.0}, {'period_start': '2024-02-05T13:30:00+00:00', 'export': 0.0}, {'period_start': '2024-02-05T14:00:00+00:00', 'export': 0.0}, {'period_start': '2024-02-05T14:30:00+00:00', 'export': 0.0}, {'period_start': '2024-02-05T15:00:00+00:00', 'export': 0.0}, {'period_start': '2024-02-05T15:30:00+00:00', 'export': 0.0}, {'period_start': '2024-02-05T16:00:00+00:00', 'export': 0.0}, {'period_start': '2024-02-05T16:30:00+00:00', 'export': 0.0}, {'period_start': '2024-02-05T17:00:00+00:00', 'export': 0.0}, {'period_start': '2024-02-05T17:30:00+00:00', 'export': 0.0}, {'period_start': '2024-02-05T18:00:00+00:00', 'export': 0.0}, {'period_start': '2024-02-05T18:30:00+00:00', 'export': 0.0}, {'period_start': '2024-02-05T19:00:00+00:00', 'export': 0.0}, {'period_start': '2024-02-05T19:30:00+00:00', 'export': 0.0}, {'period_start': '2024-02-05T20:00:00+00:00', 'export': 0.0}, {'period_start': '2024-02-05T20:30:00+00:00', 'export': 0.0}, {'period_start': '2024-02-05T21:00:00+00:00', 'export': 0.0}, {'period_start': '2024-02-05T21:30:00+00:00', 'export': 0.0}, {'period_start': '2024-02-05T22:00:00+00:00', 'export': 0.0}, {'period_start': '2024-02-05T22:30:00+00:00', 'export': 0.0}, {'period_start': '2024-02-05T23:00:00+00:00', 'export': 0.0}, {'period_start': '2024-02-05T23:30:00+00:00', 'export': 0.0}], 'grid': [{'period_start': '2024-02-04T15:30:00+00:00', 'grid': 140.0}, {'period_start': '2024-02-04T16:00:00+00:00', 'grid': 166.0}, {'period_start': '2024-02-04T16:30:00+00:00', 'grid': 289.0}, {'period_start': '2024-02-04T17:00:00+00:00', 'grid': 1017.0}, {'period_start': '2024-02-04T17:30:00+00:00', 'grid': 1430.0}, {'period_start': '2024-02-04T18:00:00+00:00', 'grid': 1128.0}, {'period_start': '2024-02-04T18:30:00+00:00', 'grid': 1512.0}, {'period_start': '2024-02-04T19:00:00+00:00', 'grid': 1155.0}, {'period_start': '2024-02-04T19:30:00+00:00', 'grid': 1320.0}, {'period_start': '2024-02-04T20:00:00+00:00', 'grid': 165.0}, {'period_start': '2024-02-04T20:30:00+00:00', 'grid': 192.0}, {'period_start': '2024-02-04T21:00:00+00:00', 'grid': 660.0}, {'period_start': '2024-02-04T21:30:00+00:00', 'grid': 412.0}, {'period_start': '2024-02-04T22:00:00+00:00', 'grid': 715.0}, {'period_start': '2024-02-04T22:30:00+00:00', 'grid': 385.0}, {'period_start': '2024-02-04T23:00:00+00:00', 'grid': 357.0}, {'period_start': '2024-02-04T23:30:00+00:00', 'grid': 385.0}, {'period_start': '2024-02-05T00:00:00+00:00', 'grid': 495.0}, {'period_start': '2024-02-05T00:30:00+00:00', 'grid': 3952.0}, {'period_start': '2024-02-05T01:00:00+00:00', 'grid': 5519.0}, {'period_start': '2024-02-05T01:30:00+00:00', 'grid': 6894.0}, {'period_start': '2024-02-05T02:00:00+00:00', 'grid': 6344.0}, {'period_start': '2024-02-05T02:30:00+00:00', 'grid': 3897.0}, {'period_start': '2024-02-05T03:00:00+00:00', 'grid': 3484.0}, {'period_start': '2024-02-05T03:30:00+00:00', 'grid': 2604.0}, {'period_start': '2024-02-05T04:00:00+00:00', 'grid': -0.0}, {'period_start': '2024-02-05T04:30:00+00:00', 'grid': 0.0}, {'period_start': '2024-02-05T05:00:00+00:00', 'grid': -0.0}, {'period_start': '2024-02-05T05:30:00+00:00', 'grid': 0.0}, {'period_start': '2024-02-05T06:00:00+00:00', 'grid': 0.0}, {'period_start': '2024-02-05T06:30:00+00:00', 'grid': 0.0}, {'period_start': '2024-02-05T07:00:00+00:00', 'grid': -0.0}, {'period_start': '2024-02-05T07:30:00+00:00', 'grid': -0.0}, {'period_start': '2024-02-05T08:00:00+00:00', 'grid': 0.0}, {'period_start': '2024-02-05T08:30:00+00:00', 'grid': 0.0}, {'period_start': '2024-02-05T09:00:00+00:00', 'grid': -0.0}, {'period_start': '2024-02-05T09:30:00+00:00', 'grid': -0.0}, {'period_start': '2024-02-05T10:00:00+00:00', 'grid': 0.0}, {'period_start': '2024-02-05T10:30:00+00:00', 'grid': -0.0}, {'period_start': '2024-02-05T11:00:00+00:00', 'grid': 0.0}, {'period_start': '2024-02-05T11:30:00+00:00', 'grid': 0.0}, {'period_start': '2024-02-05T12:00:00+00:00', 'grid': -0.0}, {'period_start': '2024-02-05T12:30:00+00:00', 'grid': 0.0}, {'period_start': '2024-02-05T13:00:00+00:00', 'grid': 0.0}, {'period_start': '2024-02-05T13:30:00+00:00', 'grid': 0.0}, {'period_start': '2024-02-05T14:00:00+00:00', 'grid': -0.0}, {'period_start': '2024-02-05T14:30:00+00:00', 'grid': 0.0}, {'period_start': '2024-02-05T15:00:00+00:00', 'grid': -0.0}, {'period_start': '2024-02-05T15:30:00+00:00', 'grid': -0.0}, {'period_start': '2024-02-05T16:00:00+00:00', 'grid': -0.0}, {'period_start': '2024-02-05T16:30:00+00:00', 'grid': -0.0}, {'period_start': '2024-02-05T17:00:00+00:00', 'grid': -0.0}, {'period_start': '2024-02-05T17:30:00+00:00', 'grid': 0.0}, {'period_start': '2024-02-05T18:00:00+00:00', 'grid': -0.0}, {'period_start': '2024-02-05T18:30:00+00:00', 'grid': 598.0}, {'period_start': '2024-02-05T19:00:00+00:00', 'grid': 1595.0}, {'period_start': '2024-02-05T19:30:00+00:00', 'grid': 1100.0}, {'period_start': '2024-02-05T20:00:00+00:00', 'grid': 385.0}, {'period_start': '2024-02-05T20:30:00+00:00', 'grid': 522.0}, {'period_start': '2024-02-05T21:00:00+00:00', 'grid': 770.0}, {'period_start': '2024-02-05T21:30:00+00:00', 'grid': 522.0}, {'period_start': '2024-02-05T22:00:00+00:00', 'grid': 605.0}, {'period_start': '2024-02-05T22:30:00+00:00', 'grid': 495.0}, {'period_start': '2024-02-05T23:00:00+00:00', 'grid': 357.0}, {'period_start': '2024-02-05T23:30:00+00:00', 'grid': 385.0}], 'consumption': [{'period_start': '2024-02-04T15:30:00+00:00', 'consumption': 137.5}, {'period_start': '2024-02-04T16:00:00+00:00', 'consumption': 220.0}, {'period_start': '2024-02-04T16:30:00+00:00', 'consumption': 293.33}, {'period_start': '2024-02-04T17:00:00+00:00', 'consumption': 1017.5}, {'period_start': '2024-02-04T17:30:00+00:00', 'consumption': 1430.0}, {'period_start': '2024-02-04T18:00:00+00:00', 'consumption': 1127.5}, {'period_start': '2024-02-04T18:30:00+00:00', 'consumption': 1512.5}, {'period_start': '2024-02-04T19:00:00+00:00', 'consumption': 1155.0}, {'period_start': '2024-02-04T19:30:00+00:00', 'consumption': 1320.0}, {'period_start': '2024-02-04T20:00:00+00:00', 'consumption': 165.0}, {'period_start': '2024-02-04T20:30:00+00:00', 'consumption': 192.5}, {'period_start': '2024-02-04T21:00:00+00:00', 'consumption': 660.0}, {'period_start': '2024-02-04T21:30:00+00:00', 'consumption': 412.5}, {'period_start': '2024-02-04T22:00:00+00:00', 'consumption': 715.0}, {'period_start': '2024-02-04T22:30:00+00:00', 'consumption': 385.0}, {'period_start': '2024-02-04T23:00:00+00:00', 'consumption': 357.5}, {'period_start': '2024-02-04T23:30:00+00:00', 'consumption': 385.0}, {'period_start': '2024-02-05T00:00:00+00:00', 'consumption': 495.0}, {'period_start': '2024-02-05T00:30:00+00:00', 'consumption': 1457.5}, {'period_start': '2024-02-05T01:00:00+00:00', 'consumption': 3025.0}, {'period_start': '2024-02-05T01:30:00+00:00', 'consumption': 4400.0}, {'period_start': '2024-02-05T02:00:00+00:00', 'consumption': 3850.0}, {'period_start': '2024-02-05T02:30:00+00:00', 'consumption': 1402.5}, {'period_start': '2024-02-05T03:00:00+00:00', 'consumption': 990.0}, {'period_start': '2024-02-05T03:30:00+00:00', 'consumption': 110.0}, {'period_start': '2024-02-05T04:00:00+00:00', 'consumption': 137.5}, {'period_start': '2024-02-05T04:30:00+00:00', 'consumption': 440.0}, {'period_start': '2024-02-05T05:00:00+00:00', 'consumption': 495.0}, {'period_start': '2024-02-05T05:30:00+00:00', 'consumption': 412.5}, {'period_start': '2024-02-05T06:00:00+00:00', 'consumption': 110.0}, {'period_start': '2024-02-05T06:30:00+00:00', 'consumption': 110.0}, {'period_start': '2024-02-05T07:00:00+00:00', 'consumption': 247.5}, {'period_start': '2024-02-05T07:30:00+00:00', 'consumption': 467.5}, {'period_start': '2024-02-05T08:00:00+00:00', 'consumption': 1100.0}, {'period_start': '2024-02-05T08:30:00+00:00', 'consumption': 1017.5}, {'period_start': '2024-02-05T09:00:00+00:00', 'consumption': 825.0}, {'period_start': '2024-02-05T09:30:00+00:00', 'consumption': 742.5}, {'period_start': '2024-02-05T10:00:00+00:00', 'consumption': 302.5}, {'period_start': '2024-02-05T10:30:00+00:00', 'consumption': 440.0}, {'period_start': '2024-02-05T11:00:00+00:00', 'consumption': 275.0}, {'period_start': '2024-02-05T11:30:00+00:00', 'consumption': 412.5}, {'period_start': '2024-02-05T12:00:00+00:00', 'consumption': 522.5}, {'period_start': '2024-02-05T12:30:00+00:00', 'consumption': 632.5}, {'period_start': '2024-02-05T13:00:00+00:00', 'consumption': 192.5}, {'period_start': '2024-02-05T13:30:00+00:00', 'consumption': 577.5}, {'period_start': '2024-02-05T14:00:00+00:00', 'consumption': 797.5}, {'period_start': '2024-02-05T14:30:00+00:00', 'consumption': 302.5}, {'period_start': '2024-02-05T15:00:00+00:00', 'consumption': 2227.5}, {'period_start': '2024-02-05T15:30:00+00:00', 'consumption': 2447.5}, {'period_start': '2024-02-05T16:00:00+00:00', 'consumption': 2200.0}, {'period_start': '2024-02-05T16:30:00+00:00', 'consumption': 623.33}, {'period_start': '2024-02-05T17:00:00+00:00', 'consumption': 907.5}, {'period_start': '2024-02-05T17:30:00+00:00', 'consumption': 1430.0}, {'period_start': '2024-02-05T18:00:00+00:00', 'consumption': 1017.5}, {'period_start': '2024-02-05T18:30:00+00:00', 'consumption': 1952.5}, {'period_start': '2024-02-05T19:00:00+00:00', 'consumption': 1595.0}, {'period_start': '2024-02-05T19:30:00+00:00', 'consumption': 1100.0}, {'period_start': '2024-02-05T20:00:00+00:00', 'consumption': 385.0}, {'period_start': '2024-02-05T20:30:00+00:00', 'consumption': 522.5}, {'period_start': '2024-02-05T21:00:00+00:00', 'consumption': 770.0}, {'period_start': '2024-02-05T21:30:00+00:00', 'consumption': 522.5}, {'period_start': '2024-02-05T22:00:00+00:00', 'consumption': 605.0}, {'period_start': '2024-02-05T22:30:00+00:00', 'consumption': 495.0}, {'period_start': '2024-02-05T23:00:00+00:00', 'consumption': 357.5}, {'period_start': '2024-02-05T23:30:00+00:00', 'consumption': 385.0}], 'cost': [{'period_start': '2024-02-04T00:00:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-04T00:30:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-04T01:00:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-04T01:30:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-04T02:00:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-04T02:30:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-04T03:00:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-04T03:30:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-04T04:00:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-04T04:30:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-04T05:00:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-04T05:30:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-04T06:00:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-04T06:30:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-04T07:00:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-04T07:30:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-04T08:00:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-04T08:30:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-04T09:00:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-04T09:30:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-04T10:00:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-04T10:30:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-04T11:00:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-04T11:30:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-04T12:00:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-04T12:30:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-04T13:00:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-04T13:30:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-04T14:00:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-04T14:30:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-04T15:00:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-04T15:30:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-04T16:00:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-04T16:30:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-04T17:00:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-04T17:30:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-04T18:00:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-04T18:30:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-04T19:00:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-04T19:30:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-04T20:00:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-04T20:30:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-04T21:00:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-04T21:30:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-04T22:00:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-04T22:30:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-04T23:00:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-04T23:30:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-05T00:00:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-05T00:30:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-05T01:00:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-05T01:30:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-05T02:00:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-05T02:30:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-05T03:00:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-05T03:30:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-05T04:00:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-05T04:30:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-05T05:00:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-05T05:30:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-05T06:00:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-05T06:30:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-05T07:00:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-05T07:30:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-05T08:00:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-05T08:30:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-05T09:00:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-05T09:30:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-05T10:00:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-05T10:30:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-05T11:00:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-05T11:30:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-05T12:00:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-05T12:30:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-05T13:00:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-05T13:30:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-05T14:00:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-05T14:30:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-05T15:00:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-05T15:30:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-05T16:00:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-05T16:30:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-05T17:00:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-05T17:30:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-05T18:00:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-05T18:30:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-05T19:00:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-05T19:30:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-05T20:00:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-05T20:30:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-05T21:00:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-05T21:30:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-05T22:00:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-05T22:30:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-05T23:00:00+00:00', 'cumulative_cost': nan}, {'period_start': '2024-02-05T23:30:00+00:00', 'cumulative_cost': nan}]}}
2024-02-04 15:31:51.338351 WARNING HASS: Code: 400, error: {"message":"Invalid JSON specified."}

Additional context

$ curl -u <<api>> https://api.octopus.energy/v1/products/OUTGOING-LITE-FIX-12M-23-09-12/electricity-tariffs/E-1R-OUTGOING-LITE-FIX-12M-23-09-12-F/standard-unit-rates/ | jq .
{
  "count": 1,
  "next": null,
  "previous": null,
  "results": [
    {
      "value_exc_vat": 8,
      "value_inc_vat": 8,
      "valid_from": "2023-09-11T23:00:00Z",
      "valid_to": null,
      "payment_method": null
    }
  ]
}

Flux charging periods seem to be ignored in favour of peak rate charging

Describe the bug

Octopus flux customer, cheap rates between 02:00 and 05:00 daily.

PV_OPT seems to be wanting to charge 3500W between 00:00 and 00:30 at a higher rate, rather than wait for the cheap period at 02:00

Expected behavior
I would have thought the optimum approach on Flux to be to consume from the grid when the battery is discharged until the cheaper period starts at 02:00.

Screenshots

pv_opt

Additional context
Logs:
pv_opt.log

pv_opt config:

# Internal configuration --- DO NOT EDIT ---
pvpy:
  module: pvpy
  global: true

inverters:
  module: inverters
  global: true

pv_opt:
  module: pv_opt
  class: PVOpt
  log: pv_opt_log
  prefix: pvopt
  inverter_type: "SOLIS_SOLAX_MODBUS"
  # inverter_type: "SOLIS_CORE_MODBUS"

  # User configuration ---  EDIT AWAY! ---

  # ========================================
  # Plant parameters
  # ========================================

  # All parameters can be a number or point to an entity UNLESS they start with 'id_'. All of
  # these are required but will be defaulted if not specified

  battery_capacity_Wh: 9400
  # inverter_efficiency_percent: 97 # Default: 97
  # charger_efficiency_percent: 91 # Default: 91
  maximum_dod_percent: 12
  charger_power_watts: 3500
  inverter_power_watts: 5000
  # inverter_loss_watts: 100
  battery_voltage: sensor.solis_battery_voltage
  
  # ========================================
  # Solcast configuration
  # ========================================
  #
  # id_solcast_today: sensor.solcast_pv_forecast_forecast_today
  # id_solcast_tomorrow: sensor.solcast_pv_forecast_forecast_tomorrow

  # ========================================
  # Solar and consumption forecast parameters
  # ========================================
  #
  # Valid options are:
  #
  # Solcast         - the Solcast mid-case forecast [Default]
  # Solcast_p90     - the Solcast high estimate
  # Solcast_p10     - the Solcast high estimate
  #
  # Set this using an "input_select" helper and it can be varied on the fly

  # solar_forecast:
  # - input_select.solar_forecast_source
  #   - Solcast

  # # consumption estimation
  # consumption_history_days:
  #   - 7
  # consumption_margin:
  #   - input_number.solar_opt_consumption_margin
  #  
  # ========================================
  # Octopus account parameters
  # ========================================

  octopus_auto: True # Read tariffs from the Octopus Energy integration. If successful this over-rides the following parameters

  octopus_account: !secret octopus_account
  octopus_api_key: !secret octopus_api_key

  # The following Can be omitted if either of the above options is working correctly:

  octopus_import_tariff_code: E-1R-FLUX-IMPORT-23-02-14-A
  octopus_export_tariff_code: E-1R-FLUX-EXPORT-23-02-14-A

  # octopus_import_tariff_code: E-1R-FLUX-IMPORT-23-02-14-G
  # octopus_export_tariff_code: E-1R-FLUX-EXPORT-23-02-14-G
  
  # ===============================================================================================================
  # Brand / Integration Specific Config: SOLIS_SOLAX_MODBUS: https://github.com/wills106/homeassistant-solax-modbus
  # ===============================================================================================================
  #
  # These are the default entities used with the Solis Solax Modbus integration. You can change them here and over-ride the defaults

  battery_voltage: sensor.solis_battery_voltage

  id_consumption:
    - sensor.solis_house_load
    - sensor.solis_bypass_load

  id_battery_soc: sensor.solis_battery_soc
  id_timed_charge_start_hours: number.solis_timed_charge_start_hours
  id_timed_charge_start_minutes: number.solis_timed_charge_start_minutes
  id_timed_charge_end_hours: number.solis_timed_charge_end_hours
  id_timed_charge_end_minutes: number.solis_timed_charge_end_minutes
  id_timed_charge_current: number.solis_timed_charge_current

  id_timed_discharge_start_hours: number.solis_timed_discharge_start_hours
  id_timed_discharge_start_minutes: number.solis_timed_discharge_start_minutes
  id_timed_discharge_end_hours: number.solis_timed_discharge_end_hours
  id_timed_discharge_end_minutes: number.solis_timed_discharge_end_minutes
  id_timed_discharge_current: number.solis_timed_discharge_current

  id_timed_charge_discharge_button: button.solis_update_charge_discharge_times
  id_inverter_mode: select.solis_energy_storage_control_switch

  # ==============================================================================================================
  # Brand / Integration Specific Config: SOLIS_CORE_MODBUS: https://github.com/fboundy/ha_solis_modbus
  # ==============================================================================================================
  #
  # These are the default entities used with the Solis Core Modbus integration. You can change them here and over-ride the defaults

  # modbus_hub: solis
  # modbus_slave: 1
  # battery_voltage: sensor.solis_battery_voltage
  # maximum_dod_percent: sensor.solis_overdischarge_soc
  #
  # id_consumption:
  #   - sensor.solis_house_load_power
  #   - ensor.solis_backup_load_power

  # id_grid_power: sensor.solis_grid_active_power
  # id_inverter_ac_power": sensor.solis_inverter_ac_power
  # id_battery_soc: sensor.solis_battery_soc

  # id_timed_charge_start_hours: sensor.solis_timed_charge_start_hour
  # id_timed_charge_start_minutes: sensor.solis_timed_charge_start_minute
  # id_timed_charge_end_hours: sensor.solis_timed_charge_end_hour
  # id_timed_charge_end_minutes: sensor.solis_timed_charge_end_minute
  # id_timed_charge_current: sensor.solis_timed_charge_current_limit

  # id_timed_discharge_start_hours: sensor.solis_timed_discharge_start_hour
  # id_timed_discharge_start_minutes: sensor.solis_timed_discharge_start_minute
  # id_timed_discharge_end_hours: sensor.solis_timed_discharge_end_hour
  # id_timed_discharge_end_minutes: sensor.solis_timed_discharge_end_minute
  # id_timed_discharge_current: sensor.solis_timed_discharge_current_limit

  # id_timed_charge_discharge_button: button.solis_update_charge_discharge_times
  # id_inverter_mode: select.solis_energy_storage_control_switch

Prediction / charge accuracy

Happy New Year!

  • This may not be a bug - probably my lack of understanding *

v 3.5.1
Yesterday I switched out from Read Only mode so early days with my understanding of how the logic works. Today at 16:00 when the new Agile import rates were published, the PV_Opt has chosen the following time slots to charge the battery:

image

I can't work out why it wouldn't select the cheapest time and you can see it will be charging (or holding, still charging?) until 06:00. Why not charge earlier and upto the 05:30 slot?

I don't fully understand the Threshold per pass (currently 4.0) & per slot (1.0), guess that affects things.

Also, tomorrow's solar forecast is good (11kWh - believe it when I see it!), but perhaps the charging forecast is skewed based on consumption over Christmas. Does the optimiser work out the battery change required to clear the expensive peak rates based on consumption history and expected solar forecast? This would explain charging later into the morning but not if 11kWh PV is predicted.

image

Regards

Agile Pricing Not Updating at Midnight

Describe the bug
There appears to be a (intermittent?) bug where Agile prices aren't properly updating at midnight so the previous day's rates are erroneously used by default.

If users have the Octopus Energy app they will see a discrepancy between the OE price (yellow) and the PV_OPt (white) price on this chart:
image

Restarting AppDaemin fixes the problem but obviously that's not a long terms fix. I have added some additional checks and logging in 3.5.2 to hopefully correct this but I am still diagnosing the root cause.

Support for DLS-W v2.2 Data Logger / Solarman

I have realised that the reason I cannot successfully install PV Opt is because my data logger is incompatible with Solax Modbus. I have a DSL-W v2.2 firmware MW3_15_0501_1.24 which works with the Solarman integration. Is it possible to incorporate support for my datalogger into your project?

Log formatting bug

Describe the bug
File "/homeassistant/appdaemon/apps/Github/pv_opt/apps/pv_opt/inverters.py", line 484, in _solis_write_holding_register
self.log(f"Inverter value already set to {value:d}.")
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ValueError: Unknown format code 'd' for object of type 'float'

AttributeError: 'NoneType' object has no attribute 'name'

Home Assistant Core: 2023.12.4
Home Assistant Supervisor: 2023.12.0
AppDaemon Add-on version: 0.16.0
Solax Modbus Integration

PVOpt results in an initialize_error. Here is the log. Any advice is greatly appreciated.
log:

AttributeError: 'NoneType' object has no attribute 'name'
^^^^^^
self.log(f" {imp_exp.title()}: {t.name}")
File "/homeassistant/appdaemon/apps/pv_opt/pv_opt.py", line 648, in _load_contract
self._load_contract()
File "/homeassistant/appdaemon/apps/pv_opt/pv_opt.py", line 288, in initialize
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
result = self.fn(*self.args, **self.kwargs)
File "/usr/lib/python3.11/concurrent/futures/thread.py", line 58, in run
^^^^^^^^^^^^^^^

Hold Charge

With Flux in particular it looks like PV Opt wants to hold 100% charge until forced discharge starts.

Rather than using forced charge this could be done for some inverters (such as Solis) using Backup SOC or equivalent which would reduce interaction with the inverter.

energidata service for pricing

sorry its not a essue but i iam pretty new to github and can't find eny batter way to write my questes.

hey here in Danemark we use thes addon to get energy price https://github.com/MTrab/energidataservice
i can't find a way to use it on the addon withs is a shame love to try you'r andon

its not clear what you mean with
inverter_type: "SOLIS_SOLAX_MODBUS"

inverter_type: "SOLIS_CORE_MODBUS"

inverter_type: SOLIS_SOLARMAN

device_name: solis

are that just names or are it a sensor from ha?

all the unmarked ( # ) in config needs to be removed or what to do with them?

i just tried my bedst to add what i could but its not working

========================================

Octopus account parameters

========================================

octopus_auto: False # Read tariffs from the Octopus Energy integration. If successful this over-rides the following parameters

id_import_tariff: sensor.energi_data_service
id_export_tariff: sensor.energi_data_service_salg

modbus_hub: "{device_name}"

modbus_slave: 1

battery_voltage: sensor.omcma3800g_battery_voltage

maximum_dod_percent: sensor.{device_name}_overdischarge_soc

update_cycle_seconds: 60

id_consumption:

  • sensor.omcma3800g_import_from_grid

Syntax error reported in pv_opt.py

Describe the bug
Installation issue - syntax error reported in pv_opt.py in appdaemon

To Reproduce
File "/homeassistant/appdaemon/apps/pv_opt/pv_opt.py", line 1693
"device_class": "current", elif status["hold_soc"]["active"]:
^^^^
SyntaxError: invalid syntax

Expected behavior
A clear and concise description of what you expected to happen.

Screenshots
Appdaemon log:

**s6-rc: info: service legacy-services: starting
s6-rc: info: service legacy-services successfully started
[22:29:42] INFO: Starting AppDaemon...
2023-12-20 22:29:59.139450 INFO AppDaemon: AppDaemon Version 4.4.2 starting
2023-12-20 22:29:59.151233 INFO AppDaemon: Python version is 3.11.6
2023-12-20 22:29:59.151946 INFO AppDaemon: Configuration read from: /config/appdaemon.yaml
2023-12-20 22:29:59.158058 INFO AppDaemon: Added log: AppDaemon
2023-12-20 22:29:59.162242 INFO AppDaemon: Added log: Error
2023-12-20 22:29:59.162727 INFO AppDaemon: Added log: Access
2023-12-20 22:29:59.163257 INFO AppDaemon: Added log: Diag
2023-12-20 22:30:00.175148 INFO AppDaemon: Loading Plugin HASS using class HassPlugin from module hassplugin
2023-12-20 22:30:01.943385 INFO HASS: HASS Plugin Initializing
2023-12-20 22:30:01.945784 WARNING HASS: ha_url not found in HASS configuration - module not initialized
2023-12-20 22:30:01.946719 INFO HASS: HASS Plugin initialization complete
2023-12-20 22:30:01.947356 INFO AppDaemon: Loading Plugin MQTT using class MqttPlugin from module mqttplugin
2023-12-20 22:30:02.460542 INFO MQTT: MQTT Plugin Initializing
2023-12-20 22:30:02.465088 INFO MQTT: Using 'localad/status' as Will Topic
2023-12-20 22:30:02.465522 INFO MQTT: Using 'localad/status' as Birth Topic
2023-12-20 22:30:02.466390 INFO AppDaemon: HTTP is disabled
2023-12-20 22:30:02.574689 INFO HASS: Connected to Home Assistant 2023.12.3
2023-12-20 22:30:02.815610 INFO MQTT: Connected to Broker at URL core-mosquitto:1883
2023-12-20 22:30:02.833793 INFO AppDaemon: App 'pv_opt' added
2023-12-20 22:30:02.852074 INFO AppDaemon: Found 1 active apps
2023-12-20 22:30:02.854388 INFO AppDaemon: Found 0 inactive apps
2023-12-20 22:30:02.855243 INFO AppDaemon: Found 2 global libraries
2023-12-20 22:30:02.855941 INFO AppDaemon: Starting Apps with 1 workers and 1 pins
2023-12-20 22:30:02.863263 INFO AppDaemon: Got initial state from namespace mqtt
2023-12-20 22:30:02.864894 INFO MQTT: MQTT Plugin initialization complete
2023-12-20 22:30:03.033157 INFO HASS: Evaluating startup conditions
2023-12-20 22:30:03.093516 INFO HASS: Startup condition met: hass state=RUNNING
2023-12-20 22:30:03.093816 INFO HASS: All startup conditions met
2023-12-20 22:30:08.396670 INFO AppDaemon: Got initial state from namespace default
2023-12-20 22:30:09.906098 INFO AppDaemon: Scheduler running in realtime
2023-12-20 22:30:09.915360 INFO AppDaemon: Adding /homeassistant/appdaemon/apps to module import path
2023-12-20 22:30:09.926168 INFO AppDaemon: Adding /homeassistant/appdaemon/apps/pv_opt to module import path
2023-12-20 22:30:09.934003 INFO AppDaemon: Adding /homeassistant/appdaemon/apps/pv_opt/config to module import path
2023-12-20 22:30:09.948777 WARNING AppDaemon: No app description found for: /homeassistant/appdaemon/apps/pv_opt/solis.py - ignoring
2023-12-20 22:30:09.954318 INFO AppDaemon: Loading App Module: /homeassistant/appdaemon/apps/pv_opt/pv_opt.py
2023-12-20 22:30:10.164543 WARNING Error: ------------------------------------------------------------
2023-12-20 22:30:10.175562 WARNING Error: Unexpected error loading module: /homeassistant/appdaemon/apps/pv_opt/pv_opt.py:
2023-12-20 22:30:10.178896 WARNING Error: ------------------------------------------------------------
2023-12-20 22:30:10.204897 WARNING Error: Traceback (most recent call last):
File "/usr/lib/python3.11/site-packages/appdaemon/app_management.py", line 1000, in check_app_updates
await utils.run_in_executor(self, self.read_app, mod["name"], mod["reload"])
File "/usr/lib/python3.11/site-packages/appdaemon/utils.py", line 304, in run_in_executor
response = future.result()
^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/concurrent/futures/thread.py", line 58, in run
result = self.fn(*self.args, **self.kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/site-packages/appdaemon/app_management.py", line 783, in read_app
self.modules[module_name] = importlib.import_module(module_name)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/importlib/init.py", line 126, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "", line 1204, in _gcd_import
File "", line 1176, in _find_and_load
File "", line 1147, in _find_and_load_unlocked
File "", line 690, in _load_unlocked
File "", line 936, in exec_module
File "", line 1074, in get_code
File "", line 1004, in source_to_code
File "", line 241, in _call_with_frames_removed
File "/homeassistant/appdaemon/apps/pv_opt/pv_opt.py", line 1693
"device_class": "current", elif status["hold_soc"]["active"]:
^^^^
SyntaxError: invalid syntax

2023-12-20 22:30:10.222560 WARNING Error: ------------------------------------------------------------
2023-12-20 22:30:10.227979 WARNING AppDaemon: Removing associated apps:
2023-12-20 22:30:10.231052 WARNING AppDaemon: pv_opt
2023-12-20 22:30:10.239272 INFO AppDaemon: Loading Global Module: /homeassistant/appdaemon/apps/pv_opt/pvpy.py
2023-12-20 22:30:16.837590 WARNING AppDaemon: Unable to find app solis in dependencies for pv_opt
2023-12-20 22:30:16.844103 WARNING AppDaemon: Ignoring app pv_opt
2023-12-20 22:30:16.852981 INFO AppDaemon: Loading app pv_opt using class PVOpt from module pv_opt
2023-12-20 22:30:16.982913 WARNING pv_opt: ------------------------------------------------------------
2023-12-20 22:30:16.984007 WARNING pv_opt: Unexpected error initializing app: pv_opt:
2023-12-20 22:30:16.988236 WARNING pv_opt: ------------------------------------------------------------
2023-12-20 22:30:16.991485 WARNING pv_opt: Traceback (most recent call last):
File "/usr/lib/python3.11/site-packages/appdaemon/app_management.py", line 1035, in check_app_updates
await self.init_object(app)
File "/usr/lib/python3.11/site-packages/appdaemon/app_management.py", line 323, in init_object
modname = await utils.run_in_executor(self, import, app_args["module"])
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/site-packages/appdaemon/utils.py", line 304, in run_in_executor
response = future.result()
^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/concurrent/futures/thread.py", line 58, in run
result = self.fn(*self.args, **self.kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/homeassistant/appdaemon/apps/pv_opt/pv_opt.py", line 1693
"device_class": "current", elif status["hold_soc"]["active"]:
^^^^
SyntaxError: invalid syntax

2023-12-20 22:30:16.999102 WARNING pv_opt: ------------------------------------------------------------
2023-12-20 22:30:17.003469 WARNING AppDaemon: Unable to find module pv_opt - initialize() skipped
2023-12-20 22:30:17.005212 INFO AppDaemon: App initialization complete
2023-12-20 23:20:14.836077 WARNING AppDaemon: Excessive time spent in utility loop: 2386.0ms, 5.0ms in check_app_updates(), 2381.0ms in other**

Additional context
It may be user error on my part attempting to set this up, so any help would be greatly appreciated.

Agile Price Nit Updating

Describe the bug
Agile pricing is carrying forward the previous day's pricing even though this should be updated daily at 16.00

To Reproduce
Steps to reproduce the behavior:

  1. Go to '...'
  2. Click on '....'
  3. Scroll down to '....'
  4. See error

Expected behavior
A clear and concise description of what you expected to happen.

Screenshots
If applicable, add screenshots to help explain your problem.

Desktop (please complete the following information):

  • OS: [e.g. iOS]
  • Browser [e.g. chrome, safari]
  • Version [e.g. 22]

Smartphone (please complete the following information):

  • Device: [e.g. iPhone6]
  • OS: [e.g. iOS8.1]
  • Browser [e.g. stock browser, safari]
  • Version [e.g. 22]

Additional context
Add any other context about the problem here.

Not loading custom tariff correctly

Describe the bug
Not loading custom

To Reproduce
Steps to reproduce the behavior:

  1. Go to '...'
  2. Click on '....'
  3. Scroll down to '....'
  4. See error

Expected behavior
A clear and concise description of what you expected to happen.

Screenshots
If applicable, add screenshots to help explain your problem.

Desktop (please complete the following information):

  • OS: [e.g. iOS]
  • Browser [e.g. chrome, safari]
  • Version [e.g. 22]

Smartphone (please complete the following information):

  • Device: [e.g. iPhone6]
  • OS: [e.g. iOS8.1]
  • Browser [e.g. stock browser, safari]
  • Version [e.g. 22]

Additional context
Add any other context about the problem here.

Stuck on ‘Loading Tariffs’

Describe the bug
My setup appears to always be stuck on ‘Loading Tarrifs’ , which is presumably why some sensors are also missing.

To Reproduce
Steps to reproduce the behavior:
Log File -

T23:00
29-01-2024
28-01-2024
27-01-2024
26-01-2024
25-01-2024
24-01-2024
23-01-2024
22-01-2024
29-01-2024
28-01-2024
27-01-2024
26-01-2024
25-01-2024
24-01-2024
23-01-2024
22-01-2024
2024-01-28 19:44:08.960443 INFO pv_opt:
2024-01-28 19:44:08.961274 INFO pv_opt: Retrieved day ahead forecast for period 21/01 23:00 - 29/01 22:00 for tariff E-1R-AGILE-FLEX-22-11-25-N
2024-01-28 19:44:09.002330 WARNING pv_opt: ------------------------------------------------------------
2024-01-28 19:44:09.002452 WARNING pv_opt: Unexpected error running initialize() for pv_opt
2024-01-28 19:44:09.002521 WARNING pv_opt: ------------------------------------------------------------
2024-01-28 19:44:09.003181 WARNING pv_opt: Traceback (most recent call last):
File "/usr/lib/python3.11/site-packages/appdaemon/app_management.py", line 162, in initialize_app
await utils.run_in_executor(self, init)
File "/usr/lib/python3.11/site-packages/appdaemon/utils.py", line 304, in run_in_executor
response = future.result()
^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/concurrent/futures/thread.py", line 58, in run
result = self.fn(*self.args, **self.kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/homeassistant/appdaemon/apps/pv_opt/pv_opt.py", line 292, in initialize
self._compare_tariffs()
File "/homeassistant/appdaemon/apps/pv_opt/pv_opt.py", line 1923, in _compare_tariffs
opt = self.pv_system.optimised_force(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/homeassistant/appdaemon/apps/pv_opt/pvpy.py", line 614, in optimised_force
max_slot = import_cost[import_cost == max_import_cost].index[0]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^
File "/usr/lib/python3.11/site-packages/pandas/core/indexes/base.py", line 5366, in getitem
return getitem(key)
^^^^^^^^^^^^
IndexError: index 0 is out of bounds for axis 0 with size 0

2024-01-28 19:44:09.003267 WARNING pv_opt: ------------------------------------------------------------
2024-01-28 22:34:23.446573 WARNING AppDaemon: Excessive time spent in utility loop: 2235.0ms, 1.0ms in check_app_updates(), 2234.0ms in other

Expected behavior
I had presumed that setting my local Tariff codes and my API / account details etc into AppDaemon would have this running smoothly, but it seems not. Hasn’t worked yet.

Screenshots

IMG_5748
IMG_5749

Desktop (please complete the following information):

  • OS: [e.g. iOS]
  • Browser [e.g. chrome, safari]
  • Version [e.g. 22]

Smartphone (please complete the following information):

  • Device: [e.g. iPhone6]
  • OS: [e.g. iOS8.1]
  • Browser [e.g. stock browser, safari]
  • Version [e.g. 22]

Additional context
Add any other context about the problem here.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.