Humbug modes for Hive heating entities?

Hi - Happy to and will reply fully tomorrow.


OK, it’s tomorrow!

Some points:

  • I do not use the Hive hub (anymore) as I am controlling locally using ZIgbee2MQTT, Mosquitto (MQTT) and a Sonoff USB-3 P dongle. ZHA/Conbee may also work but I haven’t checked.

  • I don’t use Tuya. My temp comes from the Hive thermostat because I think it is the most accurate in my house (I have Hue motion sensors with temp everywhere and I have calibrated them to the Hive thermostat).

  • I do have an Aqara TRV, but haven’t integrated it yet.

  • I boost using automations in HA; the Hive only gets off/on & increase/decrease temp instructions. This automation also keeps Hive heating in ‘Manual’ mode to allow HA scheduling to work.

  • HA/Hive will survive a restart and return to it’s prior status.

  • My schedules are a little different. A ‘default’ item is set for day/night/away/windows etc but these can then be overridden by 4 schedules with start/stop and a temp setting. I have found this allows for more flexible scheduling as, for example, I can have the temp set when I get up or at a specific time.

  • The temp is set by a sensor that takes into account all the variables - this allows my heating to survive a restart. The actual precedence can be seen in sensor.hive_heating_target_temp and is as follows:

    1. Boost
    2. Doors/Windows open
    3. Someone is home
    4. x4 Schedules
    5. House is awake
      …this works for me, but you can easily change the order.
  • There is an attribute called ‘Influence’ in sensor.hive_heating_target_temp that tells you which item is currently controlling the temp. This is useful to know as there are so many things! I have this displayed in the dashboard to keep me sane. It’s in the 2nd row (see dash screenshot in the next point) but copied below for you to see:

  • My dash looks like this (happy to share that code as well). Note: the water is ‘on’ right now.

    Here’s the same dash with one of the schedules and the defaults expanded:

  • My code sits in 2 self-contained packages - one for heating and one for water. They differ only in that the water has no temp setting. I’ll post the water later (it’s also less completed). Here is the heating package:

input_boolean: #----------------------------------------------------------------

  hive_heating_1_scheduled:

  hive_heating_2_scheduled:

  hive_heating_3_scheduled:

  hive_heating_4_scheduled:


input_datetime: #---------------------------------------------------------------

  hive_heating_1_on:
    name: Heating (1) on
    icon: mdi:radiator
    has_date: false
    has_time: true

  hive_heating_1_off:
    name: Heating (1) off
    icon: mdi:radiator-off
    has_date: false
    has_time: true

  hive_heating_2_on:
    name: Heating (2) on
    icon: mdi:radiator
    has_date: false
    has_time: true

  hive_heating_2_off:
    name: Heating (2) off
    icon: mdi:radiator-off
    has_date: false
    has_time: true

  hive_heating_3_on:
    name: Heating (3) on
    icon: mdi:radiator
    has_date: false
    has_time: true

  hive_heating_3_off:
    name: Heating (3) off
    icon: mdi:radiator-off
    has_date: false
    has_time: true

  hive_heating_4_on:
    name: Heating (4) on
    icon: mdi:radiator
    has_date: false
    has_time: true

  hive_heating_4_off:
    name: Heating (4) off
    icon: mdi:radiator-off
    has_date: false
    has_time: true


