Use wakeword off after speaking?

Hello, so I’ve been trying to turn off my wakeword/ use no wakeword after Voice assistant replies and have had no luck. I’m not sure if I’m just not using the right commands or if it’s even possible at this current time. I’m running H.A. 2024.11.2 on a raspberry pi 4 and off loaded Whisper, Piper, and Ollama3.2 to my server. I am using wyoming for future plans to integrate more satellites around my house. Essentially i’m trying to have a continues conversation without using the ake word every time, I originally started by trying to make an automation that was similar to the tapaction to start the mic.

entity: assist_satellite.assist_microphone
tap_action:
  action: assist
  pipeline_id: preferred
  start_listening: true

But I cant seem to to get that to work, I spent probably about 6 days now trying different things, chat gpt has been meh and been researching like crazy, I can’t find anything that seems to work. Here are a few of the “Solutions” That I have found but none seem to have the command to just restart the pipeline/ reactivate Voice assistant.

ww.home-assistant. io/ voice_control/ voice_remote_local_assistant/ - helpful till it wasn’t, is there a page just for commands? services? hidden services or actions? I found a few (turning off services n such) while scripting in configuration.yaml over the gui that seems to be restrictive for scripts and automations. I honestly don’t know how many different commands Ive tried and different approaches and the best I got was to shutting services off, like whisper or the mic and any documentation I find seems to be out pf date or for an esp32

Here’s one of the many different attempts I’ve made, any help would be greatly appreciated.

continuous_conversation:
  alias: Continuous Conversation
  description: Continuous conversation without requiring repeated wake words.
  mode: restart
  sequence:
    - alias: Start Session
      service: conversation.process
      data:
        text: "How can I assist you today?"

    - alias: Continuous Response Loop
      repeat:
        until:
          - condition: state
            entity_id: input_boolean.continuous_conversation
            state: "off"
        sequence:
          - wait_for_trigger:
              - platform: event
                event_type: intent_received
              - platform: state
                entity_id: input_boolean.continuous_conversation
                to: "off"
          - choose:
              - conditions:
                  - condition: template
                    value_template: >
                      {{ trigger.event.event_type == 'intent_received' }}
                sequence:
                  - service: conversation.process
                    data_template:
                      text: >
                        {{ trigger.event.data.text }}
                  - delay:
                      seconds: 1
                  - service: assist_pipeline.assist
                    data:
                      entity_id: assist_satellite.assist_microphone
                      pipeline_id: preferred
                      start_listening: false  # Explicitly stop listening first
                  - delay:
                      seconds: 0.5  # Short pause to ensure proper reset
                  - service: assist_pipeline.assist
                    data:
                      entity_id: assist_satellite.assist_microphone
                      pipeline_id: preferred
                      start_listening: true  # Restart listening
              - conditions:
                  - condition: state
                    entity_id: input_boolean.continuous_conversation
                    state: "off"
                sequence:
                  - service: conversation.process
                    data:
                      text: "Goodbye! Have a great day!"

It should work by starting the conversation
Wakeword Asks question
H.A. replies
H.A. reactivates as if the wakeword was spoken again
I ask question
H.A. replies
H.A. reactivates, doesn’t hear anything as if false activation

Here is another attempt, I was debating on downloading openWakeWords source code and seeing if I could modify that some how to add in a function to “use_wakeword = false/true” or something…

Configuration.yaml

script: !include script.yaml
input_boolean: !include input_boolean.yaml
automation: !include automation.yaml

script.yaml

alias: Continuous Conversation
description: Keeps the conversation open for follow-up intents.
mode: restart
variables:
  session_active: true
  timeout: 30  # Time in seconds before the session ends due to inactivity.
