Using Templates to play different Sonos streams at different times of day

I am trying to get HA to play differing Sonos radio streams depending on what time of day it is. I want to use Templates but this is the first time I have tried using them in anger.

Here is the automation (I still need to add variables depending on what time of day it is!). When someone comes home, when the Sonos in the living room isnt playing and the TV is off and between 09:01 and 18:30 run the script with the template variable:

alias: 'Family home play Sonos depending on time of day'
trigger:
  - platform: state
    entity_id: group.family
    from: 'not_home'
    to: 'home'
condition:
  condition: and
  conditions:
    - condition: state
      entity_id: media_player.living_room
      state: 'paused'
    - condition: state
      entity_id: media_player.samsung_tv
      state: 'off'
    - condition: time
      after: '09:01:00'
      before: '18:30:00'
      weekday:
        - mon
        - tue
        - wed
        - thu
        - fri
action:
  service: script.turn_on
  entity_id: script.sonos_template
  data_template:
    variables: >-
      {set sonos = "Absolute Radio"}
      source: {{ sonos }}

This then calls the following script, which groups all my Sonos, sets the volume and plays the favourite stream I defined in the automation:

alias: "Play <template> Radio"
sequence:
  - service: media_player.sonos_group_players
    data:
      entity_id: media_player.living_room
  - service: media_player.volume_set
    data:
      # entity_id: media_player.living_room
      entity_id: group.sonos
      volume_level: "0.13"
  - service: media_player.select_source
    data:
      entity_id: media_player.living_room
      source: '{{ sonos }}'

I am doing something wrong as nothing happens and I get this error in the log

homeassistant.core: Invalid service data for script.turn_on: expected dict for dictionary value @ data['variables']. Got '{set sonos = "Absolute Radio"} source:'

Do I need to define this variable somewhere? I know the trigger and condiitons are all working, but I am not understanding the use of templates here. Can anyone help? Thanks

data_template should look something like this:

data_template:
  source: 'Absolute Radio'

(not sure that you need the data_template in your example since this is a static reference/string)

the script would then reference that parameter with something like

  - service: media_player.select_source
    data:
      entity_id: media_player.living_room
      source: '{{ source }}'

Unfortunately this doesn’t do anything either and logs the error:

homeassistant.core: Invalid service data for script.turn_on: extra keys not allowed @ data['source']. Got 'Absolute Radio'

Try this:

action:
  service: script.turn_on
  entity_id: script.sonos_template
  data:
    variables:
      source: 'Absolute Radio'

Make sure the entity id for the script is correct, also.

Thank you for your help.

OK so it fires script.sonos_template and groups the sonos and sets the volume but doesnt play anything and I get database errors in the log?? Not sure if they are related.

sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) database is locked [SQL: 'SELECT recorder_runs.run_id AS recorder_runs_run_id, recorder_runs.start AS recorder_runs_start, recorder_runs."end" AS recorder_runs_end, recorder_runs.closed_incorrect AS recorder_runs_closed_incorrect, recorder_runs.created AS recorder_runs_created \nFROM recorder_runs \nWHERE recorder_runs.start < ? AND recorder_runs."end" > ?\n LIMIT ? OFFSET ?'] [parameters: ('2017-01-11 20:13:14.115157', '2017-01-11 20:13:14.115157', 1, 0)]
17-01-12 20:13:35 homeassistant.components.recorder: Error during query: (sqlite3.OperationalError) database is locked
17-01-12 20:13:40 homeassistant.components.recorder: Error during query: (sqlite3.OperationalError) database is locked

Can you repost your script and automation?

Here is my automation:

alias: 'Family home play Sonos depending on time of day'
trigger:
  - platform: state
    entity_id: group.family
    from: 'not_home'
    to: 'home'
condition:
  condition: and
  conditions:
    - condition: state
      entity_id: media_player.living_room
      state: 'paused'
    - condition: state
      entity_id: media_player.samsung_tv
      state: 'off'
    - condition: time
      after: '09:01:00'
      before: '18:30:00'
      weekday:
        - mon
        - tue
        - wed
        - thu
        - fri
action:
  service: script.turn_on
  entity_id: script.sonos_template
  data:
    variables:
      source: 'Absolute Radio'

Script:

alias: "Play Absolute Radio"
sequence:
  - service: media_player.sonos_group_players
    data:
      entity_id: media_player.living_room
  - service: media_player.volume_set
    data:
      # entity_id: media_player.living_room
      entity_id: group.sonos
      volume_level: "0.13"
  - service: media_player.select_source
    data:
      entity_id: media_player.living_room
      source: '{{ source }}'

for the script, above alias does you name it: sonos_template like you are using in the automation?

Like so:

sonos_template:    
  alias: "Play Absolute Radio"
  sequence:
    - service: media_player.sonos_group_players
      data:
        entity_id: media_player.living_room
    - service: media_player.volume_set
      data:
        # entity_id: media_player.living_room
         entity_id: group.sonos
         volume_level: "0.13"
    - service: media_player.select_source
      data:
        entity_id: media_player.living_room
        source: '{{ source }}'

I use seperate files and use an include in my scripts.yaml:

sonos_template: !include scripts/sonos_template.yaml

You are referring to it as sonos_template in the automation so you need to name it that or find out what the name is (it is most likely play_absolute_radio) by going to the states page ( < > in the side bar) and finding the script in the list. You’ll want to refer to it as it appears there. If you add the line above alias you can name it specifically.

Surely the reference in my automation:

entity_id: script.sonos_template

