What is the right way to call a Homeassisant service from ESPHome lambda code?

The latest esphome 2025.8.0 seems to have broken my lambda code that I used to turn on and off a Kasa plug (and worked 2 weeks ago)

              esphome::api::HomeassistantServiceResponse resp; // Call the service via C
              resp.service = "switch.turn_off";
              esphome::api::HomeassistantServiceMap data;
              data.key = "entity_id";
              data.value = "${filter_plug_entity}";
              resp.data.push_back(data);
              id(ha_api).send_homeassistant_service_call(resp);

With error:

/config/esphome/Wen3417Remote.yaml: In lambda function:
/config/esphome/Wen3417Remote.yaml:496:16: error: 'class esphome::api::HomeassistantServiceResponse' has no member named 'service'; did you mean 'set_service'?
  496 |               resp.service = "switch.turn_off";
      |                ^~~~~~~
      |                set_service
/config/esphome/Wen3417Remote.yaml:498:16: error: 'class esphome::api::HomeassistantServiceMap' has no member named 'key'
  498 |               data.key = "entity_id";
      |                ^~~

I tried a newer approach like:

              esphome::api::CustomAPIDevice api;
              api.call_service("switch", "turn_off", {{"entity_id", "${filter_plug_entity}"}});

But that and variations gave errors like:

/config/esphome/Wen3417Remote.yaml: In lambda function:
/config/esphome/Wen3417Remote.yaml:496:15: error: 'class esphome::api::CustomAPIDevice' has no member named 'call_service'
  496 |               api.call_service("switch", "turn_off", {{"entity_id", "${filter_plug_entity}"}});
      |               ^~~~~~~~~~~~

Trying:

              id(ha_api).call_service("switch.turn_off", {{"entity_id", "${filter_plug_entity}"}});

gave similar errors.

There must be a simple way to call a service from lambda code as that seems like a very basic thing to do but I can’t figure out how…
(NOTE: I know I could try to do this all in yaml, but I have a complex sequence of service calls and associated manipulations
that is simpler to do all in C)

The proble with accessing ESPHome internals directly is that they can change without notice. I have been caught out before as well.

Looks like that function changed for 2025.8:

https://api-docs.esphome.io/classesphome_1_1api_1_1_homeassistant_service_response.html

2 Likes

hello,

same boat :

/config/esphome/digicode.yaml:1141:24: error: 'class esphome::api::HomeassistantServiceMap' has no member named 'key'
 1141 |               entity_id_kv.key = "position";
      |                        ^~~
/config/esphome/digicode.yaml:1151:16: error: 'class esphome::api::HomeassistantServiceResponse' has no member named 'service'; did you mean 'set_service'?
 1151 |               resp.service = "cover.set_cover_position";
      |                ^~~~~~~
      |                set_service

please, i dont understand what to do exactly?

resp.set_service

doesnt work

I’m on the same boat, everything is broken after 2025.8.0.

/config/esphome/common/sonoff-m5/m5-3c-86/binary_sensor.yaml: In lambda function:
/config/esphome/common/sonoff-m5/m5-3c-86/binary_sensor.yaml:22:9: error: 'HomeassistantServiceResponse' was not declared in this scope
   22 |               HomeassistantServiceResponse svc;
      |         ^     ~~~~~~~~~~~~~~~~~~~~~~
/config/esphome/common/sonoff-m5/m5-3c-86/binary_sensor.yaml:23:9: error: 'svc' was not declared in this scope
   23 |               svc.service = "pyscript.switch_single_click";
      |         ^  
/config/esphome/common/sonoff-m5/m5-3c-86/binary_sensor.yaml:25:9: error: 'HomeassistantServiceMap' was not declared in this scope
   25 |               HomeassistantServiceMap ent;
      |         ^     ~~~~~~~~~~~~~~~~~
/config/esphome/common/sonoff-m5/m5-3c-86/binary_sensor.yaml:26:9: error: 'ent' was not declared in this scope; did you mean 'int'?
   26 |               ent.key = "ls_eid";
      |         ^  
      |         int
/config/esphome/common/sonoff-m5/m5-3c-86/binary_sensor.yaml:29:28: error: 'class esphome::api::APIServer' has no member named 'send_homeassistant_service_call'
   29 |               global_api_server->send_homeassistant_service_call(svc);
      |                            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  

My code as follows

          HomeassistantServiceResponse svc;
          svc.service = "pyscript.switch_single_click";

          HomeassistantServiceMap ent;
          ent.key = "ls_eid";
          ent.value = "binary_sensor.${eid}_left_button";
          svc.data.push_back(ent);
          global_api_server->send_homeassistant_service_call(svc);

