Home assistant stuck on 100% since ~2025.11 when activating voice assistant

Hi there,
Since the 2025.11~ version of home assistant, when I activate voice assistant (either via wake word or manually) from my modified Sonoff TXs with microphone, the homeassistant python process goes up to 100% and remains stuck until I force restart it. This didn’t happen with earlier versions. Rolling back to the October release (without reflashing any of the esphome devices) fixed the issue.
There are no log evidences in Home Assistant. This problem seems to only happen with my custom made TXs. It doesn’t happen with:

  • M5 Stack Atom Echo
  • Voice assistant PE Device
  • Web browser / Mobile application

I’ve attached a stripped down configuration of my Sonoff Txs:

substitutions:
  name: "int-camera-xxxx"
  friendly_name: "Camera xxxx"

  audio_lrclk_pin: GPIO4
  audio_bclk_pin: GPIO2
  audio_sdata_pin: GPIO15
  play_sample_rate: "48"

  use_ip_address: xxx.xxx.x.xxx

  wifi_ssid: !secret wifi_dmz_ssid
  wifi_password: !secret wifi_dmz_password

  mic_lrclk_pin: GPIO1
  mic_bclk_pin: GPIO3
  mic_sdata_pin: GPIO0

  vibra_time: 75ms

  vibra_motor_pin: GPIO21
  pa_power_pin: GPIO26

  touchpanel_power_pin: GPIO5

esphome:
  name: ${name}
  friendly_name: ${friendly_name}
  project:
    name: Sonoff.TxUltimate
    version: "2.0"


esp32:
  board: esp32dev
  flash_size: 8MB
  cpu_frequency: 240MHz
  framework:
    type: esp-idf

#psram:
#  mode: octal
#  speed: 40MHz

logger:
#  hardware_uart: UART2
  level: DEBUG
  baud_rate: 0


api:
#  encryption:
#    key: !secret api_key

ota:
  platform: esphome
  password: !secret ota_pwd

wifi:
  ssid: ${wifi_ssid}
  password: ${wifi_password}
  use_address: ${use_ip_address}
  ap:
    password: !secret ap_pwd

output:
  - platform: gpio
    pin: ${vibra_motor_pin}
    id: vibra_output


speaker:
  - id: audio_out
    platform: i2s_audio
    dac_type: external
    i2s_dout_pin: ${audio_sdata_pin}
    i2s_audio_id: audio_i2s
    i2s_comm_fmt: stand_msb
    timeout: never
    channel: mono
    bits_per_sample: 16bit
    sample_rate: ${play_sample_rate}000
    buffer_duration: 500ms

button:
  - id: vibra
    platform: output
    name: "${friendly_name} Vibration"
    output: vibra_output
    duration: ${vibra_time}
    entity_category: config
  - platform: safe_mode
    name: "${friendly_name} (Safe Mode)"
  - platform: restart
    name: "${friendly_name} Restart"
  - platform: template
    name: Start/Stop assistant
    id: start_assistant
    entity_category: config
    on_press:
      - if:
          condition:
            voice_assistant.is_running
          then:
            - voice_assistant.stop
          else:
            - voice_assistant.start

switch:
  - platform: gpio
    id: pa_power
    pin: ${pa_power_pin}
    name: "PA Power"
    internal: true
    restore_mode: ALWAYS_ON

  - platform: gpio
    name: "touch panel power"
    pin:
      number: ${touchpanel_power_pin}
      inverted: true
    id: touch_power
    internal: true
    restore_mode: RESTORE_DEFAULT_ON

i2s_audio:
  - id: audio_i2s
    i2s_lrclk_pin: ${audio_lrclk_pin}
    i2s_bclk_pin: ${audio_bclk_pin}
  - id: audio_mic_i2s
    i2s_lrclk_pin: ${mic_lrclk_pin}
    i2s_bclk_pin: ${mic_bclk_pin}

microphone:
  - platform: i2s_audio
    i2s_audio_id: audio_mic_i2s
    i2s_din_pin: ${mic_sdata_pin}
    adc_type: external
    id: asr_mic
    pdm: false
    channel: left
    sample_rate: 16000

voice_assistant:
  id: va
  microphone: asr_mic
  speaker: audio_out
#  media_player: va_media_player
  use_wake_word: false
  noise_suppression_level: 4
  auto_gain: 31dBFS
  volume_multiplier: 15.0