Refers to the file included in the scripts.yaml?

sonos_template: !include scripts/sonos_template.yaml

It definitely calls it OK as the Sonos speakers are gropuped and the volume is set. It just doesn’t do the last piece, playing the stream.

ah, that’s fair. A difference between my setup and something I did not have experience with. for reference I have a .yaml called sonos_tts.yaml but inside is:

   sonos_say:
     alias: SONOS TTS
     sequence:
       - service: media_player.sonos_snapshot
         data_template:
           entity_id: "{{ where }}"
       - service: tts.google_say
         data_template:
           entity_id: "{{ where }}"
           message: "{{ what }}"
       - service: media_player.sonos_restore
         data_template:
           entity_id: "{{ where }}"

To call that script, I use script.sonos_say.

another piece that may be missing from your script, since in the data section we are using a template to include a variable in the ‘section’ you will need to adjust it to use a data_template:

sonos_template:    
  alias: "Play Absolute Radio"
  sequence:
    - service: media_player.sonos_group_players
      data:
        entity_id: media_player.living_room
    - service: media_player.volume_set
      data:
        # entity_id: media_player.living_room
         entity_id: group.sonos
         volume_level: "0.13"
    - service: media_player.select_source
      data_template:
        entity_id: media_player.living_room
        source: '{{ source }}'

OK brilliant that worked! Thank you.

Now I would like to play different streams depending in the time of day. I already have the following sensor script that I copied from elsewhere:

  - platform: template
    sensors:
      time_of_day:
        friendly_name: 'Time of Day'
        value_template: >-
          {% if states.sun.sun.state == 'above_horizon' and as_timestamp(now()) < as_timestamp(states.sensor.date.state ~ ' 12:00:00') %}
            morning
          {% elif states.sun.sun.state == 'above_horizon' and as_timestamp(now()) >= as_timestamp(states.sensor.date.state ~ ' 12:00:00') %}
            afternoon
          {% elif states.sun.sun.state == 'below_horizon' and states.sensor.date.state == states.sun.sun.attributes.next_rising | truncate(10, True, '') %}
            twilight
          {% elif states.sun.sun.state == 'below_horizon' and as_timestamp(now()) < as_timestamp(states.sensor.date.state ~ ' 16:00:00') %}
            early_evening
          {% elif states.sun.sun.state == 'below_horizon' and as_timestamp(now()) < as_timestamp(states.sensor.date.state ~ ' 19:00:00') %}
            evening
          {% elif states.sun.sun.state == 'below_horizon' and as_timestamp(now()) >= as_timestamp(states.sensor.date.state ~ ' 21:00:00') %}
            late_evening
          {% endif %}

I would like to factor this sensor into the playing of a stream. So I changed the action statement in the automation to play the stream only during the ‘morning’ or ‘afternoon’. Unfortunately this doesn’t work but does not error out. Any idea what I am doing wrong with my template syntax?

action:
  service: script.turn_on
  entity_id: script.sonos_template
  data:
    variables:
      data_template:
        entity_id: >
          {% if is_state('sensor.time_of_day', 'morning') or is_state('sensor.time_of_day', 'afternoon') %}
            source: 'Absolute Radio'
          {% endif %}

you data_template has entity_id: and also source. I think it needs to look like this:

action:
  service: script.turn_on
  entity_id: script.sonos_template
  data_template:
    variables:
        source: >
          {% if is_state('sensor.time_of_day', 'morning') or is_state('sensor.time_of_day', 'afternoon') %}
            'Absolute Radio'
          {% endif %}

in your prior test/example, you included an entity_id but you set it statically in the script so it wasn’t necessary. If you wanted the automation to also set which media_player you want to use, it would look like:

action:
  service: script.turn_on
  entity_id: script.sonos_template
  data_template:
    variables:
        entity_id: {{ LOGIC TO SET ENTITY ID - you can then reference this inside the script }}
        source: >
          {% if is_state('sensor.time_of_day', 'morning') or is_state('sensor.time_of_day', 'afternoon') %}
            'Absolute Radio'
          {% endif %}

Thanks for your continued help. The stream still doesn’t fire. It definitely calls the script as the speakers group and volume sets but nothing else happens. Really odd.

If you plug the template into the dev-template page in HA, does it return ‘Absolute Radio’?

Yep, everything looks fine. Wonder if its a bug?

Try removing the variables line.

action:
  service: script.turn_on
  entity_id: script.sonos_template
  data_template:
        source: >
          {% if is_state('sensor.time_of_day', 'morning') or is_state('sensor.time_of_day', 'afternoon') %}
            'Absolute Radio'
          {% endif %}

I’m fairly new to templating and HA in general, so thanks for bearing with me! :slight_smile:

Yeah already tried that. I think this might be a bug as hass --script check_config doesnt report any issues. I think it simply doesn’t pass the data_template over to the script.

If I change it to the following (have bypassed the ‘time of day’ sensor for now):

{%- if now().strftime('%H')|int <16%}
    script.sonos_radio_absolute
{%- elif now().strftime('%H')|int <19%}
    script.sonos_radio_radiox
{%- elif now().strftime('%H')|int >19%}
    script.sonos_playlist_anything
{%- else -%}
{%- endif %}

So at certain times of day it calls scripts direct - it works!

This is not ideal as it means having multiple scripts doing more or less the same thing. Want to be more efficient with the scripts.

Hmm, interesting. I’ll have to spend some time playing with it. I don’t currently have an automation using a data_template when calling a script.

What about moving the source logic to the script itself?