[Custom Integration] ESPHome Update Manager

Hi,
I have the following problem:
only after a fresh reboot the ESPHome is searching the ESP with its IP. See my question here:

I also found this hint while googleing the whole day:

esphome run s03.yaml --device 172.16.121.178 --device s03.local

So one can start the esphome telling it should first search for the IP, than the device. Do you by chance see a way to implement this? In the above link, I have the longer explanation, what I tracked down, and why I cannot solve it on the network, where it probably belongs to.

thank you
Juergen

There's nothing I need to change in the integration for this to work for you — the fix is on the ESPHome/yaml side, not in this integration.

My integration just delegates the connection to the ESPHome dashboard API. It doesn't do any mDNS lookups or build .local addresses itself — whatever address the ESPHome dashboard uses to reach a device is also what gets used for OTA updates through this integration.

So as soon as you tell ESPHome itself to stop using <name>.local and use the IP directly, both the dashboard's log/OTA and my integration's batch updates will work without any .local resolution.

See my reply in the other topic for the solution.

HI,
I have seen your solution in the other thread, but it does not really solve my problem. Google and chatgpt suggest, I have a text sensor in the YAML

text_sensor:
  - platform: wifi_info
    ip_address:
      name: "s03 IP Address"
      id: s03_ip_address

that exposes the IP Addres. I just tried it, and it works:

So is there a chance, that I can call the API of ESPHome Update Manager and tell to use the IP for connection, not the s03.local?

Unfortunately no — my integration doesn't have an "address override" API, and adding one wouldn't actually help, because my integration isn't the thing that opens the connection to the ESP.

Here's what actually happens when you trigger an update through my integration:

  1. My integration tells the ESPHome dashboard to start an OTA for device s03.
  2. The ESPHome dashboard then decides how to reach s03 — it uses use_address from the yaml if set, otherwise falls back to <name>.local via mDNS.
  3. The dashboard pushes the firmware to the resolved address.

My integration is only involved in step 1 — it has no part in the actual network connection. So even if I added an "override IP" field to my UI, it would have nowhere to send that information; the dashboard doesn't expose a per-call address override in its API.

The text_sensor with wifi_info is nice for visibility in HA, but it only exposes the IP to Home Assistant — it doesn't change anything about how the ESPHome dashboard connects back to the device. The dashboard doesn't read HA entity states; it has its own resolver.

So the actual fix really is use_address: 172.16.121.178 in the yaml. That's not a workaround — it's the official ESPHome option for exactly your situation (mDNS not working, stable IP available). Once you set it and flash once, every future connection (logs, OTA via dashboard, OTA via my integration, OTA via Fleet for ESPHome, etc.) will go straight to the IP without any .local lookup.

If the IP ever changes (router replacement, DHCP pool reset, etc.), you'd need to update the yaml and flash once via USB. But based on your description ("dynamically, but always the same") that should be rare.

Hi,
I played around the last 14 days with ESPHome Update Manager. tl;dr it now works for me reliably, if I do some manual steps before, which might be automated (and might be valuable for others as well to do.)
The biggest issue arrives, if there is an ESPHome update, and I have a forced update. This "walks away on my development machine", but in the production update, the very long compile is an issue, as it either ends with an error on ESPHome Update manager (that I do not see), or "something" happens.
What I noticed that works reliably is this procedure:
Manually:
1.) Go to each device in ESPHome and "clean all files"
2.) For each device: Install -> Manual download -> CANCEL (no need for download!)
This compiles the new firmware and with the "clean all files" before, it takes "forever" but works always; bonus side effect: As the firmware is already compiled (and if the yaml is not changed afterwards), than the OTA is faster, as there is no more compile, just the Upload to the ESP. It seems only linking is done.
3.) Tell ESP to be awake for longer period next time he wakes up.
4.) Go to ESPHome Updates and than force the update for this ESP.

If this is done, than the update works every time.

I wonder if you could do the steps 1.) and 2.) as either an option within ESPHome Update Manager, or if there is are actions, where I could trigger this. (I digged in ESPHome but without any luck.)

all the best
Juergen

Hi, I’m on vacation right now and will look into this when back home.

1 Like

Hi Juergen,

If I understand correctly, you'd like 3 services/actions to be exposed so you can automate them yourself:

  1. Clean build files for selected devices (or all)
  2. Compile only for selected devices (or all)
  3. OTA upload only for selected devices (or all)

Correct?

Hi,
yes, that would be my wishlist. "all" is not necessary in my case as I think one could collect his specific devices in homeassistant himself and all might be too much, as one might have different device classes where one group of devices should be updated and one not. IMHO single device for all three calls is sufficient, as one wants to control what happens.

many thanks
Juergen

Hi,

Release v1.10.2 is out adds three separate actions for use in scripts and automations:

  • esphome_update_manager.clean_build_files
  • esphome_update_manager.compile
  • esphome_update_manager.upload

