Growatt Inverter Mode Switch

I’ve Implemented @KasperHolchKragelund 's excellent repository

in Home assistant by following the easy guide in the repo.

I’ts working well without any issues retrieving data as it should, changing basic settings as well. I have no issues with connection instability or 503 errors (i use https://openapi.growatt.com/)

I’ve extended the code locally to support load first setting - I basically want to control the setting of the parameter: loadFirstStopSocSet
Which is the Load First Soc Discharge Stop State
I want to automate this for managing when battery can be discharged in load first situations (setting this to 100 basically disallow battery use.)

In the code there is no example service for changing the loadfirst setting - so I’m wondering if there are any pointers the the api call (name and structure to provide)
I’d be happy to share my code as a fork of Kaspers repo, so others may utilize this as well.

I hope somebody here in this group have some input.

/OhmegaStar

I’ve so far been unable to find this parameter in modbus. If you find it I’d be keen to hear!

So i found the parameter and method;

    #load first save
    discharge_stopped_soc = self.get_state("input_select.adgw_load_bat_discharge_stop_soc")

    parameter_settings = []        
    # Create dictionary of settings to apply through the api call. The order of these elements is important.
    parameter_settings = [discharge_stopped_soc]        #Load First Battery Discharge Stop soc (numeric 0-100)

    # The api call - specifically for the mix inverter. Some other op will need to be applied if you dont have a mix inverter
    response = api.update_mix_inverter_setting(device_sn, 'mix_load_flast_value_multi', parameter_settings)

now I just have to wait 24 hours to implement & verify due to too many attempts / too many sessions.

Hm, that call returns 501, so I need to do more digging, it seems it may be a different api / url that has this feature.

You can use re-engineered add-on which avoids account locking and with nicer UI:

1 Like

Thanks @tkupka

I tried it but got an error in the AppDeamon log and created an issue on your github

Thx for using. Missing file is added,

Hi, I’m considering buying a Growatt NOAH 2000. It looks like this thread talks about bigger PV systems, this battery is just for small systems known as “plug-in” or “balcony” systems in Germany. I’m very interested in knowing if I will be able to control the when the battery should charge, or how much power to give to the load based on some meters I have. Does anyone know if this battery system is supported by home assistant and the solutions described here will apply to it?

Thanks!

I just installed a Noah 2000 and for this moment I can only control it though Growatt’s ShinePhone app.
Trying server.growatt.com redirects to that app.
I tried reaching/reading my ‘Plant’ via openapi.growatt.com but my ShinePhone-credentials don’t work there and trying to set up an account there with the same username returns that that username is already taken.
An account with another username is possible but using the same plantname does not reveal my Noah data and adding the Noah as Data logger was blocked.
So I can’t read anything through Growatt’s cloud, and I did not find a solution to read the Noah local.

So anyone who can, please tell us how we can read (and control) a Noah!

I cannot speak to your question about reading from your Noah.

But if any HA programmer is interested in general in implementing the Growatt write commands to set the start and stop times for the Grid-/Load-/Battery- First programs they can perhaps learn from my code for the OpenHAB integration here below. which should work at least for MIX, MAX, MIN, SPA, SPH, and TLX inverters…

Hi Kasper,
I just installed battery and a backup box into my solution and would like to control the charge-/discharge of the battery. Ity looks like to have found a solution.
Are you still using Node-RED? and would it be possible to share your setup?

Hi Peter,
I recently bought this: Growatt Data Bridge (Home Assistant) – SolarSmart – OPTIMAL SOLCELLE LØSNING to control the inverter through home assistant. That works well and does not rely on any Growatt server!

I then use Node-red for control, its not advanced in any way - timer and price-criteria for charging. I can share later when I get back to my computer.

This is the really simple timer I use currently.

[{"id":"017941e6e4acb977","type":"tab","label":"Flow 1","disabled":false,"info":"","env":[]},{"id":"6c277215883bd63e","type":"inject","z":"017941e6e4acb977","name":"Inject time","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"60","crontab":"","once":false,"onceDelay":"","topic":"CurrentTime","payload":"","payloadType":"date","x":170,"y":320,"wires":[["b8eb73b41a3845a7"]]},{"id":"03270cdc83746d27","type":"switch","z":"017941e6e4acb977","name":"","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"start_charge_time","vt":"global"},{"t":"eq","v":"stop_charge_time","vt":"global"}],"checkall":"true","repair":false,"outputs":2,"x":770,"y":320,"wires":[["56102caa80eb80ac"],["39c2536e5a6bb61e"]]},{"id":"b8eb73b41a3845a7","type":"moment","z":"017941e6e4acb977","name":"","topic":"","input":"","inputType":"msg","inTz":"Europe/Copenhagen","adjAmount":0,"adjType":"days","adjDir":"add","format":"HH","locale":"C","output":"","outputType":"msg","outTz":"Europe/Copenhagen","x":400,"y":320,"wires":[["e3599d1f69c91b7a"]]},{"id":"2865c404c00c0a93","type":"server-state-changed","z":"017941e6e4acb977","name":"læs starttid fra ui","server":"785585e5.21e4ec","version":5,"outputs":1,"exposeAsEntityConfig":"","entityId":"input_number.charge_start_time","entityIdType":"exact","outputInitially":true,"stateType":"str","ifState":"","ifStateType":"str","ifStateOperator":"is","outputOnlyOnStateChange":true,"for":0,"forType":"num","forUnits":"minutes","ignorePrevStateNull":false,"ignorePrevStateUnknown":false,"ignorePrevStateUnavailable":false,"ignoreCurrentStateUnknown":false,"ignoreCurrentStateUnavailable":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"eventData"},{"property":"topic","propertyType":"msg","value":"","valueType":"triggerId"}],"x":160,"y":140,"wires":[["3274f4c6c9fa86c5"]]},{"id":"82ef89b608191770","type":"server-state-changed","z":"017941e6e4acb977","name":"læs sluttid fra ui","server":"785585e5.21e4ec","version":5,"outputs":1,"exposeAsEntityConfig":"","entityId":"input_number.charge_end_time","entityIdType":"exact","outputInitially":true,"stateType":"str","ifState":"","ifStateType":"str","ifStateOperator":"is","outputOnlyOnStateChange":true,"for":0,"forType":"num","forUnits":"minutes","ignorePrevStateNull":false,"ignorePrevStateUnknown":false,"ignorePrevStateUnavailable":false,"ignoreCurrentStateUnknown":false,"ignoreCurrentStateUnavailable":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"eventData"},{"property":"topic","propertyType":"msg","value":"","valueType":"triggerId"}],"x":160,"y":200,"wires":[["bdaa00f627d5ddd5"]]},{"id":"0398f0184dc91b67","type":"change","z":"017941e6e4acb977","name":"","rules":[{"t":"set","p":"stop_charge_time","pt":"global","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":660,"y":200,"wires":[[]]},{"id":"2305f79616f76063","type":"change","z":"017941e6e4acb977","name":"","rules":[{"t":"set","p":"start_charge_time","pt":"global","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":660,"y":140,"wires":[[]]},{"id":"e3599d1f69c91b7a","type":"api-current-state","z":"017941e6e4acb977","name":"Is timer on?","server":"785585e5.21e4ec","version":3,"outputs":2,"halt_if":"on","halt_if_type":"str","halt_if_compare":"is","entity_id":"input_boolean.charge_timer_on_off","state_type":"str","blockInputOverrides":false,"outputProperties":[],"for":0,"forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":610,"y":320,"wires":[["03270cdc83746d27"],[]]},{"id":"3274f4c6c9fa86c5","type":"function","z":"017941e6e4acb977","name":"afrunding","func":"msg.payload = msg.payload >> 0\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":400,"y":140,"wires":[["2305f79616f76063"]]},{"id":"bdaa00f627d5ddd5","type":"function","z":"017941e6e4acb977","name":"afrunding","func":"msg.payload = msg.payload >> 0\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":400,"y":200,"wires":[["0398f0184dc91b67"]]},{"id":"3b92895d532806f6","type":"comment","z":"017941e6e4acb977","name":"Timer - read from UI using helpers","info":"","x":220,"y":100,"wires":[]},{"id":"8fb6f6d2da060b27","type":"comment","z":"017941e6e4acb977","name":"Timer automation - checks every minut","info":"","x":230,"y":280,"wires":[]},{"id":"a315f57348ad3500","type":"comment","z":"017941e6e4acb977","name":"Switch - manual and called from timer","info":"","x":230,"y":440,"wires":[]},{"id":"de9731c03cabb3ca","type":"comment","z":"017941e6e4acb977","name":"Calls Charge_Battery in next line","info":"","x":1030,"y":260,"wires":[]},{"id":"6ec0e7b31aa6590c","type":"api-call-service","z":"017941e6e4acb977","name":"Turn off charge timer after run","server":"785585e5.21e4ec","version":5,"debugenabled":false,"domain":"input_boolean","service":"turn_off","areaId":[],"deviceId":[],"entityId":["input_boolean.charge_timer_on_off"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1690,"y":360,"wires":[[]]},{"id":"3c709954a0ffe5be","type":"api-call-service","z":"017941e6e4acb977","name":"Sluk vandvarmer automation","server":"785585e5.21e4ec","version":5,"debugenabled":false,"domain":"switch","service":"turn_off","areaId":[],"deviceId":[],"entityId":["switch.vandvarmer_automation"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1680,"y":300,"wires":[[]]},{"id":"d3e749420e4723e6","type":"comment","z":"017941e6e4acb977","name":"Vandvarmer auto sluk når batteri lades fra nettet, ellers tænder den automatisk når batteriet er opladt.","info":"","x":1730,"y":240,"wires":[]},{"id":"f54956c8d4bc5e5f","type":"comment","z":"017941e6e4acb977","name":"Timer slukkes ikke mens der er høj nettarrif","info":"","x":1660,"y":400,"wires":[]},{"id":"97f8432db363b9b5","type":"api-call-service","z":"017941e6e4acb977","name":"","server":"785585e5.21e4ec","version":5,"debugenabled":false,"domain":"select","service":"select_next","areaId":[],"deviceId":[],"entityId":["select.growatt_battery_first_time_1"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1390,"y":320,"wires":[["6ec0e7b31aa6590c"]]},{"id":"56102caa80eb80ac","type":"api-current-state","z":"017941e6e4acb977","name":"","server":"785585e5.21e4ec","version":3,"outputs":2,"halt_if":"Disabled","halt_if_type":"str","halt_if_compare":"is","entity_id":"select.growatt_battery_first_time_1","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"for":"0","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":1070,"y":300,"wires":[["97f8432db363b9b5"],[]]},{"id":"39c2536e5a6bb61e","type":"api-current-state","z":"017941e6e4acb977","name":"","server":"785585e5.21e4ec","version":3,"outputs":2,"halt_if":"Enabled","halt_if_type":"str","halt_if_compare":"is","entity_id":"select.growatt_battery_first_time_1","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"for":"0","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":1070,"y":360,"wires":[["97f8432db363b9b5"],[]]},{"id":"785585e5.21e4ec","type":"server","name":"Home Assistant","version":5,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":false,"cacheJson":true,"heartbeat":false,"heartbeatInterval":30,"areaSelector":"friendlyName","deviceSelector":"friendlyName","entitySelector":"friendlyName","statusSeparator":"at: ","statusYear":"hidden","statusMonth":"short","statusDay":"numeric","statusHourCycle":"h23","statusTimeFormat":"h:m","enableGlobalContextStore":true}]

It uses two helpers to get time from the UI and start/stop charging.
In the fall I need to do something better, but price is usually lowest from 15-17 and always highest from 17-21. So if needed, I charge from 15-17.
UI looks like this:
image

Looks great,

I also got a SolarSmart, but I cant find anywhere how to switch the Battery to AC Charging.
From what I can see in your NodeRed config, you just set the battery to Battery first in a specific time. Is that correct?
I want to charge battery, when energy prices are low. Any idea how to do that?

J.

Its not quite straight forward.
I use “Battery First Time 1” as the switch. For this to work the other three settings must be set as shown in the image.
When that works you can make a timer or other logic to turn on/off grid charging.

I havent had the need to make this automatic yet - too much sun for grid charging the :sunny: . I will post when I have better solution.

2 Likes

Thanks, great Idea to use the timer switch. I’ll try to automate this with the tibber prices. When I make it work I post it as well.

So, I finished the automation.
Here is what I did to charge the battery at lowest enegry price:

First of all I am using the automation from Tibber future statistics, which I grabbed from the forum to determine the average prices for the next 36h window. It is not from me, I grabbed it here from the forum and from the tibber integration help in Home Assistant:

#tibber statistics
  - sensor:
    - name: Tibber future statistics
      state: "{{ states('sensor.tibber_prices') }}"
      attributes:
        all_prices: "{{ (state_attr('sensor.tibber_prices', 'today') + state_attr('sensor.tibber_prices', 'tomorrow')) }}"
        future_prices: "{{ state_attr('sensor.tibber_future_statistics', 'all_prices') | selectattr('startsAt', 'gt', (now() - timedelta(hours=1)) | string | replace(' ','T')) | list }}"
        current_price: "{{ state_attr('sensor.tibber_future_statistics', 'future_prices') | selectattr('startsAt', 'lt', now() | string | replace(' ','T')) | map(attribute='total') | first | float(0) }} "
        min: "{{ state_attr('sensor.tibber_future_statistics', 'future_prices') | map(attribute='total') | min | float(0) }}"
        max: "{{ state_attr('sensor.tibber_future_statistics', 'future_prices') | map(attribute='total') | max | float(0) }}"
        average:  "{{ state_attr('sensor.tibber_future_statistics', 'future_prices') | map(attribute='total') | average | float(0) }}"
        current_price_level: >-
          {% set price_cur = state_attr('sensor.tibber_future_statistics', 'current_price') | float(0) %}
          {% set price_avg = state_attr('sensor.tibber_future_statistics', 'average') | float(0) %}
          {% if price_cur == 0 or price_avg == 0 %}
            unknown
          {% else %}
            {% set price_ratio = (price_cur / price_avg) %}
            {% if price_ratio >= 1.4 %}
              VERY_EXPENSIVE
            {% elif price_ratio >= 1.15 %}
              EXPENSIVE
            {% elif price_ratio <= 0.6 %}
              VERY_CHEAP
            {% elif price_ratio <= 0.9 %}
              CHEAP
            {% else %}
              NORMAL
            {% endif %}
          {% endif %}
        min_3h: "{{ state_attr('sensor.tibber_future_statistics', 'future_prices') | selectattr('startsAt', 'lt', (now() + timedelta(hours=2)) | string | replace(' ','T')) | map(attribute='total') | min | float(0) }}"
        max_3h: "{{ state_attr('sensor.tibber_future_statistics', 'future_prices') | selectattr('startsAt', 'lt', (now() + timedelta(hours=2)) | string | replace(' ','T')) | map(attribute='total') | max | float(0) }}"
        avg_3h: "{{ state_attr('sensor.tibber_future_statistics', 'future_prices') | selectattr('startsAt', 'lt', (now() + timedelta(hours=2)) | string | replace(' ','T')) | map(attribute='total') | average | float(0) }}"
        current_price_level_3h: >-
          {% set price_cur = state_attr('sensor.tibber_future_statistics', 'current_price') | float(0) %}
          {% set price_avg = state_attr('sensor.tibber_future_statistics', 'future_prices') | selectattr('startsAt', 'lt', (now() + timedelta(hours=2)) | string | replace(' ','T')) | map(attribute='total') | average | float(0) %}
          {% if price_cur == 0 or price_avg == 0 %}
            unknown
          {% else %}
            {% set price_ratio = (price_cur / price_avg) %}
            {% if price_ratio >= 1.4 %}
              VERY_EXPENSIVE
            {% elif price_ratio >= 1.15 %}
              EXPENSIVE
            {% elif price_ratio <= 0.6 %}
              VERY_CHEAP
            {% elif price_ratio <= 0.9 %}
              CHEAP
            {% else %}
              NORMAL
            {% endif %}
          {% endif %}
        min_4h: "{{ state_attr('sensor.tibber_future_statistics', 'future_prices') | selectattr('startsAt', 'lt', (now() + timedelta(hours=3)) | string | replace(' ','T')) | map(attribute='total') | min | float(0) }}"
        max_4h: "{{ state_attr('sensor.tibber_future_statistics', 'future_prices') | selectattr('startsAt', 'lt', (now() + timedelta(hours=3)) | string | replace(' ','T')) | map(attribute='total') | max | float(0) }}"
        avg_4h: "{{ state_attr('sensor.tibber_future_statistics', 'future_prices') | selectattr('startsAt', 'lt', (now() + timedelta(hours=3)) | string | replace(' ','T')) | map(attribute='total') | average | float(0) }}"
        current_price_level_4h: >-
          {% set price_cur = state_attr('sensor.tibber_future_statistics', 'current_price') | float(0) %}
          {% set price_avg = state_attr('sensor.tibber_future_statistics', 'future_prices') | selectattr('startsAt', 'lt', (now() + timedelta(hours=3)) | string | replace(' ','T')) | map(attribute='total') | average | float(0) %}
          {% if price_cur == 0 or price_avg == 0 %}
            unknown
          {% else %}
            {% set price_ratio = (price_cur / price_avg) %}
            {% if price_ratio >= 1.4 %}
              VERY_EXPENSIVE
            {% elif price_ratio >= 1.15 %}
              EXPENSIVE
            {% elif price_ratio <= 0.6 %}
              VERY_CHEAP
            {% elif price_ratio <= 0.9 %}
              CHEAP
            {% else %}
              NORMAL
            {% endif %}
          {% endif %}
        min_6h: "{{ state_attr('sensor.tibber_future_statistics', 'future_prices') | selectattr('startsAt', 'lt', (now() + timedelta(hours=5)) | string | replace(' ','T')) | map(attribute='total') | min | float(0) }}"
        max_6h: "{{ state_attr('sensor.tibber_future_statistics', 'future_prices') | selectattr('startsAt', 'lt', (now() + timedelta(hours=5)) | string | replace(' ','T')) | map(attribute='total') | max | float(0) }}"
        avg_6h: "{{ state_attr('sensor.tibber_future_statistics', 'future_prices') | selectattr('startsAt', 'lt', (now() + timedelta(hours=5)) | string | replace(' ','T')) | map(attribute='total') | average | float(0) }}"
        current_price_level_6h: >-
          {% set price_cur = state_attr('sensor.tibber_future_statistics', 'current_price') | float(0) %}
          {% set price_avg = state_attr('sensor.tibber_future_statistics', 'future_prices') | selectattr('startsAt', 'lt', (now() + timedelta(hours=5)) | string | replace(' ','T')) | map(attribute='total') | average | float(0) %}
          {% if price_cur == 0 or price_avg == 0 %}
            unknown
          {% else %}
            {% set price_ratio = (price_cur / price_avg) %}
            {% if price_ratio >= 1.4 %}
              VERY_EXPENSIVE
            {% elif price_ratio >= 1.15 %}
              EXPENSIVE
            {% elif price_ratio <= 0.6 %}
              VERY_CHEAP
            {% elif price_ratio <= 0.9 %}
              CHEAP
            {% else %}
              NORMAL
            {% endif %}
          {% endif %}
        min_8h: "{{ state_attr('sensor.tibber_future_statistics', 'future_prices') | selectattr('startsAt', 'lt', (now() + timedelta(hours=7)) | string | replace(' ','T')) | map(attribute='total') | min | float(0) }}"
        max_8h: "{{ state_attr('sensor.tibber_future_statistics', 'future_prices') | selectattr('startsAt', 'lt', (now() + timedelta(hours=7)) | string | replace(' ','T')) | map(attribute='total') | max | float(0) }}"
        avg_8h: "{{ state_attr('sensor.tibber_future_statistics', 'future_prices') | selectattr('startsAt', 'lt', (now() + timedelta(hours=7)) | string | replace(' ','T')) | map(attribute='total') | average | float(0) }}"
        current_price_level_8h: >-
          {% set price_cur = state_attr('sensor.tibber_future_statistics', 'current_price') | float(0) %}
          {% set price_avg = state_attr('sensor.tibber_future_statistics', 'future_prices') | selectattr('startsAt', 'lt', (now() + timedelta(hours=7)) | string | replace(' ','T')) | map(attribute='total') | average | float(0) %}
          {% if price_cur == 0 or price_avg == 0 %}
            unknown
          {% else %}
            {% set price_ratio = (price_cur / price_avg) %}
            {% if price_ratio >= 1.4 %}
              VERY_EXPENSIVE
            {% elif price_ratio >= 1.15 %}
              EXPENSIVE
            {% elif price_ratio <= 0.6 %}
              VERY_CHEAP
            {% elif price_ratio <= 0.9 %}
              CHEAP
            {% else %}
              NORMAL
            {% endif %}
          {% endif %}
        min_12h: "{{ state_attr('sensor.tibber_future_statistics', 'future_prices') | selectattr('startsAt', 'lt', (now() + timedelta(hours=11)) | string | replace(' ','T')) | map(attribute='total') | min | float(0) }}"
        max_12h: "{{ state_attr('sensor.tibber_future_statistics', 'future_prices') | selectattr('startsAt', 'lt', (now() + timedelta(hours=11)) | string | replace(' ','T')) | map(attribute='total') | max | float(0) }}"
        avg_12h: "{{ state_attr('sensor.tibber_future_statistics', 'future_prices') | selectattr('startsAt', 'lt', (now() + timedelta(hours=11)) | string | replace(' ','T')) | map(attribute='total') | average | float(0) }}"
        current_price_level_12h: >-
          {% set price_cur = state_attr('sensor.tibber_future_statistics', 'current_price') | float(0) %}
          {% set price_avg = state_attr('sensor.tibber_future_statistics', 'future_prices') | selectattr('startsAt', 'lt', (now() + timedelta(hours=11)) | string | replace(' ','T')) | map(attribute='total') | average | float(0) %}
          {% if price_cur == 0 or price_avg == 0 %}
            unknown
          {% else %}
            {% set price_ratio = (price_cur / price_avg) %}
            {% if price_ratio >= 1.4 %}
              VERY_EXPENSIVE
            {% elif price_ratio >= 1.15 %}
              EXPENSIVE
            {% elif price_ratio <= 0.6 %}
              VERY_CHEAP
            {% elif price_ratio <= 0.9 %}
              CHEAP
            {% else %}
              NORMAL
            {% endif %}
          {% endif %}
        min_16h: "{{ state_attr('sensor.tibber_future_statistics', 'future_prices') | selectattr('startsAt', 'lt', (now() + timedelta(hours=15)) | string | replace(' ','T')) | map(attribute='total') | min | float(0) }}"
        max_16h: "{{ state_attr('sensor.tibber_future_statistics', 'future_prices') | selectattr('startsAt', 'lt', (now() + timedelta(hours=15)) | string | replace(' ','T')) | map(attribute='total') | max | float(0) }}"
        avg_16h: "{{ state_attr('sensor.tibber_future_statistics', 'future_prices') | selectattr('startsAt', 'lt', (now() + timedelta(hours=15)) | string | replace(' ','T')) | map(attribute='total') | average | float(0) }}"
        current_price_level_16h: >-
          {% set price_cur = state_attr('sensor.tibber_future_statistics', 'current_price') | float(0) %}
          {% set price_avg = state_attr('sensor.tibber_future_statistics', 'future_prices') | selectattr('startsAt', 'lt', (now() + timedelta(hours=15)) | string | replace(' ','T')) | map(attribute='total') | average | float(0) %}
          {% if price_cur == 0 or price_avg == 0 %}
            unknown
          {% else %}
            {% set price_ratio = (price_cur / price_avg) %}
            {% if price_ratio >= 1.4 %}
              VERY_EXPENSIVE
            {% elif price_ratio >= 1.15 %}
              EXPENSIVE
            {% elif price_ratio <= 0.6 %}
              VERY_CHEAP
            {% elif price_ratio <= 0.9 %}
              CHEAP
            {% else %}
              NORMAL
            {% endif %}
          {% endif %}
        min_24h: "{{ state_attr('sensor.tibber_future_statistics', 'future_prices') | selectattr('startsAt', 'lt', (now() + timedelta(hours=23)) | string | replace(' ','T')) | map(attribute='total') | min | float(0) }}"
        max_24h: "{{ state_attr('sensor.tibber_future_statistics', 'future_prices') | selectattr('startsAt', 'lt', (now() + timedelta(hours=23)) | string | replace(' ','T')) | map(attribute='total') | max | float(0) }}"
        avg_24h: "{{ state_attr('sensor.tibber_future_statistics', 'future_prices') | selectattr('startsAt', 'lt', (now() + timedelta(hours=23)) | string | replace(' ','T')) | map(attribute='total') | average | float(0) }}"
        current_price_level_24h: >-
          {% set price_cur = state_attr('sensor.tibber_future_statistics', 'current_price') | float(0) %}
          {% set price_avg = state_attr('sensor.tibber_future_statistics', 'future_prices') | selectattr('startsAt', 'lt', (now() + timedelta(hours=23)) | string | replace(' ','T')) | map(attribute='total') | average | float(0) %}
          {% if price_cur == 0 or price_avg == 0 %}
            unknown
          {% else %}
            {% set price_ratio = (price_cur / price_avg) %}
            {% if price_ratio >= 1.4 %}
              VERY_EXPENSIVE
            {% elif price_ratio >= 1.15 %}
              EXPENSIVE
            {% elif price_ratio <= 0.6 %}
              VERY_CHEAP
            {% elif price_ratio <= 0.9 %}
              CHEAP
            {% else %}
              NORMAL
            {% endif %}
          {% endif %}

Based on this I made 4 Sensors to determine the two cheapest price windows in the upcoming statistics:

#günstigstes Zeitfenster tibber

  - sensor:
      - name: Tibber Günstigstes Fenster 1 Start
        unique_id: tibber_gf_s1      
        state: >-
          {% set future_prices = state_attr('sensor.tibber_future_statistics', 'future_prices') %}
          {% if future_prices %}
            {% set windows = namespace(list=[]) %}
            {% for i in range(0, (future_prices | length) - 1) %}
              {% set price1 = future_prices[i] %}
              {% set price2 = future_prices[i + 1] %}
              {% set avg_price = ((price1.total | float) + (price2.total | float)) / 2 %}
              {% set window = {
                'window_start': price1.startsAt,
                'window_end': price2.startsAt,
                'average_price': avg_price
              } %}
              {% set windows.list = windows.list + [window] %}
            {% endfor %}
            {% set sorted_windows = windows.list | sort(attribute='average_price') %}
            {{ sorted_windows[0].window_start }}
          {% else %}
            unknown
          {% endif %}

      - name: Tibber Günstigstes Fenster 1 Ende
        unique_id: tibber_gf_e1    
        state: >-
          {% set future_prices = state_attr('sensor.tibber_future_statistics', 'future_prices') %}
          {% if future_prices %}
            {% set windows = namespace(list=[]) %}
            {% for i in range(0, (future_prices | length) - 1) %}
              {% set price1 = future_prices[i] %}
              {% set price2 = future_prices[i + 1] %}
              {% set avg_price = ((price1.total | float) + (price2.total | float)) / 2 %}
              {% set window = {
                'window_start': price1.startsAt,
                'window_end': price2.startsAt,
                'average_price': avg_price
              } %}
              {% set windows.list = windows.list + [window] %}
            {% endfor %}
            {% set sorted_windows = windows.list | sort(attribute='average_price') %}
            {{ sorted_windows[0].window_end }}
          {% else %}
            unknown
          {% endif %}

      - name: Tibber Günstigstes Fenster 2 Start
        unique_id: tibber_gf_s2    
        state: >-
          {% set future_prices = state_attr('sensor.tibber_future_statistics', 'future_prices') %}
          {% if future_prices %}
            {% set windows = namespace(list=[]) %}
            {% for i in range(0, (future_prices | length) - 1) %}
              {% set price1 = future_prices[i] %}
              {% set price2 = future_prices[i + 1] %}
              {% set avg_price = ((price1.total | float) + (price2.total | float)) / 2 %}
              {% set window = {
                'window_start': price1.startsAt,
                'window_end': price2.startsAt,
                'average_price': avg_price
              } %}
              {% set windows.list = windows.list + [window] %}
            {% endfor %}
            {% set sorted_windows = windows.list | sort(attribute='average_price') %}
            {{ sorted_windows[1].window_start }}
          {% else %}
            unknown
          {% endif %}

      - name: Tibber Günstigstes Fenster 2 Ende
        unique_id: tibber_gf_e2    
        state: >-
          {% set future_prices = state_attr('sensor.tibber_future_statistics', 'future_prices') %}
          {% if future_prices %}
            {% set windows = namespace(list=[]) %}
            {% for i in range(0, (future_prices | length) - 1) %}
              {% set price1 = future_prices[i] %}
              {% set price2 = future_prices[i + 1] %}
              {% set avg_price = ((price1.total | float) + (price2.total | float)) / 2 %}
              {% set window = {
                'window_start': price1.startsAt,
                'window_end': price2.startsAt,
                'average_price': avg_price
              } %}
              {% set windows.list = windows.list + [window] %}
            {% endfor %}
            {% set sorted_windows = windows.list | sort(attribute='average_price') %}
            {{ sorted_windows[1].window_end }}
          {% else %}
            unknown
          {% endif %}

This gives you the following sensors which can be used to to switch on the AC charging function as mentioned from KasperEdw.

grafik

In the automation I also have a certain threshold on my solar production forcast. Based on the forcasted solar production I switch on charging for one or two windows, when forcated kWh are under a cetrain value.

Best

Thomas

Hi, great that this work for you. I am trying to use the #tibber statistics code you mention, but if I add it to my configuration.yaml, it does not compile. Where exactly do you add that code? Thank you for a hint, JC

You need to make sure, that you use the topics, like “-sensor:” you put it under “template:”, if you put it all in configuration.yaml
like:
template:

  • sensor:xxx
  • sensor:xxx

or you make different yaml files for your sensor, which you include.