I need some help with looping a RTTTL sound on a speaker

I am trying to get the RTTTL song to loop until the switch is turned off but failed to do so. I’ve tried seeking chatgpt for help but it hasn’t been successful. Can you review what is wrong with the while-then block?

output:
  - platform: ledc
    pin: GPIO25
    id: buzzer_output
  - platform: gpio
    pin: GPIO26
    id: amp_relay
    #inverted: true  # HIGH = siren off, LOW = siren on

rtttl:
  output: buzzer_output


switch:
  - platform: template
    name: "Buzzer"
    id: siren_switch
    turn_on_action:
    - output.turn_on: amp_relay  # supply power to amplifier  
    - while:
        condition:
          switch.is_on: siren_switch
        then:
          - rtttl.play: 'MissionImp:d=16,o=6,b=95:32d,32d#,32d,32d#,32d,32d#,32d,32d#,32d,32d,32d#,32e,32f,32f#,32g,g,8p,g,8p,a#,p,c7,p,g,8p,g,8p,f,p,f#,p,g,8p,g,8p,a#,p,c7,p,g,8p,g,8p,f,p,f#,p,a#,g,2d,32p,a#,g,2c#,32p,a#,g,2c,a#5,8c,2p,32p,a#5,g5,2f#,32p,a#5,g5,2f,32p,a#5,g5,2e,d#,8d'

          # Wait until song finishes
          - wait_until:
              condition:
                not:
                  rtttl.is_playing
            # Optional: short pause before repeating
          - delay: 500ms
  
    turn_off_action:
    - rtttl.stop
    - output.turn_off: amp_relay  # cut power to amplifier

That looks ok to me. What is happening?

Is it only playing once?

The issue is that it is not playing at all. However, when the “while - then” block is removed and replaced with only the rtttl.play action, it plays the song fine.

Ok I’m guessing the switch state does not “turn on” until all the turn on actions complete. So this condition will never be true:

 - platform: template
    name: "Buzzer"
    id: siren_switch
    turn_on_action:
    - output.turn_on: amp_relay  # supply power to amplifier  
    - while:
        condition:
          switch.is_on: siren_switch

You’re going to have to do the looping rtttl action outside the switch turn on actions. Use the on_turn_on block.

switch:
  - platform: template
    name: "Buzzer"
    id: siren_switch
    turn_on_action:
      - output.turn_on: amp_relay  # supply power to amplifier  
    turn_off_action:
      - output.turn_off: amp_relay  # cut power to amplifier
      - rtttl.stop
    on_turn_on:
      - while:
          condition:
            switch.is_on: siren_switch
          then:
            - rtttl.play: 'MissionImp:d=16,o=6,b=95:32d,32d#,32d,32d#,32d,32d#,32d,32d#,32d,32d,32d#,32e,32f,32f#,32g,g,8p,g,8p,a#,p,c7,p,g,8p,g,8p,f,p,f#,p,g,8p,g,8p,a#,p,c7,p,g,8p,g,8p,f,p,f#,p,a#,g,2d,32p,a#,g,2c#,32p,a#,g,2c,a#5,8c,2p,32p,a#5,g5,2f#,32p,a#5,g5,2f,32p,a#5,g5,2e,d#,8d'

            # Wait until song finishes
            - wait_until:
                condition:
                  not:
                    rtttl.is_playing
              # Optional: short pause before repeating
            - delay: 500ms   

There’s still no sound. I, however, tried to use the Repeat function and it seems to work. A delay longer than the duration of the song is required to allow it to play the entire song.

The loop (while-then) function just does not seem to work at all. I tried to put the same 20s delay in the while-then block - also not successful.

output:
  - platform: ledc
    pin: GPIO25
    id: buzzer_output
  - platform: gpio
    pin: GPIO26
    id: amp_relay

rtttl:
  output: buzzer_output


switch:
  - platform: template
    name: "Buzzer"
    id: siren_switch
    turn_on_action:
    - output.turn_on: amp_relay  # supply power to amplifier
    - repeat:
        count: 5
        then:
          - rtttl.play: 'siren:d=8,o=6,b=100:a6,b6,a6,b6,a6,b6,a6,b6,a6,b6,a6,b6,a6,b6,a6,b6,a6,b6,a6,b6,a6,b6,a6,b6,a6,b6,a6,b6,a6,b6,a6,b6,a6,b6,a6,b6,a6,b6,a6,b6,a6,b6,a6,b6,a6,b6,a6,b6,a6,b6,a6,b6,a6,b6,a6,b6,a6,b6,a6,b6,a6,b6,a6,b6,a6,b6,a6,b6,a6,b6,a6,b6,a6,b6,a6,b6,a6,b6,a6,b6,a6,b6,a6,b6,a6,b6,a6,b6,a6,b6,a6,b6,a6,b6,a6,b6'
          - delay: 20s
    turn_off_action:
    - rtttl.stop
    - output.turn_off: amp_relay  # cut power to amplifier

Please report this here: https://github.com/esphome/esphome/issues

I have this similar code which works for me.

  - platform: template
    name: Air Quality 1 Alert
    id: aq_alert
    optimistic: true
    internal: TRUE
    turn_on_action:
    - if:
        condition:
          switch.is_off: aq_mute
        then:
          - rtttl.play:
              rtttl: 'smb:d=4,o=5,b=100:16e6,16e6,32p,8e6,16c6,8e6,8g6,8p,8g,8p.'
        else:
    - delay: 500ms
    - while:
        condition:
          switch.is_on: aq_alert
        then:
          - light.turn_on: yellow_led_aq1_light
          - delay: 1000ms 
          - light.turn_off: yellow_led_aq1_light
          - delay: 1000ms
    turn_off_action:
    - light.turn_off: yellow_led_aq1_light
    - rtttl.stop



I’ve modified your code to loop the RTTTL part but there’s still no sound. In the end, I decided to just use the repeat function.

1 Like

Does it make any difference if you add

optimistic: true?

Yes it works now!

I suspected something is not right when the switch is turned on in HA, the state does not register as ‘on’. Now that ‘optimistic’ is set to ‘true’, the state in HA works and the condition ‘switch.is_on: siren_switch’ is now satisfied.

Edit: Appreciate the advice and responses @tom_l and @Mahko_Mahko

1 Like

It is because you have no lambda configured to determine the state of the switch.

https://esphome.io/components/switch/template/#configuration-variables
Screenshot 2025-10-31 092650

Without this the switch has to work in optimistic mode.

Optimistic mode can be problematic if the switch state can be changed outside of ESPhome.

e.g. a light switch in optimistic mode changed by its remote control rather than esphome would cause the switch state and real state to be out of sync.

In your case it isn’t a problem.