#media_player:
#  - platform: speaker
#    id: va_media_player
#    name: Media Player
#    codec_support_enabled: false
#    buffer_size: 6000
##    task_stack_in_psram: false
#    volume_initial: 100%
#    announcement_pipeline:
#      format: WAV
#      speaker: audio_out

(Tried with either media_player and speaker)

This is the atom echo configuration (correctly working with HA) I’m actually using:

substitutions:
  name: m5stack-atom-echo
  friendly_name: M5Stack Atom Echo

esphome:
  name: ${name}
  name_add_mac_suffix: true
  friendly_name: ${friendly_name}
  min_version: 2025.5.0

esp32:
  board: m5stack-atom
  cpu_frequency: 240MHz
  framework:
    type: esp-idf

logger:
api:

ota:
  - platform: esphome
    id: ota_esphome
    password: !secret ota_pwd_sec

wifi:
  networks:
  - ssid: !secret wifi_dmz_ssid
    password: !secret wifi_dmz_password
  use_address: xxx.xxx.x.xxx

button:
  - platform: factory_reset
    id: factory_reset_btn
    name: Factory reset

i2s_audio:
  - id: i2s_audio_bus
    i2s_lrclk_pin: GPIO33
    i2s_bclk_pin: GPIO19

microphone:
  - platform: i2s_audio
    id: echo_microphone
    i2s_din_pin: GPIO23
    adc_type: external
    pdm: true
    sample_rate: 16000
    correct_dc_offset: true

speaker:
  - platform: i2s_audio
    id: echo_speaker
    i2s_dout_pin: GPIO22
    dac_type: external
    bits_per_sample: 16bit
    sample_rate: 16000
    channel: stereo  # The Echo has poor playback audio quality when using mon audio
    buffer_duration: 60ms

media_player:
  - platform: speaker
    name: None
    id: echo_media_player
    announcement_pipeline:
      speaker: echo_speaker
      format: WAV
    codec_support_enabled: false
    buffer_size: 6000
    volume_min: 0.4
    files:
      - id: timer_finished_wave_file
        file: https://github.com/esphome/wake-word-voice-assistants/raw/main/sounds/timer_finished.wav
    on_announcement:
      - if:
          condition:
            - microphone.is_capturing:
          then:
            - script.execute: stop_wake_word
      - light.turn_on:
          id: led
          blue: 100%
          red: 0%
          green: 0%
          brightness: 100%
          effect: none
    on_idle:
      - script.execute: start_wake_word
      - script.execute: reset_led

voice_assistant:
  id: va
  micro_wake_word:
  microphone:
    microphone: echo_microphone
    channels: 0
    gain_factor: 4
  media_player: echo_media_player
  noise_suppression_level: 2
  auto_gain: 31dBFS
  on_listening:
    - light.turn_on:
        id: led
        blue: 100%
        red: 0%
        green: 0%
        effect: "Slow Pulse"
  on_stt_vad_end:
    - light.turn_on:
        id: led
        blue: 100%
        red: 0%
        green: 0%
        effect: "Fast Pulse"
  on_tts_start:
    - light.turn_on:
        id: led
        blue: 100%
        red: 0%
        green: 0%
        brightness: 100%
        effect: none
  on_end:
    # Handle the "nevermind" case where there is no announcement
    - wait_until:
        condition:
          - media_player.is_announcing:
        timeout: 0.5s
    # Restart only mWW if enabled; streaming wake words automatically restart
    - if:
        condition:
          - lambda: |-
              return strcmp(id(wake_word_engine_location).current_option(), "On device") == 0;
        then:
          - wait_until:
              - and:
                  - not:
                      voice_assistant.is_running:
                  - not:
                      speaker.is_playing:
          - lambda: id(va).set_use_wake_word(false);
          - micro_wake_word.start:
    - script.execute: reset_led
  on_error:
    - light.turn_on:
        id: led
        red: 100%
        green: 0%
        blue: 0%
        brightness: 100%
        effect: none
    - delay: 2s
    - script.execute: reset_led
  on_client_connected:
    - delay: 2s  # Give the api server time to settle
    - script.execute: start_wake_word
  on_client_disconnected:
    - script.execute: stop_wake_word
  on_timer_finished:
    - script.execute: stop_wake_word
    - wait_until:
        not:
          microphone.is_capturing:
    - switch.turn_on: timer_ringing
    - light.turn_on:
        id: led
        red: 0%
        green: 100%
        blue: 0%
        brightness: 100%
        effect: "Fast Pulse"
    - wait_until:
        - switch.is_off: timer_ringing
    - light.turn_off: led
    - switch.turn_off: timer_ringing