sequence:
  - alias: Start Session
    service: conversation.process
    data:
      text: "How can I assist you today?"

  - alias: Wait for Follow-Up
    repeat:
      until:
        - condition: state
          entity_id: input_boolean.continuous_conversation
          state: "off"
      sequence:
        - wait_for_trigger:
            - platform: event
              event_type: intent_received
            - platform: state
              entity_id: input_boolean.continuous_conversation
              to: "off"
        - choose:
            - conditions:
                - condition: template
                  value_template: >
                    {{ trigger.event.event_type == 'intent_received' }}
              sequence:
                - service: conversation.process
                  data_template:
                    text: >
                      {{ trigger.event.data.text }}
                - service: input_boolean.turn_on
                  target:
                    entity_id: input_boolean.continuous_conversation
                - delay: "{{ timeout }}"
                - service: input_boolean.turn_off
                  target:
                    entity_id: input_boolean.continuous_conversation
            - conditions:
                - condition: state
                  entity_id: input_boolean.continuous_conversation
                  state: "off"
              sequence:
                - service: conversation.process
                  data:
                    text: "Goodbye! Have a great day!"

input_boolean.yaml

input_boolean:
  continuous_conversation:
    name: Continuous Conversation
    initial: false

automation.yaml

automation:
  - alias: Start Continuous Conversation
    trigger:
      - platform: state
        entity_id: assist_satellite.assist_microphone
        from: "idle"
        to: "listening"
    action:
      - service: input_boolean.turn_on
        entity_id: input_boolean.continuous_conversation
      - service: script.continuous_conversation

Someone’s gunna reply with what I’m looking for and I’m gunna feel so stupid. :rofl:

I found this

And if I run and listen to events in dev tools I get

event_type: homeassistant.start_pipeline
data: {}
origin: REMOTE
time_fired: "2024-11-24T19:30:02.505854+00:00"
context:
  id: 01JDFTE249KXXSKXAYK1VZ401B
  parent_id: null
  user_id: 851700ad76af4411b6a362a56f038fdb

or

event_type: assist.pipeline_run
data: {}
origin: REMOTE
time_fired: "2024-11-24T19:31:04.394275+00:00"
context:
  id: 01JDFTFYJAT8AE9WSA39DHJEVE
  parent_id: null
  user_id: 851700ad76af4411b6a362a56f038fdb

But I get this error

The automation “Restart Voice Pipeline After Speaking” (automation.restart_voice_pipeline_after_speaking ) has an unknown action: assist.pipeline_run .

When I run this automation

- alias: Restart Voice Pipeline After Speaking
  description: "Automatically restart the voice pipeline after the assistant finishes speaking."
  trigger:
    - platform: event
      event_type: tts-end
      event_data:
        engine: tts.piper_2
  condition:
    - condition: state
      entity_id: assist_satellite.assist_microphone
      state: listening
  action:
    - service: assist.pipeline_run
      data:
        pipeline: "01JDFTE249KXXSKXAYK1VZ401B" 
        start_stage: "intent"  
        input: "Listening for your next request..."

Any ideas why I can’t use the events in script but I can fire them?

I started looking into pipeline.py in the source code (core/homeassistant/components/assist_pipeline/pipeline.py) and I’m taking an interest in lines 1404 - 1489. I feel like this is a good place to inject a follow-up process to repeat from the stt stage after the wake word has been confirmed.

I have modified pipeline.py, manifest n such so it’s named assist_pipeline_follow_up. I then added the folder to custom components to try and get H.A. to use that pipeline instead but I can’t get it to replace Assist_pipeline. Any ideas?

LOL Right when I post this I got it to work, Had to power off the raspberry pi to hard reset H.A.
Unfortunately the mod didn’t work, but that’s ok I think I’m on the right track and can test now without reinstalling H.A.

Captain’s log day 10,

I’ve spent countless hours attempting to replace wyoming with a modified version leaving me to modify assist_satellite as well, I figured things where going good when I couldn’t get assist_microphone to activate, I was mislead. This just turned out to be what I was trying to avoid, The default integration of assist_pipeline with no way to edit or remove it, I figured I’d play it smart by finding a way to disable this hidden foe. After many attempts and following the instructions described within the H.A. main frame, I have come up empty handed. Not with knowledge, but with results. The commands of “assist_pipeline: false” in the realm of configuration.yaml does not work leaving me to once again start from the beginning of a new way to approach this hidden gem of continuous conversation.

