PV / Solar Excess Optimizer: Auto-control appliances (wallbox, dish washer, heatpump, ...) based on excess solar power

Thanks lot for those very clear explanations, @stefan73. That’s very exciting!!

So there is a priority setting for appliances. And I guess I can change it dynamicaly:

  • usualy my electric car has more that enough power → so it set low priority
  • sometimes I plan a long trip → I just need to increase the priority

Also: if my car get a suden high priority setting, the pool heater (with its lower priority) will be interrupted and my car will start charging.
When car charging is complete, if I still produce energy, pool heater will start again.
And I guess as you support dynamic current control, you can adjust automaticaly the pool heater to use all excess power.
Some other events like washing machine start can - if necessary - interrupt the pool heater.
Am I right?

hi,
I set it up yesterday, and thanks to good weather today, i managed to make your script work.

I have one question. Is there a way to set up a device, to be able to run when there is 2000W of sunshine. But it runs at 4000W. So, you allow device A to run, even when it takes some power from the grid? I guess i can virtually change it, with adapted “input values” with the helpers. But maybe you thought of this before.

Yes, I added an automation ID, here is the yaml code:

alias: "Smart Start: Spuelmaschine Überschussplanung"
description: Setzt bei Überschuss die Spülmaschinenplanung auf aktiviert
use_blueprint:
  path: pv_excess_control.yaml
  input:
    home_battery_level: sensor.battery_state_of_capacity
    min_home_battery_level: 5
    home_battery_capacity: 5
    solar_production_forecast: sensor.solcast_pv_forecast_forecast_remaining_today
    export_power: sensor.power_production_polli
    pv_power: sensor.inverter_input_power_combined
    load_power: sensor.house_consumption_power
    actual_power: sensor.sp_kueche_spuelmaschine_1_power
    automation_id: smart_start_spuelmaschine
    appliance_on_only: false
    appliance_switch_interval: 10
    appliance_switch: input_boolean.smart_start_spuelmaschine_aktiviert

Looks good. Error gone with the ID added?

@Reunion974
To your questions: Yes, dynamic appliances work. Look up the documentation. Using that on my car.

If you change the automation settings, you need to make sure that the automation gets executed once. This feeds the parameters to the background processes. Should work then.

Thought about this. Would need some extra code. Basically we would need some logics like:
If the SoC of the battery is above e.g. 90%, you are allowed to add e.g. 2000W to the assumed PV excess power. You are allowed to do so until SoC is reaching 80%.
Is this what you are thinking of?

Well I dont have a battery as such.
I’m in the process of installing a heat pump with a 800l buffertank. I want to heat up the buffer tank during the day, to be used at night. The heat pump might be able to run on solar during the day with good weather. But when the weather is bad, I will still need to run the heat pump, but I want to do this, when I have 1000W. which is much better then do it when there is no sun. 1000W is basically what I minimally get when it’s cloudy.

It still not switching the switch.

In the logbook I can see that it is triggered every 10 seconds:

file_pv_excess_control_on_time has been triggered by time 2024-03-08T09:15:10.980728

In the systemlog I can see two errors:

This error originated from a custom integration.

Logger: custom_components.pyscript.file.pv_excess_control.on_time
Source: custom_components/pyscript/eval.py:1941
Integration: Pyscript Python scripting (documentation, issues)
First occurred: 09:15:20 (1 occurrences)
Last logged: 09:15:20

Could not get state from entity automation.smart_start_spuelmaschine: name 'automation.smart_start_spuelmaschine' is not defined
This error originated from a custom integration.

Logger: custom_components.pyscript.file.pv_excess_control.on_time
Source: custom_components/pyscript/trigger.py:1268
Integration: Pyscript Python scripting (documentation, issues)
First occurred: 09:15:30 (1 occurrences)
Last logged: 09:15:30

Exception in <file.pv_excess_control.on_time> line 246: first_item = next(iter(PvExcessControl.instances.values())) ^ RuntimeError: coroutine raised StopIteration

Are you sure the automation entity ID smart_start_spuelmaschine is correct?
Because the error seems to originate from not being able to get the state of this entity. If this name is really correct (check with the dots in upper right corner → information → gearwheel), then try to set the automation entity ID to: automation.smart_start_spuelmaschine

P.S.: If you create and delete automations, you may need to restart Home Assistant. Deleted automations can sometime have a kind of zombie life.
@InventoCasa another thing to consider in all code variante: Check the dictionary for outdated entries. Still the on_time methods do not seem to be cleaned up by HA, if a automation is deleted etc.

Having an issue that when using the combined import/export sensor, the script doesnt run.
The combined sensor is just a (import - export) sensor, as I don’t have a load sensor on all the devices, only import and export wattage.

