ESPHome external component: setup() and loop() never called on Component + UARTDevice

ESPHome external component: setup() and loop() never called on Component + UARTDevice

Summary

I have an external ESPHome component for controlling a Tesla Wall Connector Gen 2 via RS485. The component is correctly registered via App.register_component() in the generated main.cpp, but setup() and loop() are never called. Even with ESP_LOGE as the very first line of setup(), nothing appears in the logs.

Environment

  • ESPHome version: 2026.2.0
  • Board: Olimex ESP32-POE-ISO (esp32-poe-iso)
  • Framework: Arduino
  • Python: 3.14.5

Component structure

The component inherits from Component, uart::UARTDevice and TeslaControllerIO:

class TWCController : public Component, public uart::UARTDevice, public TeslaControllerIO {
public:
    void setup() override;
    void loop() override;
    void dump_config() override;
    ...
};

The __init__.py registers it like this:

async def to_code(config):
    var = cg.new_Pvariable(config[CONF_ID])
    await cg.register_component(var, config)
    await uart.register_uart_device(var, config)
    ...

Generated main.cpp (relevant part)

twc = new twc_controller::TWCController();
twc->set_component_source(LOG_STR("twc-controller"));
App.register_component(twc);
twc->set_uart_parent(twc_uart);
...
App.setup();  // called at line 613

So App.register_component(twc) IS called, and App.setup() IS called. But setup() inside the component is never reached.

What I see in logs

dump_config() IS called (showing TWC ID, min/max current, flow control pin), but setup() is never called. Even this does not appear:

void TWCController::setup() {
    ESP_LOGE(TAG, "SETUP CALLED!!!");  // never appears in logs
}

What I tried

  • Changed inheritance order (Component first, then UARTDevice)
  • Changed get_setup_priority() from AFTER_CONNECTION to DATA
  • Moved initialization to loop() with initialized flag — loop() is also never called
  • Cleared build cache completely
  • Downgraded ESPHome to 2026.2.0

Question

Why would setup() and loop() not be called on a component that is registered via App.register_component()? Is there something about combining Component with uart::UARTDevice registration that prevents this?

The component is based on https://github.com/jnicolson/esphome-twc-controller

Any help appreciated.

You may find the latest release has quite a number of updates, and a careful read of the March, April and May release notes as well may offer you some solutions as the UART section has received some love recently.

Specifically look at this section in the breaking changes:

PollingComponent() default constructor: now initializes to SCHEDULER_DONT_RUN (UINT32_MAX) instead of 1. External components that bypass codegen, call PollingComponent() with no argument, and never call set_update_interval() will stop polling. Pass an interval to the constructor or call set_update_interval() explicitly #15832
1 Like