Calling HA services from within esphome lambda functions

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.

In the yaml file define the id like this:

api:
  id: api_id
1 Like

Thanks, but I think I’m still misunderstanding. I have this in my ESPHome device code, and it will not compile:

# Enable Home Assistant API
api:
  encryption:
    key: !secret api_encryption_key
  id: api_id

Which error message do you have when compiling?
Could you please share that and also your whole yaml, please?
Please remove any sensitive information before sharing.

Wow - it must have been a temporary thing – I compiled again today, and it worked and installed. And, it does what I want it to do: I can control my house lights using the buttons on the m5stack.

1 Like

Topic kick :wink: i tried updating to the latest ESPhome, and getting exact the same error. My ESPhome config looks like:

api:
services:
- service: meterstand_water
variables:
meter_value: float
then:
- globals.set:
id: initial_water_usage

id: totalWaterUsage

        value: !lambda "return ( meter_value ) ;"

ota:

dallas:

  • pin: GPIO14

wifi:
ssid: !secret SSID
password: !secret WiFi_Password
fast_connect: true

use_address: watermeter.local

Enable fallback hotspot (captive portal) in case wifi connection fails

ap:
ssid: “Watermeter Fallback Hotspot”
password: “4Ejkhjkhuixa4M”
captive_portal:
script:

  • id: reset_main_counter
    then:
    • lambda: |-
      id(main_counter_pulses) = 0;
      id(water_main_consumption).publish_state(id(main_counter_pulses));
  • id: reset_secondary_counter
    then:
    • lambda: |-
      id(secondary_counter_pulses) = 0;
      id(water_secondary_consumption).publish_state(id(secondary_counter_pulses));
  • id: publish_states
    then:
    • lambda: |-
      id(water_main_consumption).publish_state(id(main_counter_pulses));
      id(water_secondary_consumption).publish_state(id(secondary_counter_pulses));
      id(water_daily_consumption).publish_state(id(daily_counter_pulses));
      id(water_weekly_consumption).publish_state(id(weekly_counter_pulses));
      id(water_monthly_consumption).publish_state(id(monthly_counter_pulses));
      id(water_yearly_consumption).publish_state(id(yearly_counter_pulses));
      id(current_water_consumption).publish_state(id(event_quantity));

time:

  • platform: sntp
    on_time:
    • seconds: 0
      minutes: 0
      hours: 0
      then:
      • globals.set:
        id: daily_counter_pulses
        value: ‘0’
      • lambda: id(water_daily_consumption).publish_state(id(daily_counter_pulses));
    • seconds: 0
      minutes: 0
      hours: 0
      days_of_week: MON
      then:
      • globals.set:
        id: weekly_counter_pulses
        value: ‘0’
      • lambda: id(water_weekly_consumption).publish_state(id(weekly_counter_pulses));
    • seconds: 0
      minutes: 0
      hours: 0
      days_of_month: 1
      then:
      • globals.set:
        id: monthly_counter_pulses
        value: ‘0’
      • lambda: id(water_monthly_consumption).publish_state(id(monthly_counter_pulses));
    • seconds: 0
      minutes: 0
      hours: 0
      days_of_month: 1
      months: JAN
      then:
      • globals.set:
        id: yearly_counter_pulses
        value: ‘0’
      • lambda: id(water_yearly_consumption).publish_state(id(yearly_counter_pulses))

So can anybody help me what’s “wrong”?