From 6f69cc1421ad7e0d326a480713a9a40e11ac193b Mon Sep 17 00:00:00 2001 From: Constantin Pascal Date: Sat, 7 Feb 2026 14:15:20 +0200 Subject: [PATCH] excess pv boiler stuff --- excess_pv_boiler/automations.yaml | 70 +++++++++++++++++++++++++++++ excess_pv_boiler/climate.yaml | 23 ++++++++++ excess_pv_boiler/input_boolean.yaml | 20 +++++++++ excess_pv_boiler/input_number.yaml | 23 ++++++++++ excess_pv_boiler/templates.yaml | 42 +++++++++++++++++ 5 files changed, 178 insertions(+) create mode 100644 excess_pv_boiler/automations.yaml create mode 100644 excess_pv_boiler/climate.yaml create mode 100644 excess_pv_boiler/input_boolean.yaml create mode 100644 excess_pv_boiler/input_number.yaml create mode 100644 excess_pv_boiler/templates.yaml diff --git a/excess_pv_boiler/automations.yaml b/excess_pv_boiler/automations.yaml new file mode 100644 index 0000000..4af02ac --- /dev/null +++ b/excess_pv_boiler/automations.yaml @@ -0,0 +1,70 @@ +- alias: DHW Boiler Heater Relay Automation + description: >- + This automation makes sure the boiler auxiliary heater only works when the + boost is required and the heatpump aux heater is not on + triggers: + - trigger: state + entity_id: + - binary_sensor.dhw_use_boiler_aux_heater + conditions: [] + actions: + - if: + - condition: state + entity_id: binary_sensor.dhw_use_boiler_aux_heater + state: "on" + then: + - type: turn_on + device_id: b6129664e9bea24cdab57a9c743b885d + entity_id: 1b7e7c5bd07a3823f8df13b623f95e0a + domain: switch + else: + - type: turn_off + device_id: b6129664e9bea24cdab57a9c743b885d + entity_id: 1b7e7c5bd07a3823f8df13b623f95e0a + domain: switch + mode: single + +- alias: DHW Manage PV Thermostat + description: Controls shadow thermostat based on PV excess availability + mode: restart + trigger: + # Trigger when PV power becomes sufficient (with delay) + - platform: state + entity_id: binary_sensor.dhw_pv_power_enough_for_aux_heater + to: "on" + for: + minutes: "{{ states('input_number.dhw_pv_excess_usage_start_delay')|int }}" + id: "turn_on" + + # Trigger when PV power becomes insufficient (with delay) + - platform: state + entity_id: binary_sensor.dhw_pv_power_enough_for_aux_heater + to: "off" + for: + minutes: "{{ states('input_number.dhw_pv_excess_usage_stop_delay')|int }}" + id: "turn_off" + action: + - choose: + - conditions: + - condition: state + entity_id: input_boolean.dhw_use_excess_pv_for_aux_heater + state: "on" + - condition: trigger + id: "turn_on" + sequence: + - service: climate.set_temperature + target: + entity_id: climate.dhw_pv_excess_heating + data: + temperature: "{{ states('input_number.dhw_pv_target_temperature') | float }}" + hvac_mode: "heat" + + - conditions: + - condition: trigger + id: "turn_off" + sequence: + - service: climate.set_hvac_mode + target: + entity_id: climate.dhw_pv_excess_heating + data: + hvac_mode: "off" \ No newline at end of file diff --git a/excess_pv_boiler/climate.yaml b/excess_pv_boiler/climate.yaml new file mode 100644 index 0000000..8e23fb4 --- /dev/null +++ b/excess_pv_boiler/climate.yaml @@ -0,0 +1,23 @@ +- platform: generic_thermostat + name: "DHW PV Excess Heating" + heater: input_boolean.boiler_pv_dummy_heater + target_sensor: sensor.boiler_temp + min_temp: 10 + max_temp: 70 + cold_tolerance: 1 + hot_tolerance: 0.5 + min_cycle_duration: + minutes: 2 + initial_hvac_mode: "off" + precision: 0.1 + +- platform: generic_thermostat + name: "Boiler Temperature" + heater: input_boolean.boiler_pv_dummy_heater # Uses the dummy switch + target_sensor: sensor.boiler_temp # Or whatever sensor you're using from heatpump + min_temp: 10 + max_temp: 70 + cold_tolerance: 4.0 + hot_tolerance: 1.0 + min_cycle_duration: + minutes: 2 \ No newline at end of file diff --git a/excess_pv_boiler/input_boolean.yaml b/excess_pv_boiler/input_boolean.yaml new file mode 100644 index 0000000..de49a06 --- /dev/null +++ b/excess_pv_boiler/input_boolean.yaml @@ -0,0 +1,20 @@ +dhw_aux_heater_manual_override: + name: "DHW Aux Heater Manual Override" + icon: mdi:water-boiler + +dhw_allow_aux_heater_usage: + name: "DHW Allow Aux Heater Usage" + icon: mdi:check-circle + +dhw_use_excess_pv_for_aux_heater: + name: "DHW Use Excess PV for Aux Heater" + icon: mdi:solar-power + +dhw_allow_aux_heater_in_all_modes: + name: "DHW Allow Aux Heater in All Modes" + icon: mdi:thermostat + +# Dummy switch for shadow thermostat +boiler_pv_dummy_heater: + name: "Boiler PV Dummy Heater" + icon: mdi:flash \ No newline at end of file diff --git a/excess_pv_boiler/input_number.yaml b/excess_pv_boiler/input_number.yaml new file mode 100644 index 0000000..34b870f --- /dev/null +++ b/excess_pv_boiler/input_number.yaml @@ -0,0 +1,23 @@ +dhw_aux_heater_cut_off_temperature_threshold: + name: "DHW Aux Heater Cut-off Temperature Threshold" + min: 50 + max: 85 + step: 1 + unit_of_measurement: "°C" + icon: mdi:thermometer-high + +dhw_aux_heater_temperature_threshold: + name: "DHW Aux Heater Temperature Threshold" + min: 1 + max: 15 + step: 0.5 + unit_of_measurement: "°C" + icon: mdi:thermometer-lines + +dhw_pv_target_temperature: + name: "DHW PV Target Temperature" + min: 40 + max: 70 + step: 1 + unit_of_measurement: "°C" + icon: mdi:solar-power \ No newline at end of file diff --git a/excess_pv_boiler/templates.yaml b/excess_pv_boiler/templates.yaml new file mode 100644 index 0000000..aa4599e --- /dev/null +++ b/excess_pv_boiler/templates.yaml @@ -0,0 +1,42 @@ +- binary_sensor: + + - name: "DHW: Use Boiler Aux Heater" + unique_id: "dhw_config_use_boiler_aux_heater" + state: > + {% set manual_on = states('input_boolean.dhw_aux_heater_manual_override') == 'on' %} + {% set hp_aux_off = states('sensor.heat_pump_auxiliary_heater') != 'on' %} + {% set boiler_aux_allowed = states('input_boolean.dhw_allow_aux_heater_usage') == 'on' %} + {% set boiler_temp_ok = states('sensor.boiler_temp')|float < states('input_number.dhw_aux_heater_cut_off_temperature_threshold')|float %} + {# allowed checks if the resistance is permitted to run, mostly to check we're not running both boiler and HP resistance at the same time #} + {% set allowed = hp_aux_off and boiler_aux_allowed and boiler_temp_ok %} + {# here go various other conditions that influence the decision to run the boiler auxiliary heater #} + {# PV Excess to boiler - now using shadow thermostat #} + {% set pv_excess = states('input_boolean.dhw_use_excess_pv_for_aux_heater') == 'on' and + state_attr('climate.dhw_pv_excess_heating', 'hvac_action') == 'heating' %} + {% set thermostat_activity_boost = state_attr('climate.boiler_temperature','preset_mode') == 'activity' and + state_attr('climate.boiler_temperature','hvac_action') == 'heating'%} + {% set thermostat_normal_boost = states('input_boolean.dhw_allow_aux_heater_in_all_modes') == 'on' and + state_attr('climate.boiler_temperature','hvac_action') == 'heating'%} + {{ manual_on or + (allowed and ( + pv_excess or + thermostat_activity_boost or + thermostat_normal_boost + )) }} + + - name: "DHW: PV Power Enough for Aux Heater " + unique_id: "dhw_pv_power_enough_for_aux_heater" + state: > + {% set phase_buffer = states('input_number.dhw_pv_excess_usage_buffer')|float / 3 %} + {% set phase_heater_power= states('input_number.dhw_aux_heater_power')|float /3 %} + {% set turn_on_threshold = -1*(phase_buffer+phase_heater_power) %} + {% if is_state('switch.boiler_heater_relay_switch_0', 'on') %} + {{ states('sensor.deye_12_external_ct1_power')|float <= 0 + and states('sensor.deye_12_external_ct2_power')|float <= 0 + and states('sensor.deye_12_external_ct3_power')|float <= 0 }} + {% else %} + {{ states('sensor.deye_12_external_ct1_power')|float < turn_on_threshold + and states('sensor.deye_12_external_ct2_power')|float < turn_on_threshold + and states('sensor.deye_12_external_ct3_power')|float < turn_on_threshold }} + {% endif %} + device_class: power