input_number: #-----------------------------------------------------------------

  hive_heating_graph_span:    # for history graph
    name: "Days to show"
    unit_of_measurement: days(s)
    min: 1
    max: 7
    #initial: 1
    step: 1
    icon: mdi:history

  hive_heating_open_temp:
    name: "Windows open"
    unit_of_measurement: °C
    min: 12
    max: 22
    #initial: 12
    step: 0.5
    icon: mdi:power-off

  hive_heating_away_temp:     # recommends 15min when away
    name: "Away temp"
    unit_of_measurement: °C
    min: 12
    max: 22
    #initial: 15
    step: 0.5
    icon: mdi:power-off

  hive_heating_asleep_temp:   # NHS recommends 18 when asleep
    name: "Asleep temp"
    unit_of_measurement: °C
    min: 12
    max: 19
    #initial: 17
    step: 0.5
    icon: mdi:power-on

  hive_heating_awake_temp:    # Gov recommends 21.  Who recommends 21 in living room, 18 in bedroom
    name: "Awake temp"
    unit_of_measurement: °C
    min: 12
    max: 22
    #initial: 20
    step: 0.5
    icon: mdi:power-on

  hive_heating_1_temp:
    unit_of_measurement: °C
    min: 18
    max: 22
    #initial: 20
    step: 0.5
    icon: mdi:thermometer

  hive_heating_2_temp:
    unit_of_measurement: °C
    min: 18
    max: 22
    #initial: 20
    step: 0.5
    icon: mdi:thermometer
  hive_heating_3_temp:
    unit_of_measurement: °C
    min: 18
    max: 22
    #initial: 20
    step: 0.5
    icon: mdi:thermometer
  hive_heating_4_temp:
    unit_of_measurement: °C
    min: 18
    max: 22
    #initial: 20
    step: 0.5
    icon: mdi:thermometer

  hive_heating_boost_prior_temp:
    name: "Previous temp"
    min: 10
    max: 25         # set high to ensure mum's morning towel boost works even when already hot inside
    step: 1
    initial: 19
    unit_of_measurement: °C
    icon: mdi:thermometer
    mode: box
  
  hive_heating_boost_temp:
    name: "Boost by"
    min: 0.5
    max: 2
    step: 0.5
    #initial: 1.0
    unit_of_measurement: °C
    icon: mdi:plus
    mode: slider

  hive_heating_boost_time:
    name: "Boost for"
    min: 0.5
    max: 4
    step: 0.5
    #initial: 2
    unit_of_measurement: Hrs
    icon: mdi:plus
    mode: slider


input_select: #-----------------------------------------------------------------

  hive_heating_target_mode:
    name: Hive heating mode
    icon: mdi:radiator-disabled
    options:
      - "off"                     # Hive thermostat is in frost protect
      - "heat"                    # Hive thermostat is in manual control
      #- "emergency_heating"       # Hive thermostat is boosting
      #- "auto"                    # Hive thermostat/app scheduled


sensor: #-----------------------------------------------------------------------

  - platform: history_stats
    name: Hive heating today
    entity_id: binary_sensor.hive_heating_reported_action
    state: "on"
    type: time
    start: "{{ now().replace(hour=0, minute=0, second=0) }}"
    end: "{{ now() }}"


