Access Globals from update() function of custom component?

How might I expose a variable in a custom component .h file to lambdas and/or expose esphome globals to the .h code. Obviously, I’m not competent at C++ coding.

For those who are still trying to figure this out:

A global in an ESPHome configuration YAML file translates to an instance of the GlobalsComponent class in the C++ code, declared in your <NODE_NAME>/src/main.cpp. You can access it in a custom component .h file as follows:

my_global->value() = something;
something = my_global->value();

where something and my_global should be of the same or compatible type.

So by defining one or more globals in the YAML file, data can be exchanged between (custom) components, from YAML to C++ and back.

1 Like

Hi @clemo , thanks for the suggestion. I was looking for a way to share data between a custom component and esphome but when I use it the way you defined, I get an error that the global variable isnt defined which is obvious as the custom component does not include the file which includes this definition. How can we solve that?

Do you include your custom component header file in ESPHome’s YAML file? Like this:

esphome:
  name: "relay4"
  platform: ESP8266
  board: esp01_1m
  includes:
    - relay4.h

ESPHome parses the YAML file and includes your header file right below the definitions of the globals in main.cpp so your code is aware of the declarations:

globals::GlobalsComponent<uint8_t> *relay4_state;
#include "relay4.h"
// ========== AUTO GENERATED INCLUDE BLOCK END ==========="

The global variable is defined in the YAML file:

globals:
  - id: relay4_state
    type: uint8_t
    restore_value: no
    initial_value: '0'

Hope this helps.

Thanks for the explanation, that makes sense. I tried again and it worked, maybe I was doing something wrong yday.

Hello,

Is there any way to “pass” the name of a global variable to a custom light component?
Following what suggested in this thread I have managed to access a global variable from a custom light component. However, I would like to avoid hardcoding in he custom light component the name of the global variable and instead pass the name of the global variable when defining the custom light in the .yaml file.

For example:

esphome:
  includes:
    - kessil.h

globals:
  - id: reef_tank_light_brightness_value
    type: float
  - id: reef_tank_light_color_value
    type: float

light:
  - platform: custom
    id: reef_tank_light
    lambda: |-
      auto kessil_light = new KessilLight(id(reef_tank_light_brightness_pwm), id(reef_tank_light_color_pwm), id(reef_tank_light_brightness_value), id(reef_tank_light_color_value));
      App.register_component(kessil_light);
      return {kessil_light};

How can I then access the global variables (in this case reef_tank_light_brightness_value and reef_tank_light_color_value) from the custom light component?

Hello all,

I noticed in the main.cpp file the global variable are defined as follow:

globals::GlobalsComponent<float> *reef_tank_light_brightness_value;
globals::GlobalsComponent<float> *reef_tank_light_color_value;

I have so edit the custom light class to take as two globals::GlobalsComponent pointers as arguments:

#pragma once

#include "esphome.h"

class KessilLight : public Component, public LightOutput, public LightColorValues {
    public:
    KessilLight(FloatOutput *brightness_output, FloatOutput *color_output, globals::GlobalsComponent<float>  *brightness_value, globals::GlobalsComponent<float>  *color_value)
    {
        brightness_output_ = brightness_output;
        color_output_ = color_output;
        brightness_value_ = brightness_value;
        color_value_ = color_value;
    }

    LightTraits get_traits() override {
        auto traits = LightTraits();
        traits.set_supports_brightness(true);
        traits.set_supports_color_temperature(false);
        traits.set_supports_rgb(false);
        traits.set_supports_rgb_white_value(true);
        return traits;
    }

    void write_state(LightState *state) override {
        float red, green, blue;
        state->current_values_as_rgbw(&red, &green, &blue, &white, this->color_interlock_);
        state->current_values_as_brightness (&brightness);
        white = white / brightness;
        if (brightness == 0)
                {
            this->brightness_output_->set_level(0);
            this->color_output_->set_level(0);
            this->brightness_value_->value() = 0;
            this->color_value_->value() = 0;
        }
        else
        {
            this->brightness_output_->set_level(brightness);
            this->color_output_->set_level(white);
            this->brightness_value_->value() = brightness * 100;
            this->color_value_->value() = white * 100;
        }
        
    }

    protected:
    FloatOutput *brightness_output_;
    FloatOutput *color_output_;
    globals::GlobalsComponent<float>  *brightness_value_;
    globals::GlobalsComponent<float>  *color_value_;
    float brightness;
    float white;
    bool color_interlock_;
};

An in the .yaml file I pass the two global variables when defining the custom light:

light:
  - platform: custom
    id: reef_tank_light
    lambda: |-
      auto kessil_light = new KessilLight(id(reef_tank_light_brightness_pwm, reef_tank_light_brightness_value, reef_tank_light_color_value), id(reef_tank_light_color_pwm));
      App.register_component(kessil_light);
      return {kessil_light};
    lights:
      - name: "Reef Tank - Light"

Now this code compiles without errors and gets uploaded to the ESP8266. However, ESPHome fails to start up. After this code has been uploaded to the ESP8266 via OTA upload I can ping the device… but HA fails to connect to through the API. I have the impression this setup causes some sort of runtime error…

Any suggestion or advice?

I know this is an old question but going to the original question asked here, it is possible to directly expose variables in custom component .h files to lambdas.

I gave an example of how to do it here: esphome::custom::CustomLightOutputConstructor' has no member named 'current_values' - #4 by stefan-sh

Quite awhile later, but figured I’d add my own experience with this type of problem:

I’m passing a few globals into a custom class via the class constructor, as is shown in the examples in this thread. I’m using the c++ constructor initializer feature, instead of plain assignment in the constructor’s execution block.

Everything works perfectly on first boot after flashing. I’m able to read and write to the globals using pointers stored in my class. However, if I write anything to the globals from my class, all subsequent boots fail. If I don’t write anything to the globals from my class, subsequent boots work fine. Also note that if I access the globals directly by their original id, from within my class, there are no problems reading/writing. The problem is only when I store and use pointers to the globals from within my class.

I’m assuming that I’m writing bad data or writing to the wrong pointer or memory location, and that must be fouling the entire memory structure.

It would be a lot easier to just store the simple global data in my class instance. But I want the persistence features of esphome globals. I may try instantiating my own behind-the-scenes globals from within my class, or maybe even try to write directly to flash. Then I could remove the globals entirely from the main yaml file.

Curious if anyone got this pattern working, especially with regard to writing back to the globals.