Calling HA services from within esphome lambda functions

Hi,

Can anyone advise how to call HA services from within lambda functions? I’ve had a hunt around and couldn’t find any examples (and the Native API component page is fairly sparse about the what globals, objects, etc. are available within the lambda functions).

My use case is a rotary encoder modifying a global variable to move a cursor on a screen. I have a separate boolean_sensor which I’m looking to create a lambda action within the on_click trigger. I want to use a switch statement to fire different Home Assistant services depending on the value of the global variable.

Thanks!

I stand under correction, but I don’t think you can. I think you can read sensors from HA, but I don’t believe you can call HA services.

I’ve been mucking about with 2-gang light switches today, is why I think this. I ended up having to do it in Node-RED.

Yes, you can call HA services from ESPHome. See the documentation here:
https://esphome.io/components/api.html#homeassistant-service-action

It’s pretty straight forward. The only required key is the service: key. If you need to pass data like in the example to the service, then you include the data: key. Of course follow proper formatting of your YAML. This all goes in your ESPHome node, not in HA.

1 Like

Yeah you can. However OP is asking using lambdas ie. kind of c++ code directly.

It doesn’t seem straightforward. A look in the source is required.

I would try doing it with esphome automations entirely.

Thanks, yes - I know about being able to use the homeassistant.service action in automations and scripts, but my use case is significantly more simple in C++ rather than YAML.

I’d had a dig through the code and not seen anything obvious, but I’ll go back and see if I can figure something out.

1 Like

I guess you’ll have to give api: an id, say api_server

  api: 
    id: api_server

then you should be able to do api_server->send_service_call(...);

This method requires a esphome::api::ServiceCallResponse so may be something like this:

   esphome::api::ServiceCallResponse r;
   r.set_service("light.turn_on");
   r.set_data({api::KeyValuePair("entity_id", "light.my_light")});
   api_server->send_service_call(r);

I did not test this code though.

2 Likes

Thanks @glmnet - your code works pretty much perfectly and is much cleaner than the way I’d found earlier today.

For reference, the approach I got working earlier is as follows:

api::HomeAssistantServiceCallAction<> *ha_service;
ha_service = new api::HomeAssistantServiceCallAction<>(api_apiserver);
ha_service->set_service("light.toggle");
ha_service->set_data({api::KeyValuePair("entity_id", "light.my_light")});
ha_service->play();

Things to note - this relies on the default naming of the api (api_apiserver) - much better to explicitly set it as @glmnet suggests.

Ahhh yes. You’re using the action there. I went a bit deeper for simplicity.
Keep in mind Otto is a refactor ninja and this is not documented/supported api. So be ready to face a compilation error in future versions.

Thanks! I’ll bear that in mind!

In the current version set_service and set_data seems to be absent. Rather there is add_data, add_data_template, add_variable functions.

api::HomeAssistantServiceCallAction<> *ha_service;
ha_service = new api::HomeAssistantServiceCallAction<>(&*api_apiserver, true);
ha_service->add_data("entity_id", "input_boolean.cycle1_enable");
ha_service->play();

I made it this far but can’t figure out how to set the service to call. There seems to function called TEMPLATABLE_STRING_VALUE but I couldn’t get it to work. I would have set the service variable directly but it is private.
Also, I tried this in desperation, doesn’t work and makes it disconnect from HA and reconnect.

ha_service->add_data("service", "input_boolean.toggle");
ha_service->add_variable("entity_id", "input_boolean.cycle1_enable");

Hi, I’m having this exact problem. I have a custom component that defines a callback function. Inside this callback I want to call a HA service, but all this logic is defined as a lambda. Is there some new info about how to do that?

Thanks!

I’m also trying to call a home assistant service from within a lambda in ESPHome.
I’ve tried the various snippets of code in this topic but couldn’t get anything working.
Does anybody have a working example of how to call a home assistant service from within lambda code in ESPHOME?
Thanks!

In case others find this thread in search…seen on discord (from jesserockz) …

    HomeassistantServiceResponse resp;
    resp.service = "light.turn_on";
      HomeassistantServiceMap entity_id_kv;
      entity_id_kv.key = "entity_id";
      entity_id_kv.value = "light.blah";

      resp.data.push_back(entity_id_kv);
    id(api_id).send_homeassistant_service_call(resp);

“Just push_back more datas for each key/value pair”

1 Like

If i need to pass a variable to my “value”, defined in my lamba call… i can just assign it?
Like:

          HomeassistantServiceResponse resp;
          HomeassistantServiceMap      entity_id_kv;
          float                        v_set_temp;
          resp.service = "climate.set_temperature";
          entity_id_kv.key = "entity_id";
          entity_id_kv.value = "climate.trv_salotto";
          resp.data.push_back(entity_id_kv);
          entity_id_kv.key   = "temperature";
          v_set_temp         = id(trv_salotto_set_temp).state;
          v_set_temp = (v_set_temp + 0.5);
          entity_id_kv.value = v_set_temp;
          resp.data.push_back(entity_id_kv);
          id(api_id).send_homeassistant_service_call(resp);

It does not do anything this way… maybe I’m looking at the wrong part!

Thank you

Had a quick test - presumably you do have the id set under the “api:” section of yaml ?

Also appears the .value needs to be a string. This works for me:-

                       HomeassistantServiceResponse resp;
                       resp.service = "light.turn_on";
                       HomeassistantServiceMap entity_id_kv;
                       entity_id_kv.key = "entity_id";
                       entity_id_kv.value = "light.dim_09_light";
                       resp.data.push_back(entity_id_kv);
                       entity_id_kv.key = "brightness";
                       entity_id_kv.value = to_string(int(root["params"]["white"]["br"].as<float>() / 100 * 255));
                       resp.data.push_back(entity_id_kv);
                       id(api_id).send_homeassistant_service_call(resp);
3 Likes

Hello,

did you found a solution, need exactly the same for a cover position.

The example of @DeanoX seems working but i dont understand this line

entity_id_kv.value = to_string(int(root["params"]["white"]["br"].as<float>() / 100 * 255));

what to do for us to convert an integer to string please?

Thanks

ok found it!

in your case :

id(v_set_temp) = (id(trv_salotto_set_temp).state);
id(v_set_temp) = (id(v_set_temp) + 0.5);

then

entity_id_kv.value = to_string(id(v_set_temp));

works like a charm!

thanks @DeanoX

1 Like

I’m trying to call the service light.turn_on with rgb_color parameter, which requires an array of integers (I’ve tested and it also works with an array of strings of numbers).

This is how to call the service in Home Assistant:

service: light.turn_on
data:
  rgb_color: 
    - 36
    - 11
    - 218
target:
  entity_id: light.office_ceiling_lines

Any idea on how to pass the array thru the API in a lambda call?
The resp.data.push_back(entity_id_kv); works fine with strings or numbers (converted to strings), but I’m struggling with this array for rgb_color.

I still stuck on this. Any idea?

What is the value of api_id here? I tried setting it to my device’s API key, but that won’t compile.

Code:

globals:
  - id: api_id
    type: char[50]
    initial_value: 'MY_API_KEY'

When I try to compile that, I get the error:

Compiling .pioenvs/esphome-web-6a0ed4/src/main.cpp.o
src/main.cpp: In function 'void setup()':
src/main.cpp:2024:52: error: 'MY_API_KEY' was not declared in this scope
   api_id = new globals::GlobalsComponent<char[50]>(MY_API_KEY);

I have searched the forums and done web searches, and I don’t see where/how api_id is supposed to be set.