I shall start by acquiring a version without such integration’s hopefully allowing me to have full control of my modifications. I don’t know but I may have already achieved continuous conversation without knowing, only time will give the answers.

I think I have something like this close to working. I’m still working out some kinks, but this is essentially continued conversation. I’m using micro_wake_word but I suspect something similar could work with wyoming. It reads the state of voice assistant and starts a timer any time it changes from “responding” to any other state (which is basically when it is done talking). If it responds again during before the timer is up, it resets the timer. If the time expires, it runs a script to stop listening and the wakeword goes live again. The next step is to work out some sort of “stop listening” command to make it stop before the timer is up if i want to.

captive_portal:

external_components:
  - source:
      type: git
      url: https://github.com/esphome/voice-kit
      ref: dev

    components:
      - aic3204
      - audio_dac
      - media_player
      - micro_wake_word
      - microphone
      - nabu
      - nabu_microphone
      - voice_assistant
      - voice_kit
    refresh: 0s
  - source: github://pr#7605
    components: [ audio, i2s_audio, speaker]
    refresh: 0s
  - source:
      type: git
      url: https://github.com/formatBCE/Respeaker-Lite-ESPHome-integration
      ref: main
    components: [ respeaker_lite ]
    refresh: 0s

i2s_audio:
  - id: i2s_output
    i2s_lrclk_pin: 
      number: GPIO7
      allow_other_uses: true
    i2s_bclk_pin:  
      number: GPIO8
      allow_other_uses: true
    i2s_mclk_pin:  
      number: GPIO9
      allow_other_uses: true

  - id: i2s_input
    i2s_lrclk_pin:  
      number: GPIO7
      allow_other_uses: true
    i2s_bclk_pin:  
      number: GPIO8
      allow_other_uses: true
    i2s_mclk_pin:  
      number: GPIO9
      allow_other_uses: true

i2c:
  - id: bus_a
    sda: GPIO5
    scl: GPIO6
    scan: true

respeaker_lite:
  id: respeaker
  i2c_id: bus_a
  reset_pin: GPIO2
  mute_state:
    internal: true
    id: mute_state
  firmware_version:
    icon: mdi:application-cog
    name: XMOS firmware version
    internal: false
    id: firmware_version


microphone:
  - platform: nabu_microphone
    id: xiao_mic
    adc_type: external
    i2s_din_pin: GPIO44
    pdm: false
    sample_rate: 16000
    bits_per_sample: 32bit
    i2s_mode: secondary
    i2s_audio_id : i2s_input

    channel_0:
      id: nabu_mic_mww
    channel_1:
      id: nabu_mic_va


speaker:
  - platform: i2s_audio
    id: xiao_speaker
    dac_type: external
    i2s_dout_pin: GPIO43
    i2s_mode: secondary
    sample_rate: 16000
    bits_per_sample: 32bit
    i2s_audio_id: i2s_output
    channel: mono

media_player:
  - platform: nabu
    id: nabu_media_player
    name: Media Player
    internal: false
    speaker: xiao_speaker
    sample_rate: 16000
    volume_increment: 0.05
    volume_min: 0.4
    volume_max: 0.85
    files:
      - id: timer_audio
        file: https://github.com/esphome/firmware/raw/main/voice-assistant/sounds/timer_finished.wav


script:
  - id: start_idle_timer
    then:
      - delay: 15s
      - if:
          condition:
            lambda: 'return id(is_idle);'
          then:
            - voice_assistant.stop
            - logger.log: "Voice Assistant stopped after 30 seconds of inactivity."

  - id: reset_idle_timer
    then:
      - logger.log: "Voice Assistant activity detected. Resetting idle timer."
      - globals.set:
          id: is_idle
          value: "false"
      - script.stop: start_idle_timer

globals:
  - id: is_idle
    type: bool
    initial_value: "false"
  
  - id: previous_va_status
    type: std::string
    initial_value: ""