template: #---------------------------------------------------------------------

  - binary_sensor:

      # Action as reported by HIVE
      #         mum (SLR1c)     James SLR2c:
      # state:  climate.hive    climate.hive_heat
      - name: "Hive heating reported action"
        unique_id: hive_heating_reported_action
        state: "{{ is_state_attr('climate.hive_heat','hvac_action', 'heating') }}"

      # doors and windows that are heating relevant (excl. fr. door which opens for short periods answering bell
      - name: "Hive heating doors/windows open"
        state: >
          {{
            states.binary_sensor
            | selectattr('entity_id', 'search', '(doors?|window)_contact')
            | rejectattr('entity_id', 'search', 'bedroom_[a-z,1]|ensuite|front')
            | selectattr('state', 'match', 'on')
            | list  
            | count 
          }}
        attributes:
          influence: >
            {{
              states.binary_sensor
              | selectattr('entity_id', 'search', '(doors?|window)_contact')
              | rejectattr('entity_id', 'search', 'bedroom_[a-z,1]|ensuite|front')
              | selectattr('state', 'match', 'on')
              | sort(attribute='name')
              | map(attribute='name')
              | list
              | join(',\n')
              | replace(' contact', '')
            }}

  - sensor:

      # Mode as reported by HIVE thermostat: off, heat, auto (Hive schedule), emergency_heating (Hive boost)
      #         mum (SLR1c)     James SLR2c:
      # state:  climate.hive    climate.hive_heat
      - name: "Hive heating reported mode"
        unique_id: hive_heating_reported_mode
        state: "{{ states('climate.hive_heat') }}"
      # state: "{{ state_attr('climate.hive_heat','system_mode_heat') }}"

      # Current temp as reported by HIVE thermostat
      #         mum (SLR1c)     James SLR2c:
      # state:  climate.hive    climate.hive_heat
      - name: "Hive heating reported current temp"
        unique_id: hive_heating_reported_current_temp
        device_class: temperature
        unit_of_measurement: °C
        state: "{{ state_attr('climate.hive_heat','current_temperature') }}"
        state_class: measurement

      # Target temp as reported by HIVE thermostat
      #         mum (SLR1c)     James SLR2c:
      # state:  climate.hive    climate.hive_heat
      - name: "Hive heating reported target temp"
        unique_id: hive_heating_reported_target_temp
        device_class: temperature
        unit_of_measurement: °C
        state: "{{ state_attr('climate.hive_heat','temperature') }}"
        state_class: measurement

      # Triggers hive automation to set temp to this sensor 
      - name: "Hive heating target temp"
        unique_id: hive_heating_target_temp
        device_class: temperature
        unit_of_measurement: °C
        availability: "{{ has_value('binary_sensor.hive_heating_doors_windows_open') }}"
        attributes:
          influence: >
            {% set shed1 = states('input_boolean.hive_heating_1_scheduled') %}       
            {% set shed2 = states('input_boolean.hive_heating_2_scheduled') %}       
            {% set shed3 = states('input_boolean.hive_heating_3_scheduled') %}       
            {% set shed4 = states('input_boolean.hive_heating_4_scheduled') %}       

            {% set on1 = states('input_datetime.hive_heating_1_on')[:5] %}       
            {% set on2 = states('input_datetime.hive_heating_2_on')[:5] %}       
            {% set on3 = states('input_datetime.hive_heating_3_on')[:5] %}       
            {% set on4 = states('input_datetime.hive_heating_4_on')[:5] %}       

            {% set off1 = states('input_datetime.hive_heating_1_off')[:5] %}       
            {% set off2 = states('input_datetime.hive_heating_2_off')[:5] %}       
            {% set off3 = states('input_datetime.hive_heating_3_off')[:5] %}       
            {% set off4 = states('input_datetime.hive_heating_4_off')[:5] %}       

            {% set mode =     states('input_select.hive_heating_target_mode') %}
            {% set boost =    states('timer.hive_heating_boost') %}
            {% set bathroom = states('timer.hive_heating_boost_bathroom') %}
            {% set dr_win =   states('binary_sensor.hive_heating_doors_windows_open') %}
            {% set home =     states('zone.home') %}          
            {% set time =     states('sensor.time') %}
            {% set house =    states('input_select.house_mode') %}
          
            {%   if mode        == 'heat' %}
              {%   if boost     == 'active' %}                                  Boost
              {% elif bathroom  == 'active' %}                                  Bathroom
              {% elif dr_win    == 'on' %}                                      Dr/Win open
              {% elif home      != '0' %}
                {%   if shed1   == 'on' and (time >= on1) and (time < off1) %}  Schedule 1
                {% elif shed2   == 'on' and (time >= on2) and (time < off2) %}  Schedule 2
                {% elif shed3   == 'on' and (time >= on3) and (time < off3) %}  Schedule 3
                {% elif shed4   == 'on' and (time >= on4) and (time < off4) %}  Schedule 4
                {% elif house   == 'Awake' %}                                   Awake temp
                {% else %}                                                      Asleep temp
                {% endif %}
              {% else %}                                                        Away temp
              {% endif %}
            {% else %}                                                          Off
            {% endif %}
        state: >            # uses attribute 'influence'
          {% set temp1 =    states('input_number.hive_heating_1_temp')|float %}       
          {% set temp2 =    states('input_number.hive_heating_2_temp')|float %}       
          {% set temp3 =    states('input_number.hive_heating_3_temp')|float %}
          {% set temp4 =    states('input_number.hive_heating_4_temp')|float %}
          {% set open =     states('input_number.hive_heating_open_temp') %}       
          {% set away =     states('input_number.hive_heating_away_temp') %}       
          {% set awake =    states('input_number.hive_heating_awake_temp') %}       
          {% set asleep =   states('input_number.hive_heating_asleep_temp') %}   
          {% set boostpre = states('input_number.hive_heating_boost_prior_temp')|float %}
          {% set boostby =  states('input_number.hive_heating_boost_temp')|float %}

          {% set influence = state_attr('sensor.hive_heating_target_temp','influence') %}       
        
          {%   if influence == 'Boost' %}       {{ boostpre + boostby }}
          {% elif influence == 'Bathroom' %}    {{ boostpre + 0.5 }}
          {% elif influence == 'Dr/Win open' %} {{ open }}
          {% elif influence == 'Schedule 1' %}  {{ temp1 }}
          {% elif influence == 'Schedule 2' %}  {{ temp2 }}
          {% elif influence == 'Schedule 3' %}  {{ temp3 }}
          {% elif influence == 'Schedule 4' %}  {{ temp4 }}
          {% elif influence == 'Awake temp' %}  {{ awake }}
          {% elif influence == 'Asleep temp' %} {{ asleep }}
          {% elif influence == 'Away temp' %}   {{ away }}
          {% elif influence == 'Off' %}            12
          {% else %}                               15
          {% endif %}
        state_class: measurement
        icon: >
          {% set influence = state_attr('sensor.hive_heating_target_temp','influence') %}       
        
          {%   if influence == 'Boost' %}       mdi:timer
          {% elif influence == 'Bathroom' %}    mdi:shower
          {% elif influence == 'Dr/Win open' %} mdi:door-open
          {% elif influence == 'Schedule 1' %}  mdi:calendar-check-outline
          {% elif influence == 'Schedule 2' %}  mdi:calendar-check-outline
          {% elif influence == 'Schedule 3' %}  mdi:calendar-check-outline
          {% elif influence == 'Schedule 4' %}  mdi:calendar-check-outline
          {% elif influence == 'Awake temp' %}  mdi:power
          {% elif influence == 'Asleep temp' %} mdi:weather-night
          {% elif influence == 'Away temp' %}   mdi:car
          {% elif influence == 'Off' %}         mdi:power-off
          {% else %}                            mdi:help
          {% endif %}