Logs are showing:
Exception in <file.pv_excess_control.on_time> line 452: export_avg = round(sum(PvExcessControl.export_history_buffer) / len(PvExcessControl.export_history_buffer)) ^ ZeroDivisionError: division by zero

and
Could not update Export/PV history!: unsupported operand type(s) for -: ‘float’ and ‘NoneType’

It works now, my bad.

It turns the switch on as expected, but never turns off the switch although option “Only-On-Appliance” is set to false.

When I turn it off in the evening manually, it keeps off until next day when requirements are met again.

Unfortunately, I cannot see any errors in log.

@githuber110 Without debug logs we can’t help

I have set the logging level to debug. How can I export logs?

I have activated debugging in the physcript integration. That’s what I get every 10 seconds:

By the way, is there any way I can reduce the check from 10 seconds to 5 minutes which would be totally sufficient in my case? It would stop flooding the logbook.

2024-03-12 18:07:56.759 DEBUG (MainThread) [custom_components.pyscript.trigger] trigger file.pv_excess_control.on_time time_next = 2024-03-12 18:08:06.757314, now = 2024-03-12 18:07:56.759486
2024-03-12 18:07:56.759 DEBUG (MainThread) [custom_components.pyscript.trigger] trigger file.pv_excess_control.on_time waiting for 9.99783 secs
2024-03-12 18:07:56.760 DEBUG (MainThread) [custom_components.pyscript.eval] file.pv_excess_control.on_time: calling on_time(, {'trigger_type': 'time', 'trigger_time': datetime.datetime(2024, 3, 12, 18, 7, 56, 757314)})
2024-03-12 18:07:56.760 DEBUG (MainThread) [custom_components.pyscript.eval] file.pv_excess_control.on_time: calling sanity_check(, {})
2024-03-12 18:07:56.761 DEBUG (MainThread) [custom_components.pyscript.eval] file.pv_excess_control.on_time: calling values(, {})
2024-03-12 18:07:56.761 DEBUG (MainThread) [custom_components.pyscript.eval] file.pv_excess_control.on_time: calling iter(dict_values([{'instance': <custom_components.pyscript.eval.PvExcessControl object at 0x7f1e18b33ef0>, 'priority': 9}]), {})
2024-03-12 18:07:56.761 DEBUG (MainThread) [custom_components.pyscript.eval] file.pv_excess_control.on_time: calling next(<dict_valueiterator object at 0x7f1e24994360>, {})
2024-03-12 18:07:56.762 DEBUG (MainThread) [custom_components.pyscript.eval] file.pv_excess_control.on_time: calling _update_pv_history(, {})
2024-03-12 18:07:56.762 DEBUG (MainThread) [custom_components.pyscript.eval] file.pv_excess_control.on_time: calling _get_num_state("sensor.power_production_polli", {})
2024-03-12 18:07:56.762 DEBUG (MainThread) [custom_components.pyscript.eval] file.pv_excess_control.on_time: calling _get_state("sensor.power_production_polli", {})
2024-03-12 18:07:56.763 DEBUG (MainThread) [custom_components.pyscript.eval] file.pv_excess_control.on_time: calling split(".", {})
2024-03-12 18:07:56.763 DEBUG (MainThread) [custom_components.pyscript.eval] file.pv_excess_control.on_time: calling get("sensor.power_production_polli", {})
2024-03-12 18:07:56.763 DEBUG (MainThread) [custom_components.pyscript.eval] file.pv_excess_control.on_time: calling _validate_number("2", None, {})
2024-03-12 18:07:56.764 DEBUG (MainThread) [custom_components.pyscript.eval] file.pv_excess_control.on_time: calling float("2", {})
2024-03-12 18:07:56.764 DEBUG (MainThread) [custom_components.pyscript.eval] file.pv_excess_control.on_time: calling float("2", {})
2024-03-12 18:07:56.764 DEBUG (MainThread) [custom_components.pyscript.eval] file.pv_excess_control.on_time: calling float("2", {})
2024-03-12 18:07:56.766 DEBUG (MainThread) [custom_components.pyscript.eval] file.pv_excess_control.on_time: calling int(2.0, {})
2024-03-12 18:07:56.766 DEBUG (MainThread) [custom_components.pyscript.eval] file.pv_excess_control.on_time: calling _get_num_state("sensor.inverter_input_power_combined", {})
2024-03-12 18:07:56.766 DEBUG (MainThread) [custom_components.pyscript.eval] file.pv_excess_control.on_time: calling _get_state("sensor.inverter_input_power_combined", {})
2024-03-12 18:07:56.767 DEBUG (MainThread) [custom_components.pyscript.eval] file.pv_excess_control.on_time: calling split(".", {})
2024-03-12 18:07:56.767 DEBUG (MainThread) [custom_components.pyscript.eval] file.pv_excess_control.on_time: calling get("sensor.inverter_input_power_combined", {})
2024-03-12 18:07:56.767 DEBUG (MainThread) [custom_components.pyscript.eval] file.pv_excess_control.on_time: calling _validate_number("61.0", None, {})
2024-03-12 18:07:56.767 DEBUG (MainThread) [custom_components.pyscript.eval] file.pv_excess_control.on_time: calling float("61.0", {})
2024-03-12 18:07:56.768 DEBUG (MainThread) [custom_components.pyscript.eval] file.pv_excess_control.on_time: calling float("61.0", {})
2024-03-12 18:07:56.768 DEBUG (MainThread) [custom_components.pyscript.eval] file.pv_excess_control.on_time: calling float("61.0", {})
2024-03-12 18:07:56.768 DEBUG (MainThread) [custom_components.pyscript.eval] file.pv_excess_control.on_time: calling _get_num_state("sensor.house_consumption_power", {})
2024-03-12 18:07:56.768 DEBUG (MainThread) [custom_components.pyscript.eval] file.pv_excess_control.on_time: calling _get_state("sensor.house_consumption_power", {})
2024-03-12 18:07:56.769 DEBUG (MainThread) [custom_components.pyscript.eval] file.pv_excess_control.on_time: calling split(".", {})
2024-03-12 18:07:56.769 DEBUG (MainThread) [custom_components.pyscript.eval] file.pv_excess_control.on_time: calling get("sensor.house_consumption_power", {})
2024-03-12 18:07:56.769 DEBUG (MainThread) [custom_components.pyscript.eval] file.pv_excess_control.on_time: calling _validate_number("3628", None, {})
2024-03-12 18:07:56.769 DEBUG (MainThread) [custom_components.pyscript.eval] file.pv_excess_control.on_time: calling float("3628", {})
2024-03-12 18:07:56.770 DEBUG (MainThread) [custom_components.pyscript.eval] file.pv_excess_control.on_time: calling float("3628", {})
2024-03-12 18:07:56.770 DEBUG (MainThread) [custom_components.pyscript.eval] file.pv_excess_control.on_time: calling float("3628", {})
2024-03-12 18:07:56.770 DEBUG (MainThread) [custom_components.pyscript.eval] file.pv_excess_control.on_time: calling int(-3567.0, {})
2024-03-12 18:07:56.770 DEBUG (MainThread) [custom_components.pyscript.eval] file.pv_excess_control.on_time: calling append(2, {})
2024-03-12 18:07:56.770 DEBUG (MainThread) [custom_components.pyscript.eval] file.pv_excess_control.on_time: calling append(-3567, {})