text_sensor:
  - platform: homeassistant
    id: va_status
    entity_id: assist_satellite.${device_name}_assist_satellite
    name: "VA Status"
    on_value:
      then:
        - lambda: |-
            // Check if the state changes to "responding"
            if (x == "responding" && id(previous_va_status) != "responding") {
              // If state changes to "responding", reset idle timer
              id(reset_idle_timer).execute();
            }
            // Check if the state changes from "responding" to anything else
            else if (id(previous_va_status) == "responding" && x != "responding") {
              // If state changes from "responding" to something else, start idle timer
              id(start_idle_timer).execute();
              id(is_idle) = true;  // Set global variable to true
            }
            // Update the previous state to the current state
            id(previous_va_status) = x;

# just for debugging.  Logs the state of VA Status every 5 seconds
interval:
  - interval: 5s  # Adjust this interval as needed (e.g., every 5 seconds)
    then:
      - lambda: |-
          ESP_LOGD("main", "VA Status: %s", id(va_status).state.c_str());

micro_wake_word:
  vad:
  microphone: nabu_mic_mww
  on_wake_word_detected:
    
    - voice_assistant.start_continuous:
        #wake_word: !lambda return wake_word;
        #silence_detection: true
    - light.turn_on:
        id: led           
        red: 80%
        green: 0%
        blue: 80%
        brightness: 60%
        effect: fast pulse 
    #- homeassistant.service:
     #   service: tts.speak
      #  data:
       #   entity_id: tts.piper
        #  cache: 'true'
         # media_player_entity_id: media_player.respeaker_media_player
          #message: I'm here

  models:
    - model: hey_jarvis

voice_assistant:
  microphone: nabu_mic_va
  noise_suppression_level: 0
  auto_gain: 0dBFS 
  volume_multiplier: 1 
  id: assist
  media_player: nabu_media_player
  #on_stt_end:
       #then: 
         #- light.turn_off: led

  #on_idle:
   #       then:
    #      - delay: 10s
     #     - voice_assistant.stop
            
  #removed the on_error debugging because every time it heard a noise it would trigger an error and reset the wakeword, thus ruining continuous conversation.             
  #on_error:
   #       - micro_wake_word.start:  
  on_end:
        then:
          - light.turn_off: led
          
          - wait_until:
              not:
                voice_assistant.is_running:
          - micro_wake_word.start:
  
# timer functionality 
  on_timer_finished:
    - switch.turn_on: timer_ringing
    - light.turn_on:
        id: led
        effect: "Slow Pulse"
        red: 80%
        green: 0%
        blue: 30%
        brightness: 80%

    - while:
        condition:
          switch.is_on: timer_ringing
        then:
          - nabu.play_local_media_file: timer_audio
          - delay: 2s
    - light.turn_off: led
    - micro_wake_word.start:

switch:
  - platform: template
    id: timer_ringing
    optimistic: true
    internal: False
    name: "Timer Ringing"
    restore_mode: ALWAYS_OFF

light:
  - platform: esp32_rmt_led_strip
    id: led
    name: "Led Light"
    pin: GPIO1
    default_transition_length: 0s
    chipset: ws2812
    num_leds: 1
    rgb_order: grb
    rmt_channel: 0
    effects:
      - pulse:
          name: "Slow Pulse"
          transition_length: 250ms
          update_interval: 250ms
          min_brightness: 50%
          max_brightness: 100%
      - pulse:
          name: "Fast Pulse"
          transition_length: 100ms
          update_interval: 100ms
          min_brightness: 50%
          max_brightness: 100%

I’m rooting for both of you guys!
That’s one thing I would also want to have. I’ve stopped talking to it because I can’t stand having to say Hey Jarvis every phrase.
I hope you guys figure it out!

I am joining today

It seems I have made it.

NEW

globals:
  - id: to_follow_up
    type: bool

UPDATE

on_tts_start:
    - lambda: id(to_follow_up) = true;
on_end:
    - if:
        condition:
          - lambda: |-
              return id(to_follow_up);
        then:
          - lambda: id(assist).set_use_wake_word(false);
        else:
          - lambda: id(assist).set_use_wake_word(true);
    - lambda: id(to_follow_up) = false;