timer: #------------------------------------------------------------------------

  # Boosts when timer is 'active'     by 'boost_temp' [℃] for boost_time [hrs] 
  hive_heating_boost:
    name: Hive heating boost timer
    icon: mdi:radiator
    restore: true 

  # Boosts when timer is 'active'     by 0.5 [℃] for 30 [mins]
  hive_heating_boost_bathroom:
    name: Hive heating bathroom timer
    duration: "00:30:00"
    icon: mdi:shower
    restore: true 
    

script:  #----------------------------------------------------------------------

  hive_heating_increase_setpoint:
    alias: "Hive heating: Increase setpoint"
    sequence:
      - service: climate.set_temperature
        data:
          temperature: "{{ states('sensor.hive_heating_reported_target_temp') |float() +0.5 }}"
          entity_id: climate.hive_heat
    icon: mdi:thermometer-plus
    mode: queued

  hive_heating_decrease_setpoint:
    alias: "Hive heating: Increase setpoint"
    sequence:
      - service: climate.set_temperature
        data:
          temperature: "{{ states('sensor.hive_heating_reported_target_temp') |float() -0.5 }}"
          entity_id: climate.hive_heat
    icon: mdi:thermometer-minus
    mode: queued

  # only needed as I'm not sure how to set timer duration within custom:button
  hive_heating_toggle_boost:
    alias: "Hive heating: Toggle boost"
    sequence:
      - if:
          - condition: state
            entity_id: timer.hive_heating_boost
            state: 'active'
        then:
          - service: timer.finish
            target:
              entity_id: timer.hive_heating_boost
        else:                 
          # record prior target temp to allow sensor.hive_heating_target_temp to set correct boost temp
          - service: input_number.set_value
            target:
              entity_id: input_number.hive_heating_boost_prior_temp
            data:
              value: "{{ states('sensor.hive_heating_reported_current_temp') |float(15) }}"
          - service: timer.start
            target:
              entity_id: timer.hive_heating_boost
            data:
              duration:  "{{ (states('input_number.hive_heating_boost_time') |float(30)) *3600 }}"
    icon: mdi:radiator
    mode: single


automation:  #------------------------------------------------------------------

  # set mode and temp
  # Name of receiver is "Hive":
      # SLR1c: zigbee2mqtt/Hive/set
      # SLR2c: zigbee2mqtt/Hive/heat/set
      #     &: zigbee2mqtt/Hive/water/set 
  - alias: Hive heating
    id: "hive_heating"
    description: ''
    mode: queued
    initial_state: true                                     # ensure automation is on at startup
    trigger:
      - platform: template                                  # reset mode if out-of-sync
        value_template: "{{ states('sensor.hive_heating_reported_mode') != states('input_select.hive_heating_target_mode') }}"
      - platform: state
        entity_id: input_select.hive_heating_target_mode    # user sets sets new mode
      - platform: homeassistant
        event: start                                        # set correct mode on startup