binary_sensor:
  # button does the following:
  # short click - stop a timer
  # if no timer then restart either microwakeword or voice assistant continuous
  - platform: gpio
    pin:
      number: GPIO39
      inverted: true
    name: Button
    disabled_by_default: true
    entity_category: diagnostic
    id: echo_button
    on_multi_click:
      - timing:
          - ON for at least 50ms
          - OFF for at least 50ms
        then:
          - if:
              condition:
                switch.is_on: timer_ringing
              then:
                - switch.turn_off: timer_ringing
              else:
                - script.execute: start_wake_word
      - timing:
          - ON for at least 10s
        then:
          - button.press: factory_reset_btn

light:
  - platform: esp32_rmt_led_strip
    id: led
    name: None
    disabled_by_default: true
    entity_category: config
    pin: GPIO27
    default_transition_length: 0s
    chipset: SK6812
    num_leds: 1
    rgb_order: grb
    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%

script:
  - id: reset_led
    then:
      - if:
          condition:
            - lambda: |-
                return strcmp(id(wake_word_engine_location).current_option(), "On device") == 0;
            - switch.is_on: use_listen_light
          then:
            - light.turn_on:
                id: led
                red: 100%
                green: 89%
                blue: 71%
                brightness: 60%
                effect: none
          else:
            - if:
                condition:
                  - lambda: |-
                      return strcmp(id(wake_word_engine_location).current_option(), "On device") == 0;
                  - switch.is_on: use_listen_light
                then:
                  - light.turn_on:
                      id: led
                      red: 0%
                      green: 100%
                      blue: 100%
                      brightness: 60%
                      effect: none
                else:
                  - light.turn_off: led
  - id: start_wake_word
    then:
      - if:
          condition:
            and:
              - not:
                  - voice_assistant.is_running:
              - lambda: |-
                  return strcmp(id(wake_word_engine_location).current_option(), "On device") == 0;
          then:
            - lambda: id(va).set_use_wake_word(false);
            - micro_wake_word.start:
      - if:
          condition:
            and:
              - not:
                  - voice_assistant.is_running:
              - lambda: |-
                  return strcmp(id(wake_word_engine_location).current_option(), "In Home Assistant") == 0;
          then:
            - lambda: id(va).set_use_wake_word(true);
            - voice_assistant.start_continuous:
  - id: stop_wake_word
    then:
      - if:
          condition:
            lambda: |-
                  return strcmp(id(wake_word_engine_location).current_option(), "In Home Assistant") == 0;
          then:
            - lambda: id(va).set_use_wake_word(false);
            - voice_assistant.stop:
      - if:
          condition:
            lambda: |-
              return strcmp(id(wake_word_engine_location).current_option(), "On device") == 0;
          then:
            - micro_wake_word.stop:

switch:
  - platform: template
    name: Use listen light
    id: use_listen_light
    optimistic: true
    restore_mode: RESTORE_DEFAULT_ON
    entity_category: config
    on_turn_on:
      - script.execute: reset_led
    on_turn_off:
      - script.execute: reset_led
  - platform: template
    id: timer_ringing
    optimistic: true
    restore_mode: ALWAYS_OFF
    on_turn_off:
      # Turn off the repeat mode and disable the pause between playlist items
      - lambda: |-
              id(echo_media_player)
                ->make_call()
                .set_command(media_player::MediaPlayerCommand::MEDIA_PLAYER_COMMAND_REPEAT_OFF)
                .set_announcement(true)
                .perform();
              id(echo_media_player)->set_playlist_delay_ms(speaker::AudioPipelineType::ANNOUNCEMENT, 0);
      # Stop playing the alarm
      - media_player.stop:
          announcement: true
    on_turn_on:
      # Turn on the repeat mode and pause for 1000 ms between playlist items/repeats
      - lambda: |-
            id(echo_media_player)
              ->make_call()
              .set_command(media_player::MediaPlayerCommand::MEDIA_PLAYER_COMMAND_REPEAT_ONE)
              .set_announcement(true)
              .perform();
            id(echo_media_player)->set_playlist_delay_ms(speaker::AudioPipelineType::ANNOUNCEMENT, 1000);
      - media_player.speaker.play_on_device_media_file:
          media_file: timer_finished_wave_file
          announcement: true
      - delay: 15min
      - switch.turn_off: timer_ringing