I’m not really familiar with C++, but am I suppose to change it to this?

esphome::api::HomeassistantServiceResponse svc;
svc.set_service("pyscript.switch_single_click");

esphome::api::HomeassistantServiceMap ent;
ent.set_key("ls_eid");
ent.value = "binary_sensor.${eid}_left_button";
svc.data.push_back(ent);
global_api_server->send_homeassistant_service_call(svc);

My builds were also broken after updating 2025.8.0, but thankfully the changes are not too significant and got this working after looking at the docs. You’re almost there, here’s your code where I updated the args passed to the set_* functions to use the StringRef type:

esphome::api::HomeassistantServiceResponse svc;
svc.set_service(esphome::StringRef("pyscript.switch_single_click"));

esphome::api::HomeassistantServiceMap ent;
ent.set_key(esphome::StringRef("ls_eid"));
ent.value = "binary_sensor.${eid}_left_button";
svc.data.push_back(ent);
global_api_server->send_homeassistant_service_call(svc);
1 Like

Thanks, it’s working, note that homeassistant_services: true in api: need to be added to make it work.

You can also simplify it by removing the namespace

          HomeassistantServiceResponse svc;
          svc.set_service(StringRef("pyscript.switch_single_click"));

          HomeassistantServiceMap ent;
          ent.set_key(StringRef("ls_eid"));
          ent.value = "binary_sensor.${eid}_left_button";
          svc.data.push_back(ent);
          global_api_server->send_homeassistant_service_call(svc);

I also fixed it using a modified version based on the new api

              esphome::api::HomeassistantServiceResponse resp; // Call the service via C
              resp.set_service(esphome::StringRef("switch.turn_off"));
              esphome::api::HomeassistantServiceMap data;
              data.set_key(esphome::StringRef("entity_id"));
              data.value = "${filter_plug_entity}";
              resp.data.push_back(data);
              id(ha_api).send_homeassistant_service_call(resp);

Wish breaking changes like this would have been documented.

I am having a similar issue (HA 2026.1.2, ESPHome 2025.11.0). I can successfully trigger a switch via esphome if I do the standard yaml method, but I cannot get it working with a lambda.
Example esphome lambda:

script:
  - id: power_select
    parameters:
      outlet: int
      state: int
    then:
      - lambda: !lambda |
          ESP_LOGI("main", "Power_select invoked, outlet %d action %d", outlet, state);
          HomeassistantActionRequest svc;
          HomeassistantServiceMap ent;
          ent.set_key(StringRef("entity_id"));
          ent.value = StringRef("switch.tp_link_power_strip_b54b_plug_1");
          svc.set_service(StringRef("switch.turn_on"));
          svc.data.push_back(ent);
          id(api_server)->send_homeassistant_action(svc);

The home assistant logs show that it isn’t receiving a proper entity_id:

2026-01-20 10:55:44.517 ERROR (MainThread) [homeassistant] Error doing job: Task exception was never retrieved (task: None)
Traceback (most recent call last):
  File "/usr/local/lib/python3.13/site-packages/voluptuous/schema_builder.py", line 205, in __call__
    return self._compiled([], data)
           ~~~~~~~~~~~~~~^^^^^^^^^^
  File "/usr/local/lib/python3.13/site-packages/voluptuous/schema_builder.py", line 779, in validate_callable
    return schema(data)
  File "/usr/src/homeassistant/homeassistant/helpers/config_validation.py", line 227, in validate
    raise vol.Invalid(f"must contain at least one of {expected}.")
voluptuous.error.Invalid: must contain at least one of entity_id, device_id, area_id, floor_id, label_id.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/core.py", line 2784, in async_call
    processed_data: dict[str, Any] = handler.schema(service_data)
                                     ~~~~~~~~~~~~~~^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.13/site-packages/voluptuous/validators.py", line 259, in __call__
    return self._exec((Schema(val) for val in self.validators), v)
           ~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.13/site-packages/voluptuous/validators.py", line 386, in _exec
    raise e if self.msg is None else AllInvalid(self.msg, path=path)
  File "/usr/local/lib/python3.13/site-packages/voluptuous/validators.py", line 382, in _exec
    v = func(v)
  File "/usr/local/lib/python3.13/site-packages/voluptuous/schema_builder.py", line 209, in __call__
    raise er.MultipleInvalid([e])
voluptuous.error.MultipleInvalid: must contain at least one of entity_id, device_id, area_id, floor_id, label_id.

I’m assuming my lambda is wrong somewhere, maybe the keyval schema is now different?