New blueprint -- `Message malformed: Unexpected value for condition: 'None'

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??

I believe that the problem is that “choose:” is not a valid condition.

in the second action (the second “choose” block) in the 5th “choose” “conditions” the second condition is an extra “choose” (I’ve annotated the issue with an arrow to the right):

      - conditions:
          - condition: state
            entity_id: !input room_calendar
            state: "on"
          - choose:  ## <---- this isn't a valid condition
              # 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:
                   .
                   .

if you want to make a selection in the condition you will probably need to modify the logic or use a template condition in place of the choose in that location.

1 Like

Thanks for spotting that. That choose is supposed to be an embedded action after the condition, so there is a sequence: key missing.

HOWEVER, that sadly does not fix the problem I reported! Since the error message mentions the value for the condition[0] it seems to me to be in the template?

I don’t believe so.

you will notice that the error says:

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]

it’s saying that it expected a value of “and, device, not, numeric_state, or, state, sun, template, time, trigger, zone” (which are all the only valid options for a condition).

if you are still getting an error then post the new error along with the corrected blueprint because if the error is now referencing condition[0] that’s not the same as the original error which was referencing condition[1].

I seem to have got past this now, but it was by trial and error so I cannot say exactly how. When I fixed the error you spotted, I got a new error message, which was about the template syntax (improperly paired quotes or bracket), and I could then fix it.

It would appear that the missing sequence key confused the error reporting somehow.