select:
  - platform: template
    entity_category: config
    name: Wake word engine location
    id: wake_word_engine_location
    optimistic: true
    restore_value: true
    options:
      - In Home Assistant
      - On device
    initial_option: On device
    on_value:
      - if:
          condition:
            lambda: return x == "In Home Assistant";
          then:
            - micro_wake_word.stop:
            - delay: 500ms
            - lambda: id(va).set_use_wake_word(true);
            - voice_assistant.start_continuous:
      - if:
          condition:
            lambda: return x == "On device";
          then:
            - lambda: id(va).set_use_wake_word(false);
            - voice_assistant.stop:
            - delay: 500ms
            - micro_wake_word.start:

micro_wake_word:
  on_wake_word_detected:
    - voice_assistant.start:
        wake_word: !lambda return wake_word;
  vad:
  models:
    - model: okay_nabu
    - model: hey_mycroft
    - model: hey_jarvis

This is the log from the Tx device when activating the assistant:

[22:18:38.130][D][button:022]: 'Start/Stop assistant' Pressed.
[22:18:38.131][D][voice_assistant:478]: State changed from IDLE to START_MICROPHONE
[22:18:38.131][D][voice_assistant:485]: Desired state set to START_PIPELINE
[22:18:38.134][D][voice_assistant:207]: Starting Microphone
[22:18:38.135][D][ring_buffer:034]: Created ring buffer with size 16384
[22:18:38.137][D][voice_assistant:478]: State changed from START_MICROPHONE to STARTING_MICROPHONE
[22:18:38.159][D][voice_assistant:478]: State changed from STARTING_MICROPHONE to START_PIPELINE
[22:18:38.171][D][voice_assistant:228]: Requesting start
[22:18:38.172][D][voice_assistant:478]: State changed from START_PIPELINE to STARTING_PIPELINE
[22:19:01.746][I][safe_mode:042]: Boot seems successful; resetting boot loop counter
[22:19:01.754][D][esp32.preferences:149]: Writing 1 items: 0 cached, 1 written, 0 failed
[22:19:40.185][W][api.connection:1978]: Home Assistant 2025.12.4 (172.16.0.5): Reading failed SOCKET_READ_FAILED errno=104
[22:19:40.203][D][voice_assistant:478]: State changed from STARTING_PIPELINE to STOP_MICROPHONE
[22:19:40.204][D][voice_assistant:485]: Desired state set to IDLE
[22:19:40.212][D][voice_assistant:478]: State changed from STOP_MICROPHONE to STOPPING_MICROPHONE
[22:19:40.223][D][voice_assistant:478]: State changed from STOPPING_MICROPHONE to IDLE
[22:20:01.838][D][api:136]: Accept 172.16.0.5
[22:20:01.853][D][api.connection:1398]: Home Assistant 2025.12.4 (172.16.0.5) connected

[22:19:40.185][W][api.connection:1978]: Home Assistant 2025.12.4 (172.16.0.5): Reading failed SOCKET_READ_FAILED errno=104 ← this appears due to the HA Force restart.
From the log it seems the device keeps waiting for a response from HA, but it hangs indefitively.

This is a backtrace taken with gbd when the process remains stuck:

Thread 1 "python3" received signal SIGINT, Interrupt.
0x00007f614f0eb0ae in _PyEval_EvalFrameDefault ()
   from /usr/local/lib/libpython3.13.so.1.0
(gdb) bt
#0  0x00007f614f0eb0ae in _PyEval_EvalFrameDefault ()
   from /usr/local/lib/libpython3.13.so.1.0
#1  0x00007f614f1ce0b8 in ?? () from /usr/local/lib/libpython3.13.so.1.0
#2  0x00007f614b7649e8 in ?? ()
   from /usr/local/lib/python3.13/lib-dynload/_asyncio.cpython-313-x86_64-linux-musl.so
#3  0x00007f614b7668b0 in ?? ()
   from /usr/local/lib/python3.13/lib-dynload/_asyncio.cpython-313-x86_64-linux-musl.so