These let you trigger each step of the ESPHome build pipeline independently — for example, pre-compile firmware before a deep sleep device wakes up, then upload the moment it comes online.

Please give it a try and let me know if this works for your use case. More details and example automations can be found in the README.

1 Like

Hi @Krivatri,
sorry for coming back late. I was part of the organisation team in this event https://neoscon.io/, but I now found time to test. Again many thanks for the implementation. At the moment I am trying to understand what is going on. Here my results.

I have now a script like the one from your example in the documentation. It works, and looks like

alias: clean_update_s03
description: ""
sequence:
  - variables:
      s03_device_id: "{{ device_id('update.s03_firmware') }}"
  - action: esphome_update_manager.clean_build_files
    metadata: {}
    data:
      device_id:
        - "{{ s03_device_id }}"
    enabled: true
  - action: esphome_update_manager.compile
    metadata: {}
    data:
      device_id:
        - "{{ s03_device_id }}"
    enabled: false

which makes it easier for me to use my "speaking" ESP name, in this case S03. For testing and understanding, I disabled/enabled actions in the script.

In the log I get

  s03
   Status: success
   Type: Clean Build Files
   Version: 1.8.31 (ESPHome 2026.5.1)
   Started: 2026-06-08T15:35:05.235098
   Finished: 2026-06-08T15:35:06.388778

So compile works, except if I want to run the two services directly after each other. I see that in you demo YAML you wait a fixed time for the clean and assume an error, if the compile is not done after a certain time.
Is there a change to read actually the result of what the log is showing? Is there any sensor filled with this data for each ESP, that you update after either compile, or error of any esphome_update_manager task? If yes, one could wait for the "OK" result and than continue with the compile. And than one could have an automation, that is triggered by the "compiled ready", check for OK or false and either prepare the ESP for update on next wakeup or not.
Don't get me wrong: I think it will work with fixed time waiting, but I also think that one will use hunting edge cases, where the compile takes longer, as more than one device is compiling, or whatever else reason.

Another small issue I noticed:
I had a "Multiple update", ESP home changed from changed from 2026.5.1 t0 2026.5.3 and i made a firmware number update (Only firmware) of my ESP from 1.8.31 to 1.8.32 to see what happens. I cleaned the files, I than compiled and in the ESPHome Update Manager after the compile still was written:
2026.5.1 (1.8.31) → 2026.5.3 (1.8.31) -> so kept my 1.8.31 instead of 1.8.32

After the update, it showed everything as expected -> 2026.5.3 (1.8.32)

I do not know if this is an issue or intentionally, but if you want to check if the compile did work (next morning, befor doing the update), a "hint" what version the new compile led to would be IMHO good.

Many thanks
Juergen

Hi,
I don’t have much time to fix these issues, but I’ll take a look and keep you posted.
Sorry if it takes a bit longer than usual.

I had some time to investigate and the first issue can be solved with a small change to your script. The integration already fires an esphome_update_manager_finished event that you can wait on between steps:

alias: clean_compile_s03
sequence:
  - variables:
      s03_device_id: "{{ device_id('update.s03_firmware') }}"

  - action: esphome_update_manager.clean_build_files
    data:
      device_id: ["{{ s03_device_id }}"]
  - wait_for_trigger:
      - trigger: event
        event_type: esphome_update_manager_finished
        event_data:
          operation: clean
    timeout: "00:05:00"
    continue_on_timeout: false

  - if: "{{ wait.trigger.event.data.summary.get('success', 0) == 0 }}"
    then:
      - stop: "Clean failed — skipping compile"
        error: true

  - action: esphome_update_manager.compile
    data:
      device_id: ["{{ s03_device_id }}"]
  - wait_for_trigger:
      - trigger: event
        event_type: esphome_update_manager_finished
        event_data:
          operation: compile
    timeout: "00:30:00"
    continue_on_timeout: false

  - if: "{{ wait.trigger.event.data.summary.get('success', 0) == 0 }}"
    then:
      - stop: "Compile failed — skipping upload"
        error: true

  - action: esphome_update_manager.upload
    data:
      device_id: ["{{ s03_device_id }}"]

Please report back if this works for you — if so I'll add it to the README.

I'll look into your second observation (panel showing the old project version after a YAML bump) next — it looks like a display refresh issue during the compile window, the actual install result is correct.

EDIT:
second issue (project version) should be resolved in release v1.10.3

I am just testing, but noticed, that the "wrong firmware" is not happening, when it does the compile (and the ESPHome version is not updated at the same time...) see

thank you so much
Juergen
P.S.: I have to test more throrowly tomorrow, as my sleeping ESP make me crazy. I have to make sure, to get them AWAKE before the upload, without the upload beeing triggered, if they are in the "short wake" period, not rocket science, but I am on the run. Will check tomorrow.