I have a problem debugging a blueprint.There is apparently an error now in a template that used to work, and still works in the developer tools.
Following helpful answers to my post https://community.home-assistant.io/t/new-blueprint-message-malformed-required-key-not-provided/680077, I think I got the hang of numbering the YAML items and added them to the comments (please check). I solved the bug in that post and a couple more, but am now stuck on this one, apparently in a template.
I now have the message
Message malformed: Unexpected value for condition: 'None'. Expected and, device, not, numeric_state, or, state, sun, template, time, trigger, zone @ data['action'][1]['choose'][4]['conditions'][1]
Here is the latest code:
### ------------------------------------------------------------------------------------
###
### HEATING X, Release 2
### --------------------
###
### Release 1 (Feb 2023) had the following features
### Controls one or more thermostats from a calendar
### Allows temporary manual override
### Optionally turns off thermostat if a door or window is opened,
### Optionally turns off thermostat if the room is unoccupied for a while.
###
### Release 2 (Feb 2024) adds the following features
### Away mode. Changes the heating of all rooms to the 'away temperature' setting when on, reverts to the usual schedules etc when off.
### Background temperature can be specified to use when there is no active calendar event or room is unoccupied
### TRV battery life saved by transmitting to TRV only when there is an actual change
###
### 06-Feb-23 | Andy Symons | Release 1. See feature list above
### 26-Jan-24 | Andy Symons | Release 2. See feature list above
###
### ------------------------------------------------------------------------------------
blueprint:
name: "Heating X2" # temporary name for testing
description: Controls one or more thermostats from a calendar, allows temporary manual override, and optionally turns off thermostat if a door or window is opened, or if the room is unoccupied for a while.
domain: automation
### ----------------------------------------------------------------------------
### INPUTS
### ----------------------------------------------------------------------------
input:
thermostat_controls:
name: DEVICE ENTITY - Thermostat control (mandatory)
description: One or more thermostat entities that are to be controlled by this automation
selector:
entity:
filter:
domain: climate
multiple: true
thermostat_set_temperature_sensors:
name: DEVICE ENTITY - Thermostat set temperature (mandatory)
description: The (template) sensors that read the set temperature from the specified thermostats
selector:
entity:
filter:
domain: sensor
multiple: true
door_or_window_open_sensors:
name: DEVICE ENTITY - Door or window open sensors (if applicable)
description: Zero or more sensors that detect whether a door or window is open
selector:
entity:
filter:
domain: binary_sensor
device_class: opening
multiple: true
default: []
room_occupancy_sensors:
name: DEVICE ENTITY - room occupancy sensors (if applicable)
description: Zero or more sensors that detect whether there is anyone in the room
selector:
entity:
filter:
domain: binary_sensor
device_class: occupancy
multiple: true
default: []
away_switch:
name: DEVÄ°CE ENTITY - Away switch, binary sensor or input boolean (if required)
description: A switch, input boolean, or binary sensor that changes all rooms to 'away' mode
selector:
entity:
room_calendar:
name: CALENDAR ENTITY - room calendar (mandatory)
description: The calendar dedicated to scheduling events for this room
selector:
entity:
filter:
domain: calendar
manual_temperature:
name: HELPER - Manual temperature (mandatory)
description: The global variable (helper) to hold the required temperature
selector:
entity:
filter:
domain: input_number
set_temperature:
name: HELPER - Set temperature (mandatory)
description: The global variable (helper) to hold the required temperature
selector:
entity:
filter:
domain: input_number
required_temperature:
name: HELPER - Required temperature (mandatory)
description: The global variable (helper) to hold the required temperature
selector:
entity:
filter:
domain: input_number
setting_reason:
name: HELPER - Setting reason (mandatory)
description: The global variable (helper) into which the automation writes the reason for the current setting (for use on a dashboard)
selector:
entity:
filter:
domain: input_text
door_or_window_open_timer:
name: HELPER - Door or window open timer (mandatory even if not used)
description: The global variable (helper) to hold the timer for the period since a door or window was opened
selector:
entity:
filter:
domain: timer
unoccupancy_timer:
name: HELPER - Unoccupancy timer (mandatory even if not used)
description: The global variable (helper) to hold the timer for the period since the room was last unoccupied
selector:
entity:
filter:
domain: timer
warmup_timer:
name: HELPER - Warmup timer (mandatory)
description: The global variable (helper) to hold the timer for the event warmup period
selector:
entity:
filter:
domain: timer
manual_override_timer:
name: HELPER - Manual override timer (mandatory)
description: The global variable (helper) to hold the timer for a manual intervention
selector:
entity:
filter:
domain: timer
echoblock_timer:
name: HELPER - Echoblock timer (mandatory)
description: The timer for use inside the automation to disinguish genuine manual changes of the set temperature from those set by the automation
selector:
entity:
filter:
domain: timer
minimum_thermostat_temperature:
name: PARAMETER - Minimum thermostat temperature
description: The minimum temperature that the thermostat device can accept
selector:
number:
min: 0
max: 100
default: 5
maximum_thermostat_temperature:
name: PARAMETER - Maximum thermostat temperature
description: The maximum temperature that the thermostat device can accept
selector:
number:
min: 0
max: 100
default: 30
frost_setting:
name: PARAMETER - Frost setting
description: The temperature to be used when the heating is turned off
selector:
number:
min: 0
max: 100
default: 5
away_temperature:
name: PARAMETER - Away temperature
description: The temperature to be used when away mode is in operation
selector:
number:
min: 0
max: 100
default: 6
background_temperature:
name: PARAMETER - Frost setting
description: The temperature to be used when there is no calendar event or the room is unoccupied
selector:
number:
min: 0
max: 100
default: 10
warmup_period:
name: PARAMETER - Warmup period
description: The period of time from the start of a new event for which room unoccupancy will be ignored
selector:
time:
default: "02:00:00"
manual_override_period:
name: PARAMETER - Manual override period
description: The time period for which a manual intervention will override the schedule
selector:
time:
default: "02:00:00"
door_or_window_open_period:
name: PARAMETER - Door or window open period
description: The time period for which a door or window may be open before the heating is turned off
selector:
time:
default: "0:03:00"
unoccupancy_period:
name: PARAMETER - Unoccupancy period
description: The time period for which the room may be unoccupied before the heating is turned off
selector:
time:
default: "01:00:00"
mode: queued # use all triggers but avoid conflicting states
## ----------------------------------------------------------------------------
## LOCAL VARÄ°ABLES
## needed to capture global variable values for use in templates
## ----------------------------------------------------------------------------
variables:
local_thermostat_set_temperature_sensors: !input thermostat_set_temperature_sensors
local_door_or_window_open_sensors: !input door_or_window_open_sensors
local_required_temperature: !input required_temperature
local_manual_temperature: !input manual_temperature
local_set_temperature: !input set_temperature
local_manual_override_timer: !input manual_override_timer
local_maximum_thermostat_temperature: !input maximum_thermostat_temperature
local_minimum_thermostat_temperature: !input minimum_thermostat_temperature
local_room_calendar: !input room_calendar
local_room_occupancy_sensors: !input room_occupancy_sensors
## ----------------------------------------------------------------------------
## TRIGGERS
## ----------------------------------------------------------------------------
trigger:
# 1. Calendar state to on (active event)
- platform: state
entity_id: !input room_calendar
to: "on"
id: calendar_state_to_on
# 2. Calendar state to off, unknown, or unavailable (no active event)
- platform: state
entity_id: !input room_calendar
from: "on"
id: calendar_state_to_off
# 3. Calendar event start
- platform: calendar
event: start
offset: "0:0:0"
entity_id: !input room_calendar
id: calendar_event_start
# 4. Calendar event end
- platform: calendar
event: end
offset: "0:0:0"
entity_id: !input room_calendar
id: calendar_event_end
# 5. Change in any one of the thermostat set temperatures
- platform: state
entity_id: !input thermostat_set_temperature_sensors
for:
seconds: 5
id: set_temperature_change
# 6. End of manual override (timer idle, paused, unknown, or unavailable)
- platform: state
entity_id: !input manual_override_timer
from: active
id: manual_override_end
# 7. room becomes unoccupied: sensor available and off (clear)
- platform: state
entity_id: !input room_occupancy_sensors
to: "off"
for:
seconds: 10
id: room_unoccupied
# 8. room becomes occupied: sensor 'detected' (on), 'unknown', or 'unavailable'
- platform: state
entity_id: !input room_occupancy_sensors
from: "off"
for:
seconds: 10
id: room_occupied
# 9. A door or window is opened
- platform: state
entity_id: !input door_or_window_open_sensors
to: "on"
for:
seconds: 10
id: door_or_window_opened
# 10. A doors or windows is closed, or becomes 'unknown' or 'unavailable'
- platform: state
entity_id: !input door_or_window_open_sensors
from: "on"
for:
seconds: 10
id: doors_and_windows_closed
# 11. Door or window open timer finished (time to turn off the heating)
- platform: state
entity_id: !input door_or_window_open_timer
from: active
id: door_or_window_open_timer_end
# 12. Unoccupancy timer finished (time to turn off the heating)
- platform: state
entity_id: !input unoccupancy_timer
from: active
id: unoccupancy_timer_end
# 13. Away switch change
- platform: state
entity_id: !input away_switch
# 14. end of warmup period
- platform: state
entity_id: !input warmup_timer
from: active
id: warmup_timer_end
## ----------------------------------------------------------------------------
## ACTION[0] -- RESPOND TO TRIGGERS
## ----------------------------------------------------------------------------
action:
# Each choice responds to a specific trigger (where a response is needed))
- choose:
#
# ACTION[0]CHOOSE[0] Manual override start
#
- conditions:
- condition: trigger
id: set_temperature_change
#ignore if it is just an echo from a setting from this automation
- condition: state
entity_id: !input echoblock_timer
state: idle
#ignore if same value as before (e.g. triggered by template reload)
- condition: template
value_template: "{{ not states(local_set_temperature) == states(local_required_temperature) }}"
sequence:
- service: timer.start
data:
duration: !input manual_override_period
target:
entity_id: !input manual_override_timer
- service: input_number.set_value
target:
entity_id: !input manual_temperature
data:
value: "{{ states(local_thermostat_set_temperature) }}"
#
# b. Door or window opened - start timer
#
- conditions:
- condition: trigger
id: door_or_window_opened
# Do not restart timer if already running
- condition: not
conditions:
- condition: state
entity_id: !input door_or_window_open_timer
state: active
sequence:
- service: timer.start
data:
duration: !input door_or_window_open_period
target:
entity_id: !input door_or_window_open_timer
#
# c. Doors and windows closed - restart and pause timer
#
- conditions:
- condition: trigger
id: doors_and_windows_closed
sequence:
- service: timer.start
data:
duration: !input door_or_window_open_period
target:
entity_id: !input door_or_window_open_timer
- service: timer.pause # Keeps it in the active state
target:
entity_id: !input door_or_window_open_timer
#
# d. Unoccupancy - start timer
#
- conditions:
- condition: trigger
id: room_unoccupied
# Do not restart timer if already running
- condition: not
conditions:
- condition: state
entity_id: !input unoccupancy_timer
state: active
sequence:
- service: timer.start
data:
duration: !input unoccupancy_period
target:
entity_id: !input unoccupancy_timer
#
# e. room occupied - restart and pause timer
#
- conditions:
- condition: trigger
id: room_occupied
sequence:
- service: timer.start
data:
duration: !input unoccupancy_period
target:
entity_id: !input unoccupancy_timer
- service: timer.pause # Keeps it in the active state
target:
entity_id: !input unoccupancy_timer
## ----------------------------------------------------------------------------
## ACTION[1] -- DETERMINE THE REQUIRED TEMPERATURE ACCORDNG TO THE STATE
## ----------------------------------------------------------------------------
## Each choice sets the required temperature and the reason text, for later use
## The states are tested in order of their priority over other states
- choose:
# ACTION[1].CHOOSE[0]. If 'away' switch is on
- conditions:
- condition: state
entity_id: !input away_switch
state: "on"
sequence:
- service: input_number.set_value
data:
value: !input away_temperature
target:
entity_id: !input required_temperature
- service: input_text.set_value
data:
value: "Set to away temperature because away mode is turned on"
target:
entity_id: !input setting_reason
# ACTION[1].CHOOSE[1]. If a door or window is open
- conditions:
- condition: state
entity_id: !input door_or_window_open_timer
state: idle
- condition: template
value_template: "{{ local_door_or_window_open_sensors | select ('is_state', 'on') | list | count > 0 }}"
sequence:
- service: input_number.set_value
data:
value: !input frost_setting
target:
entity_id: !input required_temperature
- service: input_text.set_value
data:
value: "Turned off because a door or window is open"
target:
entity_id: !input setting_reason
# ACTION[1].CHOOSE[2]. If the room has been unoccupied for the set period, and we are not in a warmup period ***
- conditions:
- condition: state
entity_id: !input unoccupancy_timer
state: idle
- condition: state
entity_id: !input warmup_timer
state: idle
- condition: template
value_template: "{{ local_room_occupancy_sensors | reject ('is_state', [ 'unknown', 'unavailable' ] ) | select ('is_state', 'on') | list | count > 0 }}"
sequence:
- service: input_number.set_value
data:
value: !input background_temperature
target:
entity_id: !input required_temperature
- service: input_text.set_value
data:
value: "Set to background temperature because the room is unoccupied"
target:
entity_id: !input setting_reason
# ACTION[1].CHOOSE[3]. If there is a manual override in operation
- conditions:
- condition: state
entity_id: !input manual_override_timer
state: active
sequence:
- service: input_number.set_value
data:
value: !input manual_temperature
target:
entity_id: !input required_temperature
- service: input_text.set_value
data:
value: "Set manually to {{ states(local_manual_temperature) }}"
## {{ Set manually to {{ states(local_manual_temperature) }}. Time left {{ as_timestamp(state_attr(local_manual_override_timer,'finishes_at')) - as_timestamp(now())) | timestamp_custom('%H:%M', False, 0) }} }}
target:
entity_id: !input setting_reason
# ACTION[1].CHOOSE[4]. If there is an active calendar event
- conditions:
- condition: state
entity_id: !input room_calendar
state: "on"
- choose:
# ACTION[1].CHOOSE[4].CONDITION[0]. If there is no temperature field
- conditions:
- condition: template
value_template: >-
{{ not state_attr(local_room_calendar, 'description').split('#') | count >= 3 }}
sequence:
- service: input_number.set_value
data:
value: !input background_temperature
target:
entity_id: !input required_temperature
- service: input_text.set_value
data:
value: >-
{{ "Set to background temperature because the calendar event '" + state_attr(local_room_calendar, 'message') + "' does not specify a temperature." }}
target:
entity_id: !input setting_reason
# ACTION[1].CHOOSE[4].CONDITION[1]. If temperature field is not a number
- conditions:
- condition: template
value_template: >-
{{ not state_attr(local_room_calendar, 'description').split('#')[1] | is_number }}
sequence:
- service: input_number.set_value
data:
value: !input background_temperature
target:
entity_id: !input required_temperature
- service: input_text.set_value
data:
value: >-
{{ "Set to background temperature because the calendar event '" + state_attr(local_room_calendar, 'message') + "' does not specify a valid number for the temperature." }}
target:
entity_id: !input setting_reason
# ACTION[1].CHOOSE[4].CONDITION[2].If specified temperature is too low
- conditions:
- condition: template
value_template: >-
{{ not state_attr(local_room_calendar, 'description').split('#')[1] | float(0)) >= local_minimum_thermostat_temperature }}
sequence:
- service: input_number.set_value
data:
value: !input background_temperature
target:
entity_id: !input required_temperature
- service: input_text.set_value
data:
value: >-
{{ "Set to background temperature because the calendar event '" + state_attr(local_room_calendar, 'message') + "' specifies a temperature below the minimum." }}
target:
entity_id: !input setting_reason
# ACTION[1].CHOOSE[4].CONDITION[3].If specified temperature is too high
- conditions:
- condition: template
value_template: >-
{{ not ( state_attr(local_room_calendar, 'description').split('#')[1] | float(0)) <= local_maximum_thermostat_temperature }}
sequence:
- service: input_number.set_value
data:
value: !input background_temperature
target:
entity_id: !input required_temperature
- service: input_text.set_value
data:
value: >-
{{ "Set to background temperature because the calendar event '" + state_attr(local_room_calendar, 'message') + "' specifies a temperature above the maximum." }}
target:
entity_id: !input setting_reason
# ACTION[1].CHOOSE[4].DEFAULT. Else, the normal case, there is an active calendar event with a valid temperature
default:
sequence:
- service: input_number.set_value
data:
value: "{{ state_attr(local_room_calendar, 'description').split('#')[1] | float(0) }}"
target:
entity_id: !input required_temperature
- service: input_text.set_value
data:
value: >-
{{ "Set to " + states(local_required_temperature) + " by calendar event '" + state_attr(local_room_calendar, 'message') + "' until " + ( as_timestamp(state_attr(local_room_calendar, 'end_time')) ) | timestamp_custom('%a %d %b %Y at %H:%M') + "." }}
target:
entity_id: !input setting_reason
# ACTION[1].CHOOSE[5]. If there is no active calendar event
- conditions:
- condition: state
entity_id: !input room_calendar
state: "off"
sequence:
- service: input_number.set_value
data:
value: !input background_temperature
target:
entity_id: !input required_temperature
- service: input_text.set_value
data:
value: >-
{{ "Set to background temperature because nothing is scheduled."}}
{% if state_attr(local_room_calendar, 'message') %}
{{ "The next event is '" + state_attr(local_room_calendar, 'message') + "' " + ( as_timestamp(state_attr(local_room_calendar, 'start_time')) ) | timestamp_custom('on %a %d %b %Y at %H:%M', false)}}
{% else %}
{{ "There are no future events." }}
{% endif %}
target:
entity_id: !input setting_reason
#ACTION[1].CHOOSE[6]. If the calendar state becomes unknown
- conditions:
- condition: state
entity_id: !input room_calendar
state: unknown
sequence:
- service: input_number.set_value
data:
value: !input background_temperature
target:
entity_id: !input required_temperature
- service: input_text.set_value
data:
value: Set to background temperature because the calendar state is unknown
target:
entity_id: !input setting_reason
#ACTION[1].CHOOSE[7]. If the calendar becomes unavailable
- conditions:
- condition: state
entity_id: !input room_calendar
state: unavailable
sequence:
- service: input_number.set_value
data:
value: !input background_temperature
target:
entity_id: !input required_temperature
- service: input_text.set_value
data:
value: Set to background temperature because the calendar is unavailable
target:
entity_id: !input setting_reason
# ACTION[1].Default - should never happen!
default:
- service: input_number.set_value
data:
value: !input frost_setting
target:
entity_id: !input required_temperature
- service: input_text.set_value
data:
value: Turned off by because room state canot be determined (program error)
target:
entity_id: !input setting_reason
## ----------------------------------------------------------------------------
## ACTION[2] -- COMMUNICATE WITH THE TRV(s)
## ----------------------------------------------------------------------------
# Check not already set
- if:
- condition: template
value_template: >-
{{ states(local_required_temperature) <> states(local_set_temperature) }}
then:
# Send the command
- service: climate.set_temperature
data:
temperature: local_required_temperature
target:
entity_id: !input thermostat_controls
# Start the echoblock timer
- service: timer.start
data:
duration:
seconds: 10
target:
entity_id: !input echoblock_timer
# Wait 10 seconds for command to be received and reflected back
- delay: "00:00:10"
# Check TRV(s) responded
# Need a loop to test each individually?
- if:
- condition: template
value_template: >-
{{ states(local_required_temperature) | float(0) <> states(local_set_temperature) | float(0) }}
then:
- service: input_text.set_value
data:
value: "Turned off because a door or window is open"
target:
entity_id: !input setting_reason
- service: notify.notify
data:
title: "Heating X error"
message: "{{'TRV' states(local_thermostat_set_temperature_sensors) 'is not responding'}}"
If my numbering is correct, the error is in the template of the code
- condition: template
value_template: >-
{{ not state_attr(local_room_calendar, 'description').split('#')[1] | is_number }}
This code previously worked, and still works in the developer tools template test area:
{% set local_room_calendar = 'calendar.zzz_test_calendar' %}
{{ state_attr(local_room_calendar, 'description') }}
{{ state_attr(local_room_calendar, 'description').split('#')[1] }}
{{ not state_attr(local_room_calendar, 'description').split('#')[1] | is_number }}
… gives the correct answers
Temperature #26.5#
26.5
False
So what’s the problem??