#4  0x00007f614f0d9ce7 in _PyObject_MakeTpCall ()
   from /usr/local/lib/libpython3.13.so.1.0
#5  0x00007f614f0f35e5 in _PyEval_EvalFrameDefault ()
   from /usr/local/lib/libpython3.13.so.1.0
#6  0x00007f610ba011c7 in __pyx_f_13aioesphomeapi_10connection_13APIConnection_process_packet ()
   from /usr/local/lib/python3.13/site-packages/aioesphomeapi/connection.cpython-313-x86_64-linux-musl.so
#7  0x00007f610b889031 in __pyx_f_13aioesphomeapi_13_frame_helper_5noise_19APINoiseFrameHelper__handle_frame ()
   from /usr/local/lib/python3.13/site-packages/aioesphomeapi/_frame_helper/noise.cpython-313-x86_64-linux-musl.so
#8  0x00007f610b887373 in __pyx_f_13aioesphomeapi_13_frame_helper_5noise_19APINoiseFrameHelper_data_received ()
--Type <RET> for more, q to quit, c to continue without paging--
   from /usr/local/lib/python3.13/site-packages/aioesphomeapi/_frame_helper/noise.cpython-313-x86_64-linux-musl.so
#9  0x00007f610b8882b4 in __pyx_pw_13aioesphomeapi_13_frame_helper_5noise_19APINoiseFrameHelper_9data_received ()
   from /usr/local/lib/python3.13/site-packages/aioesphomeapi/_frame_helper/noise.cpython-313-x86_64-linux-musl.so
#10 0x00007f614f0dc129 in PyObject_Vectorcall ()
   from /usr/local/lib/libpython3.13.so.1.0
#11 0x00007f614f0ed816 in _PyEval_EvalFrameDefault ()
   from /usr/local/lib/libpython3.13.so.1.0
#12 0x00007f614f144d9c in ?? () from /usr/local/lib/libpython3.13.so.1.0
#13 0x00007f614f25b722 in ?? () from /usr/local/lib/libpython3.13.so.1.0
#14 0x00007f614f092130 in ?? () from /usr/local/lib/libpython3.13.so.1.0
#15 0x00007f614f1046c6 in ?? () from /usr/local/lib/libpython3.13.so.1.0
#16 0x00007f614f0eee11 in _PyEval_EvalFrameDefault ()
   from /usr/local/lib/libpython3.13.so.1.0
#17 0x00007f614f1bac3e in PyEval_EvalCode ()
   from /usr/local/lib/libpython3.13.so.1.0
#18 0x00007f614f1d5220 in ?? () from /usr/local/lib/libpython3.13.so.1.0
#19 0x00007f614f1046c6 in ?? () from /usr/local/lib/libpython3.13.so.1.0
#20 0x00007f614f0dc129 in PyObject_Vectorcall ()
   from /usr/local/lib/libpython3.13.so.1.0
#21 0x00007f614f0ed816 in _PyEval_EvalFrameDefault ()
--Type <RET> for more, q to quit, c to continue without paging--
   from /usr/local/lib/libpython3.13.so.1.0
#22 0x00007f614f1efd69 in ?? () from /usr/local/lib/libpython3.13.so.1.0
#23 0x00007f614f1eec3d in Py_RunMain ()
   from /usr/local/lib/libpython3.13.so.1.0
#24 0x00007f614f1a8767 in Py_BytesMain ()
   from /usr/local/lib/libpython3.13.so.1.0
#25 0x00007f614f699496 in libc_start_main_stage2 (main=0x562481618190, argc=5, 
    argv=0x7ffd68d24b98) at src/env/__libc_start_main.c:95
#26 0x0000562481618056 in _start ()
(gdb) 

This is my configuration:

  • Home Assistant OS 16.3 Running on Proxmox
  • Home Assistant 2025.12.4
  • ESPHome 2025.12.2
  • All my devices (Sonoffs, Atom echo, Voice assistant PE) use the same Assistant profile

The sonoff microphone and speaker do work correctly: The media_player works if i try to stream something via HA and the microphone correctly detects my wake word (not included in the stripped down yaml) or my voice with older version of HA, so I really don’t understand where the problem could be.

Can you please help me? I’ve really tried any sort of different configuration / setting, but I haven’t find a way to solve the problem

Thanks!

Thanks, just reported in Home assistant core github tracker: