EV charging scheduling solution with solar power and a home battery

I’ve seriously invested in solar power to charge my EV (a Polestar 2). I’ve used home assistant and node red to automate and monitor my home charging solution.

My current situation:

  • 22 kWh solar panels in 3 orientations
  • 2 x 10 kWh GoodWe ET hybrid inverters
  • 2 x 22,08 kWh BYD HVM batteries
  • Polestar 2, about 30 kWh needed for daily commute
  • A Wallbox pulsar plus charger
  • 8 kWh a day household consumption
  • Dynamic electricity prices. They change hourly.

A few weeks of fiddling around I ended up with this (phone view):

The idea is the following:

  • Status of the charge switch, home battery and charger
  • automatic schedule for switching to the default weekday / weekend strategy
  • charging at night only, option next to is ignore all automation and charge when connected
  • charge from battery (= ignore electricity price) or only charge when the battery is full. At the bottom of this section you can set the % it needs to keep for home use.
  • The bottom is the charge from grid strategy. I use the nordpool_diff integration with an API connection coming from the ENTSO-e Transparency Platform Energy Prices integration. I use a 6 hour interval and a 20 hour interval entity coming from the nordpool_diff integration (see their docs on how to do it)

Here is the code for the dashboard:

title: Laden
views:
  - theme: noctis
    path: default_view
    title: Home
    badges: []
    cards:
      - type: vertical-stack
        title: Status
        cards:
          - square: true
            columns: 2
            type: grid
            cards:
              - type: entity
                entity: input_boolean.charge
                state_color: true
                name: Status laden
              - type: gauge
                entity: sensor.battery_state_of_charge
                name: Status Thuisbatterij
                severity:
                  red: 0
                  yellow: 15
                  green: 50
                needle: false
          - type: entity
            entity: sensor.wallbox_portal_status_description
            name: Status lader
      - type: vertical-stack
        title: Laadschema
        cards:
          - show_name: true
            show_icon: true
            type: button
            tap_action:
              action: toggle
            entity: input_boolean.automatic_charging_schedule
            name: Automatisch schema
            show_state: true
          - square: true
            columns: 2
            type: grid
            cards:
              - show_name: true
                show_icon: true
                type: button
                tap_action:
                  action: toggle
                entity: input_boolean.charge_at_night_schedule
                name: Enkel 's nachts laden
                show_state: true
              - show_name: true
                show_icon: true
                type: button
                tap_action:
                  action: toggle
                entity: input_boolean.charge_when_connected
                name: Direct laden, opties negeren
                show_state: true
      - type: vertical-stack
        title: Instellingen thuisbatterij
        cards:
          - square: true
            columns: 2
            type: grid
            cards:
              - show_name: true
                show_icon: true
                type: button
                tap_action:
                  action: toggle
                entity: input_boolean.only_charge_from_battery
                show_state: true
                name: Enkel van de thuisbatterij laden
              - show_name: true
                show_icon: true
                type: button
                tap_action:
                  action: toggle
                entity: input_boolean.only_charge_with_full_battery
                show_state: true
                name: Enkel laden wanneer de thuisbatterij vol is
          - type: entities
            entities:
              - entity: input_number.stop_charging_at_battery_percentage
                name: Stop met laden bij
      - square: true
        columns: 2
        type: grid
        title: Laden van grid
        cards:
          - show_name: true
            show_icon: true
            type: button
            tap_action:
              action: toggle
            entity: input_boolean.charge_short_interval
            name: Goedkoopste uren van een interval van 6 uur
            show_state: true
          - show_name: true
            show_icon: true
            type: button
            tap_action:
              action: toggle
            entity: input_boolean.charge_long_interval
            show_state: true
            name: Goedkoopste uren van een interval van 20 uur

All the entities used on the dashboard are helpers. Most are input_booleans and the slider is a number.

I use node red for the automation. There are 3 flows:

  • Charge logic
  • Wallbox charger control
  • Automatic schedule

The charge logic is a bit challenging. I don’t want the charger switching on / off all the time (it’s bad for the car battery and the home battery) so I need some kind of hysteresis to avoid that.
The Wallbox integration runs via an API which isn’t reliable. It can become unavailable several times a day. The same thing happens with the GoodWe inverters. They are right on the edge of my wifi range and I’m waiting for the parts for a physical connection. I need the automation to be reliable and be able to cope with connections dropping.

The idea behind the automation is to turn the charger off by a 5s timer. If the logic turns the charger on it sends a reset to the delay timer preventing it from turning off the charge entity. I do this to avoid the entity turning to off for a few milliseconds and then turning on again.
Once the Wallbox charger is connected I check the state of the “charge when connected” and just turn it on when this boolean is set to on. After that I just run through all the boolean inputs and check the conditions. Feel free to ask questions if some of the logic is not very clear.

The other flows turn the charger on or off based on the charge on/of boolean. I used a 30s inject because I found the state_changed node to be unreliable. For some reason it got stuck in “running” mode without triggering.

Charge logic:

[{"id":"3e69cdaf73117960","type":"tab","label":"Charge on logic","disabled":false,"info":"","env":[]},{"id":"640730e15ac8308f","type":"api-current-state","z":"3e69cdaf73117960","name":"","server":"f610b696.ce7818","version":3,"outputs":2,"halt_if":"on","halt_if_type":"str","halt_if_compare":"is","entity_id":"input_boolean.charge_at_night_schedule","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":310,"y":280,"wires":[["d5e983d821b83bad"],["37fe8108339c41c4","8adb93a631a3cbe8","8a47a19ca145f6c3","31ce5a6452e8a6a6"]]},{"id":"edac062ec8c65a2e","type":"api-current-state","z":"3e69cdaf73117960","name":"Battery >=?%","server":"f610b696.ce7818","version":3,"outputs":2,"halt_if":"$number(\t   $entities(\"input_number.stop_charging_at_battery_percentage\").state\t)\t","halt_if_type":"jsonata","halt_if_compare":"gte","entity_id":"sensor.battery_state_of_charge","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"on","valueType":"str"},{"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":620,"y":580,"wires":[["69ae13bada2c89ec"],[]]},{"id":"d5e983d821b83bad","type":"time-range-switch","z":"3e69cdaf73117960","name":"19:00-07:00","lat":"51.16054","lon":"4.6485","startTime":"19:01","endTime":"07:01","startOffset":0,"endOffset":0,"x":670,"y":280,"wires":[["37fe8108339c41c4","8adb93a631a3cbe8","8a47a19ca145f6c3","31ce5a6452e8a6a6"],["84266b72acf4a5db"]]},{"id":"8a47a19ca145f6c3","type":"api-current-state","z":"3e69cdaf73117960","name":"","server":"f610b696.ce7818","version":3,"outputs":2,"halt_if":"on","halt_if_type":"str","halt_if_compare":"is","entity_id":"input_boolean.only_charge_from_battery","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":250,"y":580,"wires":[["edac062ec8c65a2e"],[]]},{"id":"514bb4f2744ee319","type":"api-call-service","z":"3e69cdaf73117960","name":"Charge on","server":"f610b696.ce7818","version":5,"debugenabled":false,"domain":"input_boolean","service":"turn_on","areaId":[],"deviceId":[],"entityId":["input_boolean.charge"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1070,"y":620,"wires":[["c0b5d5d30dfbbd83"]]},{"id":"31ce5a6452e8a6a6","type":"api-current-state","z":"3e69cdaf73117960","name":"","server":"f610b696.ce7818","version":3,"outputs":2,"halt_if":"on","halt_if_type":"str","halt_if_compare":"is","entity_id":"input_boolean.only_charge_with_full_battery","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":320,"y":700,"wires":[["9b24b931347d780c"],[]]},{"id":"9b24b931347d780c","type":"api-current-state","z":"3e69cdaf73117960","name":"Battery >=96%","server":"f610b696.ce7818","version":3,"outputs":2,"halt_if":"96","halt_if_type":"num","halt_if_compare":"gte","entity_id":"sensor.battery_state_of_charge","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"on","valueType":"str"},{"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":680,"y":700,"wires":[["c53bebd0c6bc9449"],[]]},{"id":"2be8becef398085f","type":"api-call-service","z":"3e69cdaf73117960","name":"Charge on","server":"f610b696.ce7818","version":5,"debugenabled":false,"domain":"input_boolean","service":"turn_on","areaId":[],"deviceId":[],"entityId":["input_boolean.charge"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1190,"y":780,"wires":[["c0b5d5d30dfbbd83"]]},{"id":"615e4fcebe049208","type":"api-current-state","z":"3e69cdaf73117960","name":"","server":"f610b696.ce7818","version":3,"outputs":2,"halt_if":"on","halt_if_type":"str","halt_if_compare":"is","entity_id":"input_boolean.charge_when_connected","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":320,"y":180,"wires":[["a34d2937afd18c16"],[]]},{"id":"a34d2937afd18c16","type":"api-current-state","z":"3e69cdaf73117960","name":"Wallbox Unavailable?","server":"f610b696.ce7818","version":3,"outputs":2,"halt_if":"Unavailable","halt_if_type":"str","halt_if_compare":"is","entity_id":"switch.wallbox_portal_pause_resume","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"on","valueType":"str"},{"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":680,"y":180,"wires":[[],["40fb6d6ab472f564"]]},{"id":"40fb6d6ab472f564","type":"api-call-service","z":"3e69cdaf73117960","name":"Charge on","server":"f610b696.ce7818","version":5,"debugenabled":false,"domain":"input_boolean","service":"turn_on","areaId":[],"deviceId":[],"entityId":["input_boolean.charge"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":910,"y":180,"wires":[["c0b5d5d30dfbbd83"]]},{"id":"37fe8108339c41c4","type":"api-current-state","z":"3e69cdaf73117960","name":"","server":"f610b696.ce7818","version":3,"outputs":2,"halt_if":"on","halt_if_type":"str","halt_if_compare":"is","entity_id":"input_boolean.charge_long_interval","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":290,"y":420,"wires":[["316fff4bc9f3f0ba"],[]]},{"id":"316fff4bc9f3f0ba","type":"api-current-state","z":"3e69cdaf73117960","name":"Interval 20 > 0.5?","server":"f610b696.ce7818","version":3,"outputs":2,"halt_if":"0.5","halt_if_type":"num","halt_if_compare":"gt","entity_id":"sensor.nordpool_diff_interval_20","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"on","valueType":"str"},{"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":630,"y":420,"wires":[["00e38cfb26f19e37"],[]]},{"id":"00e38cfb26f19e37","type":"api-call-service","z":"3e69cdaf73117960","name":"Charge on","server":"f610b696.ce7818","version":5,"debugenabled":false,"domain":"input_boolean","service":"turn_on","areaId":[],"deviceId":[],"entityId":["input_boolean.charge"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":930,"y":420,"wires":[["c0b5d5d30dfbbd83"]]},{"id":"025eba556e0f115c","type":"inject","z":"3e69cdaf73117960","name":"Inject 30s","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"30","crontab":"","once":true,"onceDelay":"1","topic":"","payload":"","payloadType":"date","x":130,"y":80,"wires":[["afe72b3375754982"]]},{"id":"afe72b3375754982","type":"delay","z":"3e69cdaf73117960","name":"","pauseType":"delay","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":300,"y":80,"wires":[["b354759ee335037d"]]},{"id":"8adb93a631a3cbe8","type":"api-current-state","z":"3e69cdaf73117960","name":"","server":"f610b696.ce7818","version":3,"outputs":2,"halt_if":"on","halt_if_type":"str","halt_if_compare":"is","entity_id":"input_boolean.charge_short_interval","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":290,"y":480,"wires":[["9daaeb0eb34917f7"],[]]},{"id":"9daaeb0eb34917f7","type":"api-current-state","z":"3e69cdaf73117960","name":"Interval 6 > 0?","server":"f610b696.ce7818","version":3,"outputs":2,"halt_if":"0","halt_if_type":"num","halt_if_compare":"gt","entity_id":"sensor.nordpool_diff_interval_6","state_type":"num","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"on","valueType":"str"}],"for":"0","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":620,"y":480,"wires":[["8f0d81d271476fc4"],[]]},{"id":"8f0d81d271476fc4","type":"api-call-service","z":"3e69cdaf73117960","name":"Charge on","server":"f610b696.ce7818","version":5,"debugenabled":false,"domain":"input_boolean","service":"turn_on","areaId":[],"deviceId":[],"entityId":["input_boolean.charge"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":950,"y":480,"wires":[["c0b5d5d30dfbbd83"]]},{"id":"a65f19fbcded465a","type":"delay","z":"3e69cdaf73117960","name":"","pauseType":"delay","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":1020,"y":80,"wires":[["9bf4f1e8a804fa95"]]},{"id":"c0b5d5d30dfbbd83","type":"change","z":"3e69cdaf73117960","name":"","rules":[{"t":"set","p":"reset","pt":"msg","to":"","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":1340,"y":320,"wires":[["a65f19fbcded465a"]]},{"id":"84266b72acf4a5db","type":"api-call-service","z":"3e69cdaf73117960","name":"Charge off","server":"f610b696.ce7818","version":5,"debugenabled":false,"domain":"input_boolean","service":"turn_off","areaId":[],"deviceId":[],"entityId":["input_boolean.charge"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":870,"y":340,"wires":[[]]},{"id":"b354759ee335037d","type":"api-current-state","z":"3e69cdaf73117960","name":"Wallbox Ready? (= not connected)","server":"f610b696.ce7818","version":3,"outputs":2,"halt_if":"Ready","halt_if_type":"str","halt_if_compare":"is","entity_id":"sensor.wallbox_portal_status_description","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":540,"y":80,"wires":[["a25411b7fce0f702"],["a65f19fbcded465a","615e4fcebe049208","640730e15ac8308f"]]},{"id":"a25411b7fce0f702","type":"api-call-service","z":"3e69cdaf73117960","name":"Charge off","server":"f610b696.ce7818","version":5,"debugenabled":false,"domain":"input_boolean","service":"turn_off","areaId":[],"deviceId":[],"entityId":["input_boolean.charge"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":930,"y":20,"wires":[[]]},{"id":"69ae13bada2c89ec","type":"api-current-state","z":"3e69cdaf73117960","name":"Charge is off?","server":"f610b696.ce7818","version":3,"outputs":2,"halt_if":"off","halt_if_type":"str","halt_if_compare":"is","entity_id":"input_boolean.charge","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":740,"y":640,"wires":[["5d112a199befc150"],["514bb4f2744ee319"]]},{"id":"5d112a199befc150","type":"api-current-state","z":"3e69cdaf73117960","name":"Battery >=27%","server":"f610b696.ce7818","version":3,"outputs":2,"halt_if":"$number(\t   $entities(\t       \"input_number.stop_charging_at_battery_percentage\"\t   ).state\t)+7","halt_if_type":"jsonata","halt_if_compare":"gte","entity_id":"sensor.battery_state_of_charge","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"on","valueType":"str"},{"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":860,"y":560,"wires":[["397e79a20c316e03"],[]]},{"id":"397e79a20c316e03","type":"api-call-service","z":"3e69cdaf73117960","name":"Charge on","server":"f610b696.ce7818","version":5,"debugenabled":false,"domain":"input_boolean","service":"turn_on","areaId":[],"deviceId":[],"entityId":["input_boolean.charge"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1050,"y":560,"wires":[["c0b5d5d30dfbbd83"]]},{"id":"c53bebd0c6bc9449","type":"api-current-state","z":"3e69cdaf73117960","name":"Charge is off?","server":"f610b696.ce7818","version":3,"outputs":2,"halt_if":"off","halt_if_type":"str","halt_if_compare":"is","entity_id":"input_boolean.charge","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":760,"y":760,"wires":[["645bf126dd182a76"],["2be8becef398085f"]]},{"id":"645bf126dd182a76","type":"api-current-state","z":"3e69cdaf73117960","name":"Battery >=99%","server":"f610b696.ce7818","version":3,"outputs":2,"halt_if":"99","halt_if_type":"num","halt_if_compare":"gte","entity_id":"sensor.battery_state_of_charge","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"on","valueType":"str"},{"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":940,"y":700,"wires":[["516ae73dca691dde"],[]]},{"id":"516ae73dca691dde","type":"api-call-service","z":"3e69cdaf73117960","name":"Charge on","server":"f610b696.ce7818","version":5,"debugenabled":false,"domain":"input_boolean","service":"turn_on","areaId":[],"deviceId":[],"entityId":["input_boolean.charge"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1150,"y":700,"wires":[["c0b5d5d30dfbbd83"]]},{"id":"9bf4f1e8a804fa95","type":"api-call-service","z":"3e69cdaf73117960","name":"Charge off","server":"f610b696.ce7818","version":5,"debugenabled":false,"domain":"input_boolean","service":"turn_off","areaId":[],"deviceId":[],"entityId":["input_boolean.charge"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1230,"y":80,"wires":[[]]},{"id":"f610b696.ce7818","type":"server","name":"Home Assistant","version":5,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"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}]

Wallbox switch:

[{"id":"3de22918c63c2f60","type":"tab","label":"Wallbox on/off","disabled":false,"info":"","env":[]},{"id":"f14b893e615127cf","type":"api-current-state","z":"3de22918c63c2f60","name":"Aan het laden?","server":"f610b696.ce7818","version":3,"outputs":2,"halt_if":"on","halt_if_type":"str","halt_if_compare":"is","entity_id":"switch.wallbox_portal_pause_resume","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"for":"10","forType":"num","forUnits":"milliseconds","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":620,"y":120,"wires":[[],["cf1458bd1c027aca"]]},{"id":"cf1458bd1c027aca","type":"switch","z":"3de22918c63c2f60","name":"Unavailable? Wallbox","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"unavailable","vt":"str"},{"t":"neq","v":"unavailable","vt":"str"}],"checkall":"true","repair":false,"outputs":2,"x":840,"y":120,"wires":[[],["bbfd23f57d3439e0"]]},{"id":"1acbd3f2406d3620","type":"api-current-state","z":"3de22918c63c2f60","name":"Aan het laden?","server":"f610b696.ce7818","version":3,"outputs":2,"halt_if":"on","halt_if_type":"str","halt_if_compare":"is","entity_id":"switch.wallbox_portal_pause_resume","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"for":"10","forType":"num","forUnits":"milliseconds","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":620,"y":180,"wires":[["65825307f9fa0f3a"],[]]},{"id":"65825307f9fa0f3a","type":"api-call-service","z":"3de22918c63c2f60","name":"Wallbox off","server":"f610b696.ce7818","version":5,"debugenabled":false,"domain":"switch","service":"turn_off","areaId":[],"deviceId":[],"entityId":["switch.wallbox_portal_pause_resume"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":950,"y":200,"wires":[[]]},{"id":"bbfd23f57d3439e0","type":"api-call-service","z":"3de22918c63c2f60","name":"Wallbox on","server":"f610b696.ce7818","version":5,"debugenabled":false,"domain":"switch","service":"turn_on","areaId":[],"deviceId":[],"entityId":["switch.wallbox_portal_pause_resume"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1050,"y":120,"wires":[[]]},{"id":"a7b451b6423faa0f","type":"delay","z":"3de22918c63c2f60","name":"","pauseType":"delay","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":420,"y":120,"wires":[["f14b893e615127cf"]]},{"id":"9df9726fe6b73087","type":"delay","z":"3de22918c63c2f60","name":"","pauseType":"delay","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":440,"y":180,"wires":[["1acbd3f2406d3620"]]},{"id":"a10f4cde57790669","type":"inject","z":"3de22918c63c2f60","name":"30s","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"30","crontab":"","once":true,"onceDelay":"10","topic":"","payload":"","payloadType":"date","x":90,"y":160,"wires":[["4a1d3639ab25fd14"]]},{"id":"4a1d3639ab25fd14","type":"api-current-state","z":"3de22918c63c2f60","name":"Charge on/off?","server":"f610b696.ce7818","version":3,"outputs":2,"halt_if":"on","halt_if_type":"str","halt_if_compare":"is","entity_id":"input_boolean.charge","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":240,"y":160,"wires":[["a7b451b6423faa0f"],["9df9726fe6b73087"]]},{"id":"f610b696.ce7818","type":"server","name":"Home Assistant","version":5,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"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}]

Schedule:

[{"id":"aeeb910c928b1184","type":"tab","label":"Automatic charging schedule","disabled":false,"info":"","env":[]},{"id":"db307551baf8511c","type":"inject","z":"aeeb910c928b1184","name":"Inject weekday 1/min","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"*/1 0-23 * * 1,2,3,4,5","once":true,"onceDelay":"5","topic":"","payload":"","payloadType":"date","x":200,"y":180,"wires":[["327f7c958e782bb9"]]},{"id":"c726b31e319cbd0c","type":"inject","z":"aeeb910c928b1184","name":"Inject weekend 1/min","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"*/1 0-23 * * 6,0","once":true,"onceDelay":"1","topic":"","payload":"","payloadType":"date","x":220,"y":460,"wires":[["2fab2cf710156d8b"]]},{"id":"d8fd2f88530ff283","type":"api-current-state","z":"aeeb910c928b1184","name":"Automatic schedule on","server":"f610b696.ce7818","version":3,"outputs":2,"halt_if":"on","halt_if_type":"str","halt_if_compare":"is","entity_id":"input_boolean.automatic_charging_schedule","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"for":"10","forType":"num","forUnits":"seconds","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":200,"y":240,"wires":[["142777d01278f080","0d547b5d64084f7b"],[]]},{"id":"327f7c958e782bb9","type":"delay","z":"aeeb910c928b1184","name":"","pauseType":"delay","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":400,"y":180,"wires":[["d8fd2f88530ff283"]]},{"id":"142777d01278f080","type":"api-call-service","z":"aeeb910c928b1184","name":"Off: long interval, when connected, full battery, only from battery","server":"f610b696.ce7818","version":5,"debugenabled":false,"domain":"input_boolean","service":"turn_off","areaId":[],"deviceId":[],"entityId":["input_boolean.charge_long_interval","input_boolean.charge_when_connected","input_boolean.only_charge_from_battery","input_boolean.only_charge_with_full_battery"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":570,"y":240,"wires":[[]]},{"id":"0d547b5d64084f7b","type":"api-call-service","z":"aeeb910c928b1184","name":"On: at night, short interval","server":"f610b696.ce7818","version":5,"debugenabled":false,"domain":"input_boolean","service":"turn_on","areaId":[],"deviceId":[],"entityId":["input_boolean.charge_short_interval","input_boolean.charge_at_night_schedule"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":450,"y":300,"wires":[[]]},{"id":"2fab2cf710156d8b","type":"delay","z":"aeeb910c928b1184","name":"","pauseType":"delay","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":440,"y":460,"wires":[["047d514c94b511f8"]]},{"id":"047d514c94b511f8","type":"api-current-state","z":"aeeb910c928b1184","name":"Automatic schedule on","server":"f610b696.ce7818","version":3,"outputs":2,"halt_if":"on","halt_if_type":"str","halt_if_compare":"is","entity_id":"input_boolean.automatic_charging_schedule","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"for":"10","forType":"num","forUnits":"seconds","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":240,"y":520,"wires":[["8f5178e93efba710","dbd773f441881490"],[]]},{"id":"8f5178e93efba710","type":"api-call-service","z":"aeeb910c928b1184","name":"Off: long interval, short interval, when connected, at night, only from battery","server":"f610b696.ce7818","version":5,"debugenabled":false,"domain":"input_boolean","service":"turn_off","areaId":[],"deviceId":[],"entityId":["input_boolean.charge_long_interval","input_boolean.charge_short_interval","input_boolean.charge_when_connected","input_boolean.charge_at_night_schedule","input_boolean.only_charge_from_battery"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":650,"y":520,"wires":[[]]},{"id":"dbd773f441881490","type":"api-call-service","z":"aeeb910c928b1184","name":"On: Only charge with full battery","server":"f610b696.ce7818","version":5,"debugenabled":false,"domain":"input_boolean","service":"turn_on","areaId":[],"deviceId":[],"entityId":["input_boolean.only_charge_with_full_battery"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":510,"y":580,"wires":[[]]},{"id":"f610b696.ce7818","type":"server","name":"Home Assistant","version":5,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"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}]

Remarks, advice, questions, … feel free to reply!

Great project - question, do you factor in the cost of a cycle of your battery? Eg. - assuming your battery has an economic life of 6000 cycles do you take account of the usage cost? My battery will arrive in 2 month time; I intend to build similar logic (situation: 60kwh car battery, 15kwh commute, 15kwh life4po batteries/12kw power, 7kwp solar panels; wallbox pulsar 11kw; dynamic contract).

Based on energy prices I want to charge the battery and off load when prices exceed 15c of purchase cost (roughly the cost per kwh assuming 6000 cycles economic lifetime and 95% battery efficiency). Don’t intend to charge the car from the home battery - seems inefficient and makes to me more sense to offload to grid at the highest rate and charge the car directly

How many hours do you select in each of these intervals of 6 and 20hours? 2? 3? Can you share the code of these two booleans? Not completely sure what you are testing against >0 and >0.5 respectively for these two bools.

So far spend my time optimizing green charging where I adjust the A of the wallbox from 6-16 depending on excess solar. All in HA automations though. What’s the advantage of node red in your setup?

A lot of questions you have here:

Battery liftime: Number of cycles is a nice commercial way to show battery lifetime, but when you look at the warranty terms and conditions you’ll see most of the brands use thoughput, so I made two sensors to track it. The numbers come from the BYD warranty conditions, they warrant at least 60% capacity after 68,310MWh thoughput:

 template:
    - sensor:
       - name: "battery_lifetime"
          unit_of_measurement: "%"
          state: >
            {% set current_charge = states('sensor.total_battery_discharge') | float %}
            {{ ((1 - (current_charge / 68310)) * 100) | float(100) | round(2)}}
        - name: "battery_minimum_charge_left"
          unit_of_measurement: "kWh"
          state: >
            {% set lifetime = states('sensor.battery_lifetime') | float %}
            {% set degradation = 0.4 | float %}
            {% set maximum_charge = 22.08 | float %}
            {{ (((1 - degradation) + (degradation * (lifetime / 100))) * maximum_charge) | float | round(2)}}

Battery use: It would be easy to track the costs, but I don’t take it into account in an automation. I decided not to use peak shaving or to load/unload from/to grid using the battery. For me, the costs just don’t add up.
In winter I can charge at night when the costs are low. During the other months the solar production should be sufficient and I can charge from battery.

Hour control: I use the GitHub - jpulakka/nordpool_diff: Transforms electricity spot price into thermostat control signal. Home Assistant custom component. integration of which I use the interval sensor. For example:

sensor:
  - platform: nordpool_diff
    entsoe_entity: sensor.injectie_current_electricity_market_price
    nordpool_entity: sensor.nordpool_kwh_be_eur_5_10_0
    filter_length: 3
    filter_type: interval
    unit: ''
    normalize: 'no'

It just looks forward and compares the current price the the price for the next xx hours and sets the price difference (not the absolute value) on a scale from -1 to +1, so the >0 option in the automation means the current price is in the cheapest half for the next xx hours. It’s explained in more detail in the automations readme.
Since the prices usually rise towards the morning peak the car usually is able to charge for 3 to 5 hours each night.

Using node red appears to be more conveniet for me since it’s a visual too.

2 Likes

@Bel_RV How did you integrate the BYD batteries into HA?

I am still looking for the “best” batteries that can be integrated in HA.
AlphaESS has nice battery solutions and they presumably can be managed via modbus.

@bartland I don’t communicate with the batteries directly but through a goodwe ET inverter. The standard goodwe integration exposes 149 entities to HA, a lot of which are of the battery. There is also a custom integration with even more options.