Thank you very much for this awesome blueprint.

Is there any way to use dynamic EV charging with a non-smart Wallbox?

Thanks!

@githuber110 Cannot see anything wrong in the log provided in the last post. In the post before (which I responded to), I can only see that the entity provided seems to be incorrect or not connected. So I guess things are solved now.
Wrt to the logging: remove the debug logging settings. You should then see less entries in the log. The logbook topic will stay. For changing this to 5 min, there would be several changes to the code needed and you would lose the 10s sampling of the PV excess power.

@InventoCasa I tried to mis-use the current code in a lot of forms. Entering a wrong automation id leads to an error message that seems conclusive to me. We could add addtl code but I do not see how this would make the situation better. I guess the only way forward would be a way to get rid of the automation id - if possible at all.
I guess the problems of the users are due to automation id, their entity and combined import/export sensors. Fro the latter I changed the code a bit on my branch.

If the wallbox is not smart and you own a tesla, search for TeslaSolarCharger. That is another integration for HA.

Yes, the previous issue is solved, it was caused by me / a wrong automation ID. I will change the logging back to info. So far I noticed only once that the solution switched off my appliance automatically as expected, all other days I needed to switch it off at the end of the day. I will monitor the behaviour and check the logs, if I will find anything suspicious, I will let you know. Thank you for your effort!

Hi i get this message does anyone what is wrong ?

at a guess you’ve not followed the full instructions and missed part of the setup…
this part (from post one here in “Prerequisites” just before the “Installation” section) is missing

  • Pyscript must be configured to allow all imports. This can be done
    • either via UI:
      • Configuration → Integrations page → “+” → Pyscript Python scripting
      • After that, you can change the settings anytime by selecting Options under Pyscript in the Configuration page
    • or via configuration.yaml:
pyscript:
  allow_all_imports: true

and finally a reboot after all this is done.

Configuring pyscript is what allows HA see the service that your Repair is complaining about