Help me understand why this works in a blueprint but not in my own automation?

Hey all,

I’m doing some automations based around Octopus energy events, using this blueprint as my own template (found here: https://bottlecapdave.github.io/HomeAssistant-OctopusEnergy/blueprints/octopus_energy_octoplus_join_saving_session.yml):

blueprint:
  name: Octopus Energy - Join Saving Session
  description: Automatically joins saving sessions when they become available
  domain: automation
  author: BottlecapDave
  input:
    saving_session_event_entity:
      name: Saving session events
      description: The saving session event entity to join (e.g. event.octopus_energy_{{ACCOUNT_ID}}_octoplus_saving_session_events)
      selector:
        entity:
          filter:
          - domain:
            - event
            integration: octopus_energy
          multiple: false
mode: single
variables:
  saving_session_event_entity: !input saving_session_event_entity
trigger:
- platform: state
  entity_id: !input saving_session_event_entity
condition:
- condition: template
  value_template: "{{ state_attr(saving_session_event_entity, 'available_events') != None and state_attr(saving_session_event_entity, 'available_events') | length > 0 }}"
action:
- service: octopus_energy.join_octoplus_saving_session_event
  data:
    event_code: "{{ state_attr(saving_session_event_entity, 'available_events')[0]['code'] }}"
  target:
    entity_id: !input saving_session_event_entity
- service: persistent_notification.create
  data:
    title: Joined Saving Sessions
    message: >
      {% set event = state_attr(saving_session_event_entity, 'available_events')[0] %}
      {% set event_start = event['start'] %}
      Joined a new Octopus Energy saving session. It starts at {{ event_start.strftime('%H:%M') }} on {{ event_start.day }}/{{ event_start.month }} for {{ event.duration_in_minutes | int }} minutes.

Using that I made my own automation that creates a calendar event and then sends a notification similar to the one in the blueprint:

alias: Create Octopus calendar events
description: ""
triggers:
  - trigger: state
    entity_id:
      - binary_sensor.octopus_energy_a_xxxxxxxx_octoplus_saving_sessions
    attribute: next_joined_event_start
    id: Saving session
  - trigger: state
    entity_id:
      - >-
        binary_sensor.octopus_energy_a_xxxxxxxx_octoplus_free_electricity_session
    attribute: next_event_start
    id: Free electricity
conditions:
  - condition: template
    value_template: "{{ trigger.to_state.attributes[trigger.attribute] is not none }}"
actions:
  - variables:
      start: "{{ trigger.to_state.attributes[trigger.attribute] }}"
      end: |
        {% if trigger.id == 'Saving session' %}
          {{ trigger.to_state.attributes['next_joined_event_end'] }}
        {% else %}
          {{ trigger.to_state.attributes['next_event_end'] }}
        {% endif %}
  - action: calendar.create_event
    metadata: {}
    data:
      summary: "{{ trigger.id }}"
      start_date_time: "{{ start }}"
      end_date_time: "{{ end }}"
    target:
      entity_id: calendar.octoplus_events
  - action: notify.telegram_us
    metadata: {}
    data:
      message: >-
        Joined a new Octopus {{ trigger.id | lower }} event on {{ start.day
        }}/{{ start.month }} from {{ start.strftime('%H:%M') }} till {{ end.strftime('%H:%M') }}.
mode: single

The calendar bit works great, but the notification fails with the error Error rendering data template: UndefinedError: 'str object' has no attribute 'strftime'. I’m using the exact same logic as the blueprint to create my message so I’m struggling to understand why it’s failing?

I tried a bunch of things including defining the time variables separately, but it just moved the error to that section. Eventually I bounced the problem off an AI, it suggested using as_datetime when defining the variables, but that made no difference (later testing shows they’re already datetime objects). Eventually it suggested using as_timestamp instead, which worked as per the below.

So yeah, what I’m hoping to get an understanding of is why this is failing when everything suggests it should work? Would absolutely love any feedback!

Meanwhile, here’s the edit that works:

alias: Create Octopus calendar events
description: ""
triggers:
  - trigger: state
    entity_id:
      - binary_sensor.octopus_energy_a_xxxxxxxx_octoplus_saving_sessions
    attribute: next_joined_event_start
    id: Saving session
  - trigger: state
    entity_id:
      - >-
        binary_sensor.octopus_energy_a_xxxxxxxx_octoplus_free_electricity_session
    attribute: next_event_start
    id: Free electricity
conditions:
  - condition: template
    value_template: "{{ trigger.to_state.attributes[trigger.attribute] is not none }}"
actions:
  - variables:
      start: "{{ trigger.to_state.attributes[trigger.attribute] | as_timestamp }}"
      end: |
        {% if trigger.id == 'Saving session' %}
          {{ trigger.to_state.attributes['next_joined_event_end'] | as_timestamp }}
        {% else %}
          {{ trigger.to_state.attributes['next_event_end'] | as_timestamp }}
        {% endif %}
  - action: calendar.create_event
    metadata: {}
    data:
      summary: "{{ trigger.id }}"
      start_date_time: "{{ start | timestamp_local }}"
      end_date_time: "{{ end | timestamp_local }}"
    target:
      entity_id: calendar.octoplus_events
  - action: notify.telegram_us
    metadata: {}
    data:
      message: >-
        Joined a new Octopus {{ trigger.id | lower }} event on {{
        (start | timestamp_custom('%d/%m')) }} from {{
        (start | timestamp_custom('%H:%M')) }} till {{
        (end | timestamp_custom('%H:%M')) }}.
mode: single

The blueprint works because it gets and uses the datetime object within the same template. While a datetime object can be stored in a Jinja variable, it cannot be stores in a HASS variable – a HASS variable can only store the basic data types like boolean, number, string, list, dict. A datetime object will be converted to a string and a string has no strftime attribute/function.

1 Like

Which you can convert back to a datetime object by using the |as_datetime filter.

1 Like

Boom, thank you! So the blueprint is able to do it because it’s making the entire message into a variable, and processing the data it needs at the point of that variable’s creation. That makes sense!

I’m not great at the syntax when it gets complicated, but you’re saying that I could fix my original automation by replacing {{ start.strftime('%H:%M') }} with… Let me see if I can figure it out…

{{ start | as_datetime.strftime('%H:%M') }} ? That doesn’t look right to me but maybe it is?

Add parentheses:

{{ (start | as_datetime).strftime('%H:%M') }}

1 Like

Thanks. It looks like I’d also need to do that for the .day and .month bits, so now I’m trying to decide whether it’s neater to do it like that or with the timestamp solution!

Just to close out the thread: I used this information to settle on the below solution. Re-setting the variables within the message template, similar to how the blueprint works, keeps the manipulations to a minimum and makes the whole thing easier to read. I think it also makes it somewhat self-explanatory as to why the variables are being re-set.

Thanks everyone for your help and explanations! I’ve learned a lot today.

alias: Create Octopus calendar events
description: ""
triggers:
  - trigger: state
    entity_id:
      - binary_sensor.octopus_energy_a_xxxxxxxx_octoplus_saving_sessions
    attribute: next_joined_event_start
    id: Saving session
  - trigger: state
    entity_id:
      - >-
        binary_sensor.octopus_energy_a_xxxxxxxx_octoplus_free_electricity_session
    attribute: next_event_start
    id: Free electricity
conditions:
  - condition: template
    value_template: "{{ trigger.to_state.attributes[trigger.attribute] is not none }}"
actions:
  - variables:
      start: "{{ trigger.to_state.attributes[trigger.attribute] }}"
      end: |
        {% if trigger.id == 'Saving session' %}
          {{ trigger.to_state.attributes['next_joined_event_end'] }}
        {% else %}
          {{ trigger.to_state.attributes['next_event_end'] }}
        {% endif %}
  - action: calendar.create_event
    metadata: {}
    data:
      summary: "{{ trigger.id }}"
      start_date_time: "{{ start }}"
      end_date_time: "{{ end }}"
    target:
      entity_id: calendar.octoplus_events
  - action: notify.telegram_us
    metadata: {}
    data:
      message: >-
        {% set start = start | as_datetime %}
        {% set end = end | as_datetime %}
        Joined a new Octopus {{ trigger.id | lower }} event on {{ start.day }}/{{ start.month }} from {{ start.strftime('%H:%M') }} till {{ end.strftime('%H:%M') }}.
mode: single