esphome / issues

Issue Tracker for ESPHome
https://esphome.io/
290 stars 35 forks source link

Can non longer do an OTA update with ESP32 in safe_mode after ESPHOME update 2024.6.0 when using on_begin: action under ota: #5976

Open Dilbert66 opened 3 months ago

Dilbert66 commented 3 months ago

The problem

Latest changes related to safe_mode and OTA configuration seems to have broken the ability to do an OTA update if the device goes into safe_mode (either manually via a switch or via 10 failed boot attempts) when there is an on_begin action specified for the ota: I keep getting connection refused. When the on_begin section is on, the esphome.ota component does not load (no log dump display) during transition to safe_mode. The esphome.ota does show up fine and ota works when the offending on_begin is removed.

My safe mode and ota config options:

ota:
   password: !secret ota_password
   platform: esphome
   on_begin:
          switch.turn_off: connection_status_switch 

safe_mode:

Which version of ESPHome has the issue?

2024.6.2

What type of installation are you using?

Home Assistant Add-on

Which version of Home Assistant has the issue?

Core: 2024.6.4

What platform are you using?

ESP32

Board

ESP32-c3 and esp32-d1 mini

Component causing the issue

OTA and safe_mode

Example YAML snippet

No response

Anything in the logs that might be useful for us?

No response

Additional information

No response

randybb commented 3 months ago

Any logs? I have tested it on C3 and S3 and no issues.

Dilbert66 commented 3 months ago

I'm doing a few more tests. I suspect it's a custom component I'm loading that is affecting it as when I use a barebones config, the ota component loads fine in safe mode. I will post more info later.

Dilbert66 commented 3 months ago

Ok, found the culprit. I thought I had commented that out when testing. Anyhow it's an on_begin action under the ota that's causing the failure:

ota:
   password: !secret ota_password
   platform: esphome
   on_begin:
          switch.turn_off: connection_status_switch 
Dilbert66 commented 3 months ago

ok, i see what is happening. When a trigger event is added, the preprocessing delays adding the ota component to the components queue which happesn after the safe_mode return .

  // ota.esphome:
  //   platform: esphome
  //   password: !secret 'ota_password'
  //   on_begin:
  //   - then:
  //     - switch.turn_off:
  //         id: connection_status_switch
  //       type_id: switch__turnoffaction_id
  //     automation_id: automation_id_3
  //     trigger_id: ota_otastarttrigger_id
  //   id: esphome_esphomeotacomponent_id
  //   version: 2
  //   port: 3232
  esphome_esphomeotacomponent_id = new esphome::ESPHomeOTAComponent();
  ota_otastarttrigger_id = new ota::OTAStartTrigger(esphome_esphomeotacomponent_id);
  automation_id_3 = new Automation<>(ota_otastarttrigger_id);
### on a no trigger setup, the ota component setup is normally done here and added to the queue.  But since in this case, it happens later in the process after the "should_enter_safe_mode" check, it never gets added to the queue and thus is not seen in the safe_mode reboot.

// safe_mode:
  //   id: safe_mode_safemodecomponent_id
  //   boot_is_good_after: 1min
  //   disabled: false
  //   num_attempts: 10
  //   reboot_timeout: 5min
  safe_mode_safemodecomponent_id = new safe_mode::SafeModeComponent();
  safe_mode_safemodecomponent_id->set_component_source("safe_mode");
  App.register_component(safe_mode_safemodecomponent_id);
  if (safe_mode_safemodecomponent_id->should_enter_safe_mode(10, 300000, 60000)) return;
Dilbert66 commented 3 months ago

In the "esphome\ota\__init__.py" file, moving the await for the ota_to_code trigger setup after the component registration fixes the issue on my system as shown below:

@coroutine_with_priority(52.0)
async def to_code(config):
    var = cg.new_Pvariable(config[CONF_ID])
    await ota_to_code(var, config)                               <------- move this line to
    cg.add(var.set_port(config[CONF_PORT]))
    if CONF_PASSWORD in config:
        cg.add(var.set_auth_password(config[CONF_PASSWORD]))
        cg.add_define("USE_OTA_PASSWORD")
    cg.add_define("USE_OTA_VERSION", config[CONF_VERSION])

    await cg.register_component(var, config)
                                                                           <----- here