Photovoltaics (PV) + battery + hass = maximize auto-consumption

Summary

With the construction of a new house came the idea of an intelligent energy management system. I had the chance to work with homeassistant in a “pilot” phase when I was still living in an apartment, so I was able to realize the capabilities of this platform on a house-scale environment. My research brought me to the point to not only equip the house with photovoltaic panels (PV) for energy production, but to also use an air-air heat pump together with a battery for energy storage.

My vision at that point was simple: become independent from third party energy providers and maximise the consumption of energy that I produce myself. This vision obviously is not achievable, as grey and cold winter days always require me to pull in electricity from the public grid. However, I now manage to

  • Turn on and off electric consumers, so that I can use most of the energy that I produce with my PV system

  • Minimize the amount of kWh that I inject into the public grid (as there’s no monetary gain for me to do so)

  • Minimize the amount of kWh that I retrieve from the public grid, provided that there’s enough “home solar energy” available

I needed to learn the hard way that none of my technical counterparts (electricians & installer for heating system) was able to connect the dots and to propose technical solutions. So I worked on a way to connect all of my devices and to make them “smart”, in order to achieve the above goals.

Technical Setup and Equipment

The heart of my heating setup is represented by an air-to-air heat pump (Alpha Innotec LWCV 122), combined with an electric heating coil for drinking water. The entire house is equipped with a floor heating system.

The 36 PV panels are installed both on the east (12) and west (24) side of the roof and get inverted by 2 separate Fronius inverters (Fronius Symo 4.5-3-M and Fronius Symo Hybrid 5.0-3-S) whereas the latter is the focal management device for incoming, outgoing and stored energy (BYD B-Box-HV 7.7 with 7.7kW useable storage).

From a homeassistant component perspective, I use darksky for weather forecast and nightly temperature predictions. Besides that, I use the season and sun component.

Configuration of electrical consumers

The electric consumers in my automations are mainly the heat pump, the heating coil for drinking water and a Renault ZOE electric car:

  • The heat pump injects data into homeassistant via rest API and I can artificially inject excess energy by triggering the “photovoltaic” mode in the Luxtronik control unit, which needs to be activated by a certified technician from Alpha Innotec. Once activated, it reacts upon potential free contacts of bridge NTC-24 <-> GND on the mainboard of the heat pump. This bridging is realized through a z-wave device (Fibaro Smart Implant). Once the electric circuit bridged by the Fibaro device, the heat pump sets the heating temperature to the highest value available and activates the heating process. In my case, this “burns” around 2kWh or energy and creates warm water for the floor heating system, which is then stored in a huge 800 liters boiler. Likewise I can transform electric energy into warm water and store it for later usage.

  • The heating coil is an independent device which usually serves as backup in case the heat pump cannot heat the drinking water up to the desired temperature. I turned the logic around and made it the primary heating device as long as there’s excess energy produced by my PV system. A 400 litre boiler gets heated up to 65 degrees and – once hot – provides warm water for 2-3 days. A Sonoff basic device activates the coil’s fuse located in the electric cabinet and then releases 2kW of energy to the heating coil. I was able to reduce the maximum heating capacity to 2kW rather than 4 or 6, via a manual bridging of contacts on the heating coil control unit.

  • As with the heat pump, a z-wave Fibaro Smart Implant acts in order to create a potential free contact in my KEBA P20 charging station which is connected to my electric car. Unfortunately this KEBA charging station cannot be directly connected to a network, so I had to use this workaround. The charging energy can be manually set to values beginning at 10A, so I chose to set it to a static 13 Ampere with 240V, which equals 3.1kWh of maximum energy consumption.

So how does it work?

After some months of experimenting, I decided to put in place a concept which I call “battery debt”. Why a “debt” concept? Because it takes into account the weather forecast and avoids that too much energy gets pushed (and “lost” since it’s only poorly compensated) into the public grid. I can thus optimize my auto-consumption as the battery never gets fully charged during the day but remains a critical “buffer” when switching on and off any consumers. The only component that I do not manage is the battery charge/discharge, so basically the battery sits in the middle of the producers and consumers.

Details on the "battery debt" concept

In my concept, electric consumers gets activated as soon as a specific “battery debt state of charge (SOC)” is reached. I do not wait until the battery charge has reached a SOC of 100%, no, I make a bet on the SOC while taking into account the chance to be able to fully load the battery until sunset, based on weather forecasts. This allows me to trigger consumers pretty early and sometimes at the expense of the (partially loaded) battery. As soon as the “target debt SOC” is higher than the actual SOC, the system cuts out electric consumers, until the battery recovers (loads) again and reaches the target SOC.

It’s important to note that the “target debt SOC” is dynamic and thus starts low the morning and increases towards the evening. The calculation of this value takes into account the length of the day in my timezone and the remaining hours of sunlight that can theoretically shine on my roof. An important additional variable is the precipitations and cloud factor that I retrieve from darksky (cloudy, partial cloudy, sunny, rainy, snowy, etc.). The more “sunny” the weather forecast, the more I’m willing to make a bet regarding the “debt factor”, which can vary between 70% (highest bet) and 10% (lowest bet) off a 100% SOC rate.

[Before applying this concept, I was basing the activation of consumers on “excess energy” threshold which were only attained once the battery was fully loaded. You could use this concept for your installation if you don’t have a battery attached. I can share code with you if that helps.]

Code Review

[Important note: all of the below configurations have been grouped within a package]

The general settings describe things that I need for some basic override functionalities, the definition of the battery debt status, etc.

I don’t want my heat pump (once it runs) to be exposed to arbitrary power cuts (through recurring on/off series), since I read in forums that the compressor should run for a minimum of 30 minutes. The reason is that each start of the compressor shortens the expected lifetime of the heat pump. That’s why I define a timer with the pre-set value of 45 minutes per cycle.

  ###############################
  ###   General Settings      ###
  ###############################
  
input_boolean:
 energy_flow_auto_control:
   name: automatic energy flow control
   initial: on
   
 energy_audio_notifications:
   name: Notifications for energy events
   initial: off
   icon: mdi:bell-ring

input_number:
 dynamic_battery_debt:
   name: dynamically calculated offset value for battery debt
   initial: 50
   min: 10
   max: 100
   step: 10

timer:
 heat_pump_runtime:
   duration: '00:45:00'

The first part of the automation section dynamically calculates the “energy debt” for the battery SOC. Note that this value can change any time, once darksky updates it’s forecasts. The second half manages the timer for the heat pump cycles.

   ##########################
   ###    -+SECTION+-     ###
   ###     Automation     ###
   ##########################

automation:


# calculation of "energy debt" value, based on weather forecast

 - alias: '(energy control) dynamic change of energy debt ratio'
   initial_state: true
   trigger:
     - platform: state
       entity_id: weather.dark_sky
     - platform: state
       entity_id: sensor.dark_sky_icon_0
   action:
     - service: input_number.set_value
       data_template: 
         value: >
           {% if states.weather.dark_sky.state == 'cloudy' or states.weather.dark_sky.state == 'rainy' or states.weather.dark_sky.state == 'snowy' %}
             10
           {% elif states.weather.dark_sky.state == 'partlycloudy' %}
             50
           {% elif states.weather.dark_sky.state == 'sunny' %}
             70
           {% else %}
             20
           {% endif %}
         entity_id: input_number.dynamic_battery_debt


# timer for heat pump authorization

 - alias: '(energy control) activate heat pump runtime' 
   initial_state: true
   trigger:
     - platform: state
       entity_id: switch.heat_generation_authorization
       to: 'on'
   action: 
     - service: timer.start
       entity_id: timer.heat_pump_runtime

 - alias: '(energy control) deactivate heat pump runtime' 
   initial_state: true
   trigger:
     - platform: event
       event_type: timer.finished
       event_data:
         entity_id: timer.heat_pump_runtime
   action:
     - service: switch.turn_off
       entity_id: switch.heat_generation_authorization



The start section merely turns on consumers at the right point of time, whereas the stop section stops these. Note that I have hard coded the priorities of my favorite 3 consumers (heat pump, hot water boiler, and ev charge) into the code.

         ##################################
         ###        +Category+          ###
         ###       START & CHANGE       ###
         ##################################

         ##########################################
         ### hot water + ev charge + heat pump  ###
         ##########################################
         # authorize and activate other consumers as soon as the battery reaches higher levels

  - alias: '(energy control) authorize energy consumers'
    initial_state: true
    trigger:
      - platform: state
        entity_id: switch.heat_generation_authorization
        to: 'off'
      - platform: numeric_state
        entity_id: sensor.battery_soc
        above: 91
      - platform: numeric_state
        entity_id: sensor.battery_soc
        above: 94
      - platform: numeric_state
        entity_id: sensor.battery_soc
        above: 96
      - platform: numeric_state
        entity_id: sensor.battery_soc
        above: 98
      - platform: template
        value_template: "{{ states.sensor.public_grid_consumption.state | int < -2000 }}"  # use case: incoming PV energy is too high for SYMO Hybrid to charge the battery further and energy gets lost to the public grid
      - platform: template
        value_template: "{{states.sensor.target_debt_soc.state | int < states.sensor.battery_soc.state | int and states.sensor.battery_discharge.state | int < -2500 }}" # will authorize consumers (such as heat pump at first), as soon as target soc level has been reached + PV produces enough power
    condition:
      - condition: state
        entity_id: binary_sensor.target_soc_failure
        state: 'off'
      - condition: state
        entity_id: input_boolean.energy_flow_auto_control
        state: 'on'
    action:
      - service: switch.turn_on
        data_template:
          entity_id: >
            {% if is_state('binary_sensor.heat_gen_auth_requirements_fulfilled', 'on') and states.switch.heat_generation_authorization.state == 'off' %}
              switch.heat_generation_authorization
            {% elif (states.binary_sensor.heat_gen_auth_requirements_fulfilled.state == 'off' or states.switch.heat_generation_authorization.state == 'on') and states.sensor.energy_flow_prio.state == 'hot_water' and not states.binary_sensor.hot_water_boiler_2kw.state == 'on' and not states.binary_sensor.water_hot.state == 'on' %} 
              switch.hot_water_template_switch
            {% else %}
              switch.ev_charging_authorization
            {% endif %}


         ###############################
         ###         STOP            ###
         ###############################

  - alias: '(energy control) turn off consumers if missing power' 
    initial_state: true
    trigger:
      - platform: state
        entity_id: binary_sensor.target_soc_failure
        to: 'on'
      - platform: state
        entity_id: binary_sensor.target_soc_failure
        to: 'on'
        for:
          minutes: 1
      - platform: state
        entity_id: binary_sensor.target_soc_failure
        to: 'on'
        for:
          minutes: 2
      - platform: numeric_state
        entity_id: sensor.public_grid_consumption
        above: 500
        for:
          minutes: 3
    condition:
      - condition: state
        entity_id: input_boolean.energy_flow_auto_control
        state: "on"
    action:
      - service: switch.turn_off
        data_template:
          entity_id: >
            {% if states.sensor.energy_flow_prio.state == 'ev_charge' and states.binary_sensor.hot_water_boiler_2kw.state == 'off' %}
              switch.ev_charging_authorization
            {% elif states.sensor.energy_flow_prio.state == 'ev_charge' and states.binary_sensor.water_hot.state == 'on' %}
              switch.hot_water_template_switch
            {% elif is_state('binary_sensor.ev_charging_authorized', 'on') %}
              switch.ev_charging_authorization
            {% elif states.switch.hot_water_template_switch.state == 'on' %}
              switch.hot_water_template_switch
            {% endif %}

  - alias: '(energy control) stop heat pump authorization'
    initial_state: false
    trigger:
      - platform: template
        value_template: "{{ states.sensor.remaining_pv_light.state | float < 0.5 }}"  # stop the heatpump latest when the sun will go down VERY soon
      - platform: numeric_state
        entity_id: sensor.luxtronik_id_web_temperatur_trl
        above: 40
    action: 
      - service: switch.turn_off
        entity_id: switch.heat_generation_authorization 


  - alias: '(energy control) turn off boiler when water is hot' 
    initial_state: true
    trigger:
      - platform: state
        entity_id: binary_sensor.water_hot
        to: 'on'
    action:
      - service: switch.turn_off
        entity_id: switch.hot_water_template_switch`

The sensors section shows the hard coded priorities for the electrical consumers, followed by some sensors that extract data from the Fronius solar inverters. (Note: there’s now a Fronius component that you could use as an alternative, see https://www.home-assistant.io/integrations/fronius/). The following lines of code (sensors) calculate data for further usage either in the front end or for automations.

Have a look at my other project here to learn about a dynamic allocation of power to consumers, depending on custom set priorities.

   ##########################
   ###   -+ SECTION +-    ###
   ###      Sensors       ###
   ##########################

sensor:
   
   # hard-coded prio for energy consumers
 - platform: template
   sensors:
     energy_flow_prio:
       friendly_name: "prio 1 for energy consumption"
       value_template: >
         {% if states.binary_sensor.water_hot.state == 'off' %}
             hot_water
         {% elif states.binary_sensor.water_hot.state == 'on' %}
             ev_charge
         {% endif %}
       icon_template: >
         {% if states.binary_sensor.water_hot.state == 'off' %}
           mdi:water-boiler
         {% elif states.binary_sensor.water_hot.state == 'on' %}
           mdi:car
         {% endif %}

   # values from Fronius inverters
 - platform: rest
   resource: http://192.168.1.31/solar_api/v1/GetInverterRealtimeData.cgi?Scope=System
   name: real time AC production est
   value_template: "{{value_json.Body.Data.PAC.Values['1'] | int }}"
   unit_of_measurement: "W"
   scan_interval: 30

 - platform: rest
   resource: http://192.168.1.32/solar_api/v1/GetPowerFlowRealtimeData.fcgi
   name: gross AC production PV ouest
   value_template: "{{value_json.Body.Data.Site['P_PV']  | int  }}"
   unit_of_measurement: "W"
   scan_interval: 30

 - platform: rest
   resource: http://192.168.1.32/solar_api/v1/GetPowerFlowRealtimeData.fcgi
   name: net household grid injection
   value_template: "{{value_json.Body.Data.Site['P_Load']  | int  }}"
   unit_of_measurement: "W"
   scan_interval: 30

 - platform: rest
   resource: http://192.168.1.32/solar_api/v1/GetPowerFlowRealtimeData.fcgi
   name: public grid consumption
   value_template: "{{value_json.Body.Data.Site['P_Grid']  | int  }}"
   unit_of_measurement: "W"
   scan_interval: 30


 - platform: rest
   resource: http://192.168.1.32/solar_api/v1/GetInverterRealtimeData.cgi?Scope=System
   name: net AC production PV ouest
   value_template: "{{value_json.Body.Data.PAC.Values['1'] | int }}"
   unit_of_measurement: "W"
   scan_interval: 30
   
 - platform: rest
   resource: http://192.168.1.32/solar_api/v1/GetPowerFlowRealtimeData.fcgi
   name: Battery SOC
   unit_of_measurement: "%"
   value_template: "{{value_json.Body.Data.Inverters['1']['SOC'] | int }}"
   scan_interval: 30

 - platform: rest
   resource: http://192.168.1.32/solar_api/v1/GetPowerFlowRealtimeData.fcgi
   name: battery discharge
   unit_of_measurement: "W"
   value_template: "{{value_json.Body.Data.Site['P_Akku'] | int }}"
   scan_interval: 30


   # calculated values
 - platform: template
   sensors:
     cumulated_gross_real_time_production: # 
       friendly_name: "cumulated gross PV real time production"
       value_template: "{{ states.sensor.real_time_ac_production_est.state | int + states.sensor.gross_ac_production_pv_ouest.state | int }}"
       unit_of_measurement: "W"

 - platform: statistics
   entity_id: sensor.cumulated_gross_real_time_production
   name: cumulated production 5min
   max_age:
     minutes: 5
   precision: 0

 - platform: statistics
   entity_id: sensor.cumulated_gross_real_time_production
   name: cumulated production 30min
   max_age:
     minutes: 30
   precision: 0
   

# total household power consumption WITHOUT battery charge -> gives consumers priority over battery charge

 - platform: template
   sensors:
     household_power_consumption:
       friendly_name: "real time household consumption excl. battery charge"
       unit_of_measurement: "W"
       value_template: >
         {% if states.sensor.net_household_grid_injection.state  | int <= 0 %}
           {{ states.sensor.net_household_grid_injection.state  | int | abs +  states.sensor.real_time_ac_production_est.state | int }}
         {% elif states.sensor.net_household_grid_injection.state  | int > 0 %}
           {{ states.sensor.real_time_ac_production_est.state | int - states.sensor.net_household_grid_injection.state  | int  }}
         {%endif%}


 - platform: statistics
   entity_id: sensor.household_power_consumption
   name: total household consumption 5min
   max_age:
     minutes: 5
   precision: 0



# [[[ informational ]]] total household power consumption INCLUDING battery charge -> gives battery charge priority over anything else

 - platform: template
   sensors:
     household_power_consumption_inc_batt:
       friendly_name: "real time household consumption incl. battery charge"
       unit_of_measurement: "W"
       value_template: >
         {% if states.sensor.battery_discharge.state | int  <= 0 %}
           {{ states.sensor.household_power_consumption.state | int + states.sensor.battery_discharge.state | int | abs  }}
         {% elif states.sensor.battery_discharge.state | int > 0 %}
           {{ states.sensor.household_power_consumption.state | int }}
         {%endif%}

 - platform: statistics
   entity_id: sensor.household_power_consumption_inc_batt
   name: total household consumption inc batt 5min
   max_age:
     minutes: 5
   precision: 0

# total household excess power EXCLUDING battery charge 

 - platform: template
   sensors:
     total_excess_household_power_excl_batt_charge:
       friendly_name: "excess household power 5min mean excl batt"
       unit_of_measurement: "W"
       value_template: "{{ (states.sensor.cumulated_production_5min_mean.state | int - states.sensor.total_household_consumption_5min_mean.state | int) }}"

# total household excess power INCLUDING battery charge

 - platform: template
   sensors:
     total_real_time_excess_household_power:
       friendly_name: "excess household power inc batt"
       unit_of_measurement: "W"
       value_template: "{{ (states.sensor.cumulated_gross_real_time_production.state | int - states.sensor.household_power_consumption_inc_batt.state | int) }}"

 - platform: template
   sensors:
     total_excess_household_power:
       friendly_name: "excess household power inc batt (5min mean calc)"
       unit_of_measurement: "W"
       value_template: "{{ (states.sensor.cumulated_production_5min_mean.state | int - states.sensor.total_household_consumption_inc_batt_5min_mean.state | int) }}"


# calculate remaining_pv_light, dynamic_debt_offset and target_debt_soc

 - platform: template
   sensors:
     remaining_pv_light:
       friendly_name: "remaining daylight for PV energy production"
       unit_of_measurement: "h"
       value_template: >
         {% if is_state('sensor.season', 'summer') %}
           {{'%.2f' |format (states.sensor.remaining_daylight.state | float -1.5) }}
         {% else %}
           {{'%.2f' |format (states.sensor.remaining_daylight.state | float -1.15) }}
         {% endif %}
         
 - platform: template
   sensors:
     dynamic_debt_offset:
       friendly_name: "debt offset from 100% SOC"
       unit_of_measurement: "%"
       value_template: "{{ '%.2f' |format (states.input_number.dynamic_battery_debt.state | int * (states.sensor.remaining_pv_light.state | float / (states.sensor.daylength.state | float - 1.5))) }}"

 - platform: template
   sensors:
     target_debt_soc:
       friendly_name: "target dynamic SOC including debt"
       unit_of_measurement: "%"
       value_template: >
         {% if states.sensor.dynamic_debt_offset.state | int > 0 %}
           {{'%.2f' |format (100-states.sensor.dynamic_debt_offset.state | float) }}
         {% else %}
           99
         {%endif%}

The remaining pv light sensor gives information about the theoretical time which is left in order to produce energy. In my specific situation, the sun eventually gets blocked by either the neighbor’s house or a nearby forest, which I take into account in my template. Based on this value I can then calculate the dynamic offset for the energy debt as well as the target value for the battery over time (target_debt_soc).

  - platform: template
    sensors:
      remaining_daylight:
        friendly_name: "Remaining Daylight in hours"
        unit_of_measurement: "h"
        value_template: "{{ '%.2f' |format ((as_timestamp(states.sun.sun.attributes.next_setting) - as_timestamp(now())) / 3600) }}"

The binary sensors section first calculates whether the requirements for a heat pump authorization are met. It measures the operational state of the heat pump itself, puts it in relation with the remaining light for an operation cycle, then checks the next night’s minimum temperatures and compares the value of the heating water with a pre-set value. Only if this sensor returns “true”, the heat pump would be allowed to get authorized by an automation. The other sensors provide basic binary information about the status of some switches (on or off) and the dynamic SOC value of the battery.

For the calculation of heat pump related sensors (based on the Luxtronik control unit), please see the following thread: Writing a component for Luxtronik Heatpumps

    ##########################
    ###    - SECTION -     ###
    ###  Binary Sensors    ###
    ##########################
binary_sensor:

  - platform: template
    sensors:
      heat_gen_auth_requirements_fulfilled:
        friendly_name: "requirements for heat pump authorization are fulfilled"
#        value_template: "{{ (is_state('sensor.wp_betriebszustand', 'Heizen') or is_state('sensor.wp_betriebszustand', 'Abtauen')) and states.sensor.remaining_pv_light.state | float >= 1.3 and states.sensor.dark_sky_overnight_low_temperature_0.state | int < 11 and (45 - states.sensor.luxtronik_id_web_mitteltemperatur.state | int >= states.sensor.luxtronik_id_web_temperatur_tvl.state | int) }}"
        value_template: "{{ is_state('sensor.wp_betriebszustand', 'Heizen') or states.sensor.remaining_pv_light.state | float >= 1.2 and states.sensor.season.state != 'summer' and (states.sensor.luxtronik_id_web_mitteltemperatur.state | int < 10 or states.sensor.dark_sky_overnight_low_temperature_0.state | int < 15) and (45 - states.sensor.luxtronik_id_web_mitteltemperatur.state | int >= states.sensor.luxtronik_id_web_temperatur_tvl.state | int) }}"
        device_class: lock

  - platform: template
    sensors:
      ev_charging_authorized:
        friendly_name: "3.1kW EV charge"
        value_template: "{{ is_state('switch.ev_charging_authorization', 'on') }}"
        device_class: lock

  - platform: template
    sensors:
      heat_generation_authorized:
        friendly_name: "2kW heat pump"
        value_template: "{{ is_state('switch.heat_generation_authorization', 'on') }}"
        device_class: lock

  - platform: template
    sensors:
      target_soc_failure:
        friendly_name: "dynamic battery SOC status"
        device_class: battery   # battery device class: On means low, Off means normal
        value_template: "{{ states.sensor.battery_soc.state | int < (100- states.sensor.dynamic_debt_offset.state | int )}}"
        
  - platform: mqtt
    name: "hot water boiler 2kw"
    state_topic: "stat/sonoff_01/POWER"
    payload_on: "ON"
    payload_off: "OFF"
    device_class: power

Finally, the switches section manages the operation of the Sonoff basic and represents it as a template switch for easier visualisation in the GUI.

    ##########################
    ###    -+ SECTION +-   ### 
    ###      Switches      ###
    ##########################

switch: # Sonoff Basic 01 Switch manages heating coil for warm water (drinking water) production
  - platform: mqtt
    name: "hot water production"
    command_topic: "cmnd/sonoff_01/power"
    state_topic: "stat/sonoff_01/POWER"
    qos: 1
    payload_on: "on"
    payload_off: "off"
    retain: true

  - platform: template
    switches:
      hot_water_template_switch:
        friendly_name: "hot water boiler switch"
        value_template: "{{ is_state('binary_sensor.hot_water_boiler_2kw', 'on') }}"
        turn_on:
          service: switch.turn_on
          data:
            entity_id: switch.hot_water_production
        turn_off:
          service: switch.turn_off
          data:
            entity_id: switch.hot_water_production

Conclusion, open points and questions

Obviously, this concept will never be finished. I have however reached a level of satisfaction which gives me confidence into the achievements, even for the coming winter period with shorter an colder days.

It’s clear that there are negative points with regards to a “electricity to heating” concept. The downside of storing electric energy in warm water boilers is that you’ll always encounter a loss through degrading temperatures within the boiler.

One question remains with regards to my installation: Would this concept deteriorate the life cycle of the battery? I’m looking forward to your comments and advice :smiley:

26 Likes

Great stuff. However I think you are confusing kWh and kW.

kW is a measure of power, ie the rate of consumption of energy. A generator (ie a solar panel, or a hydro system) will be rated in kW. If it, say, produces 250v at 4A it produces 1000W or 1kW of power.

On the other hand kWh is a measure of energy. If the above example generator runs for one hour, it delivers 1kWh of energy. If it runs for two hours it delivers 2kWh.

1 Like

It took me a couple of reads to catch up to your highly developed and thought through approach here. I am sorry I cannot add more than to say, keep up the good work.

I think this solution is a peek into the future where increasingly smart methods of solar PV consumption will be sought after. I am in Australia where PV is huge, and currently exports are paid, but we can see an end to the FIT system looming.

I was just thinking it would be great to have a database of domestic appliances that resume their operational state after a forced “power cut”. This could allow a dishwasher be set up on a programme in the morning then a sonoff cut the power, ready to then have the power turned back on again, when one of your priority levels had been met, this resuming the programme and completing the task.

I know my Big Ass Haiku fan works in this way. I am just about to see if any ofmy other devices also resume functionality after a “power cut”

Washing machines
Clothes dryers
Dishwashers
???

Thanks again for your structural analysis of this issue and solutions provided?

Do you have this automation in operation?

J.

Thanks for your comment. In fact, I’d agree with you the desire to have more home appliances made “intelligent” in a way that they would resume where they left off in case of prior power cuts, etc. However in some cases this won’t work, as there are dependencies (hot water in the washing machine or dish washer, which would be could after 30 minutes of idle time).

To answer your question: yes, this is operational since some weeks and working quite well.

Thanks Markus,

I think I explained things poorly:
The idea of the power cut was not to interrupt the device cycle, but to enable the triggering of the start time for the device when solar PV generation is strong.

E.g. You load the washing machine, but it only has a dumb timer. Instead you start the relevant cycle, then 2 seconds later you use a Sonoff POW or any smart switch to turn the power off to the washing machine. It is now effectively in “standby” ready to be triggered to start a wash cycle via any automation at any time. Hacking a level of smartness into existing dumb appliances.

Using your priority automation, you could add switching on the washing machine Smart Switch as one of the items.

I don’t have an EV Car, or electric hot water heating, so was looking for other electrical “loads” to trigger.

I have so far confirmed that my washing machine and dishwasher both support “power-cycle resume” capabilities…

Sorry if I have hijacked your topic… but I think it is related.

1 Like

You’re absolutely right and your comment is absolutely in line with this thread because this is the ultimate goal → use available PV energy as much as possible and so that you prioritize certain “household services” in favour of others.

Maybe as a reference for other users: what are the devices that you’re using?

My home appliances that support power-cycle resume are:
Dishwasher: Bosch SMS66MI02A
Washing Machine: LG WD1408NPW

I have tested the Washing Machine but not the dishwasher.

The manual for the Bosch details this feature as a response to a power failure and that it resumes the wash cycle.

Hi Markus, looks really nice, I have the same PV-System with the Battery, but where and how do you get sensor.battery_soc this sensor?

Hi Dietlman! I’m using rest calls in order to extract this data:

sensor:
  - platform: rest
    resource: http://192.168.1.32/solar_api/v1/GetPowerFlowRealtimeData.fcgi
    name: Battery SOC
    unit_of_measurement: "%"
    value_template: "{{value_json.Body.Data.Inverters['1']['SOC'] | int }}"
    scan_interval: 60

Fronius provide a pretty complete documentation of their exposed API, you can search them online -> Fronius Solar API V1.

Hi Markus, thanks a lot, I will look into it, for now I am happy with the Battery SOC the other seonsor I get from the HACS Fronius integration.

This is absolutely fantastic. I am in the middle of my house construction, however, I spent months discussing with companies to do something similar. They are simply not willing to dive into something more complicated.

However, what you can find really important for your system is PV forecast. It might be more precise than just weather forecast because they focus directly on PV production optimization. I am not sure whether it works only in the Czech Republic or whether they overlap to Germany. If you are close to the borders, it would work. It is funded by the Czech Technical University and it’s free.

1 Like

Hi Markus
I have the same PV-system, one Symo and one Symo Hybrid + BYD, smartmeter as you have.
I am struggling a bit on the best way to query the inverters and how to have a global result with two inverters … Have you always used the API directly ?? How do you have the total power as you have two inverters ?
if you could post some exemple, I would appreciate… thanks
PS: coming from Switzerland too… :slight_smile:

Hello Michel! and welcome to the community!

I’ve always been using the API queries for the inverters and it works perfectly for me ever since.

Regarding your questions, you find all answers in the “sensor” section of my above yaml configuration: “real time AC production est for the Fronius Symo” and “gross AC production PV ouest” for the Fronius Symo Hybrid that manages the battery charge/discharge as well. The sensor "“cumulated gross PV real time production” brings the two inverter values together and calculates the gross AC production overall.

Any other question? I’m happy to help.

Cheers,
Markus

Absolutely fantastic… thanks Markus

Cheers

Michel

@mastermarkush thank you for sharing this one, I have a similar setup, just not yet a battery. I would like to implement a similar solution. I see you are triggering the heatpump by using a switch/relais, I have seen you also check the luxtronik ha integration in another post, so I was wondering if you also considered to use the heatpump via api integration? Or what is the advantage of using the switch? could I do as well with using the luxtronik write service to increase warm water temperatue for 45 minutes?

Thank you

Hi Chris! The advantage of using a simple switch was (back in mid-2019) that the “write” option was not yet available, so it was the ONLY solution available (to my best knowledge). Furthermore, I wanted to separate the heating from the water boiler, therefore the heatpump has its own switch (that triggers the photovoltaics function and initiates a heating cycle which gives me hot water reserve for under floor heating) and another switch that activates the boiler. Based on the amount of available PV energy, I can decide whether to trigger the boiler or whether to wait for the heatpump to initiate a cycle in order to heat water.

Your question is interesting as I haven’t yet thought about using the updated module including “write” permissions. It would make sense to do so though, as I would earn an even more granular control over the energy consumption. Whereas the heat pump would automatically heat water once the temperature is below a threshold, I could then proactively trigger this process… Sounds like a nice improvement :smiley:

Regarding your other question: looking at the latest documentation https://github.com/Bouni/luxtronik you should be able to change the value for warm water temperature (see ID_Soll_BWS_akt The set point for hot water generation, for example 50.0 for 50.0°C).

Hi Markus, that makes sense. Maybe you find the below configuration useful. I’ll have a look now how to decide to increase how and when to heat warmwater/boilder and floor heating more based on the PV energy available, without a battery yet. I have connected my home-assistant to my heatpump and can manage the heating and warmwater function using these values from the GUI.

      - group: parameters
        id: ID_Einst_WK_akt #Heizung Temperatur Soll/Anpassung
        friendly_name: Heizung Temperatur Soll Anpassung
      - group: parameters
        id: ID_Einst_BWS_akt #Warmwasser Temperatur Soll/Anpassung
        friendly_name: Warmwasser Temperatur Soll Anpassung
      - group: parameters
        id: ID_Ba_Hz_akt #The mode of operation of the heating circuit, possible values are "Automatic", "Second heatsource", "Party", "Holidays", "Off"
        friendly_name: Heizug Modus
      - group: parameters
        id: ID_Ba_Bw_akt #The mode of operation of the hot water circuit, possible values are "Automatic", "Second heatsource", "Party", "Holidays", "Off"
        friendly_name: Warmwasser Modus

Then I have automations to write to the luxtronik, when my input select or input number values change from UI, next step would be to automate using what you did.

input_select:
  luxheat_mode:
    name: Luxtronik Heizung Modus
    options:
      - "Automatic"
      - "Off"
      - "Party"
    icon: mdi:radiator

  luxww_mode:
    name: Luxtronik Warmwasser Modus
    options:
      - "Automatic"
      - "Off"
      - "Party"
    icon: mdi:hand-water

input_number:
  luxheat_shiftpoint:
    name: Luxtronik Heizung Shiftpoint 
    initial: 0
    min: -2
    max: 2
    step: 0.5
    mode: box

  luxww_shiftpoint:
    name: Luxtronik Warmwasser Shiftpoint 
    initial: 50
    min: 48
    max: 68
    step: 0.5
    mode: box

- id: '40'
  alias: Set Luxtronik Heating Mode
  trigger:
    entity_id: input_select.luxheat_mode
    platform: state
  action:
    service: luxtronik.write
    data:
      parameter: ID_Ba_Hz_akt
    data_template:
      value: '{{ trigger.to_state.state }}'
- id: '41'
  alias: Set Luxtronik Warmwater Mode
  trigger:
    entity_id: input_select.luxww_mode
    platform: state
  action:
    service: luxtronik.write
    data:
      parameter: ID_Ba_Bw_akt
    data_template:
      value: '{{ trigger.to_state.state }}'
- id: '42'
  alias: Set Luxtronik Heating Sollwert Verschiebung
  trigger:
    entity_id: input_number.luxheat_shiftpoint
    platform: state
  action:
    service: luxtronik.write
    data:
      parameter: ID_Einst_WK_akt
    data_template:
      value: '{{ trigger.to_state.state }}'
- id: '43'
  alias: Set Luxtronik Warmwasser Sollwert Verschiebung
  trigger:
    entity_id: input_number.luxww_shiftpoint
    platform: state
  action:
    service: luxtronik.write
    data:
      parameter: ID_Einst_BWS_akt
    data_template:
      value: '{{ trigger.to_state.state }}'
1 Like

I have upgraded to the latest release so that I can now write values for the desired warm water temperature. Some hints that I can give you:

  • Depending on the settings in the heat pump, you must insert a higher maximum value for the warm water temperature. Mine was set to 50 degrees Celsius, and I increased it to 60 degrees. I will never want to reach this value, however I need it because of the “hysteresis” -> read on
  • I changed the “hysteresis” (absolute Kelvin value of tolerance before the heat pump starts to kick-in) of the warm water production to a lower value (7 degrees Celsius) rather than 10 degrees or more. Now, with a standard desired temperature of 43 degrees, every increase of the desired temperature of at least 7 degrees will make the heat pump generate hot water. If you leave the “hysteresis” value too high, the heat pump might not start immediately
  • Finally I put the temperature in the luxtronik.write call to a value of 52 degrees. Now I can be sure that I can manually kick-off the production of warm water at whatever moment. The combination of standard temperature, hysteresis and max desired value is correct.
  • I tried out the “Party” Mode thinking that the heat pump would go into full-power mode and generate as much hot water as possible - this wasn’t the case in my installation though, so I reverted back to the manual increase of desired warm water temperature.
1 Like

what value do I need to change for the hysterese? I do not see a writeable value for that here python-luxtronik/luxtronik/parameters.py at 910ab0ed894cb8e3d686d812992e38055286907c · Bouni/python-luxtronik · GitHub? I assume only values with true can be written.

I had to change the hysterese value directly in the GUI of the heatpump, via service access.