#       - platform: state
#         entity_id: input_button.set_hive_heating            # set correct mode manually - used in sidebar alarm
      - platform: state
        entity_id: sensor.hive_heating_target_temp          # programme sets new temp
    action:
      - choose:
          - conditions:
              - condition: state
                entity_id: input_select.hive_heating_target_mode
                state: 'off'
            sequence:
              - service: mqtt.publish
                data:
                  topic: zigbee2mqtt/Hive/heat/set
                  payload: |-
                    {
                      "system_mode":"off",
                      "temperature_setpoint_hold":"0"
                    }
          - conditions:
              - condition: state
                entity_id: input_select.hive_heating_target_mode
                state: 'heat'
            sequence:
              - service: mqtt.publish
                data:
                  topic: zigbee2mqtt/Hive/heat/set
                  payload: |-
                    {
                      "system_mode":"heat",
                      "temperature_setpoint_hold":"1",
                      "occupied_heating_setpoint":"{{ states('sensor.hive_heating_target_temp') }}"
                    }
          - conditions:
              - condition: state
                entity_id: input_select.hive_heating_target_mode
                state: 'auto'
            sequence:
              - service: mqtt.publish
                data:
                  topic: zigbee2mqtt/Hive/heat/set
                  payload: |-
                    {
                      "system_mode":"auto",
                      "temperature_setpoint_hold":"1"
                    }
          - conditions:
              - condition: state
                entity_id: input_select.hive_heating_target_mode
                state: 'emergency_heating'
            sequence:
              - service: mqtt.publish
                data:
                  topic: zigbee2mqtt/Hive/heat/set
                  payload: |-
                    {
                      "system_mode":"emergency_heating",
                      "temperature_setpoint_hold":"1",
                      "temperature_setpoint_hold_duration":"{{ states('input_number.hive_heating_boost_time')|float *60 }}",
                      "occupied_heating_setpoint":"{{ states('sensor.hive_heating_reported_current_temp')|float + states('input_number.hive_heating_boost_temp')|float }}"
                    }

  - alias: "Hive heating: Wakeup boost"       # warm mum's radiators for 30 mins or 0.5℃ when she gets up
    id: hive_heating_wakeup_boost
    mode: single
    variables:
      boost: "{{ states('sensor.hive_heating_reported_current_temp') |float(15) }}"
      awake: "{{ states('input_number.hive_heating_awake_temp')|float(15) }}"
    trigger:
      - platform: state
        entity_id: input_select.house_mode
        to: "Awake"
    condition:
      - condition: numeric_state              # someone is home
        entity_id: zone.home
        above: 0
      - condition: state                      # heating is off
        entity_id: binary_sensor.hive_heating_reported_action
        state: 'off'
      - condition: numeric_state              # outside temp under 18℃
        entity_id: sensor.weather_temp
        below: 18
      - "{{ boost > awake }}"                 # check boost temp > awake temp - otherwise just turn heating on as usual
    action:
      - service: input_number.set_value       # set boost_prior for sensor.hive_heating_target_temp
        target:
          entity_id: input_number.hive_heating_boost_prior_temp
        data:
          value: "{{ boost }}"
      - service: timer.start
        target:
          entity_id: timer.hive_heating_boost_bathroom
        data:
          duration: "1800"                    # 30 mins x 30 seconds

The automation is actually quite simple, the ‘brains’ are in sensor.hive_heating_target_temp

Note that the automation uses other sensors including:

states('binary_sensor.hive_heating_doors_windows_open').  # are any heating relevant windows open? (excludes front door)
states('zone.home')                                       # Is anyone home?
states('sensor.time')                                     # what time is it? ...from system_monitor integration
states('input_select.house_mode')                         # is my house 'awake', 'asleep' or in 'manual' mode?

Note there are 2 boosts:

  • The 1st is ‘normal’ (controlled my timer.hive_heating_boost) and can be set by time or temp rise. Merely starting this timer sets the hive to ‘boost’
  • The 2nd is 'special (controlled by timer.hive_heating_boost_bathroom). This is fixed to 30 mins and goes off when house_mode goes from asleep to awake & it’s a colder day & the heating isn’t already on. This is for my mum who has a single-channel receiver (no hot water control) who likes to have her bathroom radiators warm when she showers in the morning. 30 mins is enough to do that without heating the house up as well. You can remove any code related to this if you have a 2-channel Hive receiver. …I only have this as I maintain mums HA and we share this package. Leaving it there and ignoring it also works :slight_smile:

…bet you wished you never asked :joy:
Have a read and ask any questions you like :+1:

4 Likes