Take a variable from HA on ESP32 boot and start working from it

Hello. I have set a magnetic counter on my home water counter, connected to an ESP32 through ESPHome with the following setup:

esphome:
  name: esphome-web-71afe8
  friendly_name: ESPHome Pantalla
  on_boot:
    - homeassistant.service:
        service: input_number.set
        data:
          entity_id: input_number.total_consumo_agua_global
          value: !lambda 'return id(total_agua).state;'

esp32:
  board: esp32dev
  framework:
    type: arduino

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "*****"

ota:

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "****"
    password: "****"

captive_portal:

sensor:
  - platform: pulse_counter
    pin: 
      number: 19
      allow_other_uses: true
    update_interval : 6s
    name: "pulso agua"
    id: pulso_agua
    filters:
      - multiply: 0.1

  - platform: pulse_meter
    pin: 
      number: 19
      allow_other_uses: true
    name: "Medidor pulsos agua"
    unit_of_measurement: "litros/min"
    icon: "mdi:water"
    total:
      name: "Total Agua"
      unit_of_measurement: "litros"
    filters:
      - multiply: 0.1

  - platform: template
    name: "Flujo de agua"
    id: flujo_agua
    accuracy_decimals: 1
    unit_of_measurement: "l/min"
    icon: "mdi:water"
    lambda: return (id(pulso_agua).state * 1);
    update_interval: 6s

  - platform: homeassistant
    id: total_agua
    entity_id: sensor.esphome_web_71afe8_total_agua
    on_value:
      then:
        - homeassistant.service:
            service: input_number.set
            data:
              entity_id: input_number.total_consumo_agua_global
              value: !lambda 'return x;'

  - platform: wifi_signal
    name: "WiFi ESP"
    update_interval: 60s
    id: intensidad_wifi_esp

font:
  - file: "fonts/roboto.ttf"
    id: robotofont
    size: 15

  - file: "fonts/roboto.ttf"
    id: robotofont_large
    size: 25
  - file: "fonts/roboto.ttf"
    id: robotofont_small
    size: 12
    
i2c:
  sda: GPIO21
  scl: GPIO22

# Añadir el componente de pantalla OLED
display:
  - platform: ssd1306_i2c
    model: "SSD1306 128x64"
    address: 0x3C
    reset_pin: GPIO0
    lambda: |-
      // Imprime "Consumo Total:"
      it.print(0, 0, id(robotofont), "Consumo Total:");
      // Imprime el valor del sensor "total_agua"
      if (id(total_agua).has_state()) {
        it.printf(0, 15, id(robotofont_large), "%.0f", id(total_agua).state);
      }
      // Imprime "Wifi:" un poco más arriba
      it.print(0, 50, id(robotofont), "Wifi:  ");
      // Imprime la intensidad de la señal WiFi como una barra horizontal más gruesa
      if (id(intensidad_wifi_esp).has_state()) {
        float signal_strength = min(max(2 * (id(intensidad_wifi_esp).state + 100.0), 0.0), 100.0);
        for (int i = 0; i < signal_strength / 10; i++) {
          it.print(35 + i * 10, 50, id(robotofont_small), "|");
          it.print(35 + i * 10, 55, id(robotofont_small), "|");
        }
      }

The problem is that, if I have a power outage, it starts from zero, so set an input.number in home assistant:

  • Every time the value of the counter changes, it updates this input number with its value.
  • If there is a power outage, on boot, the ESP32 should take this value from Home Assistant and start counting from this number.

Besides, it’s not working; the input number is not being updated and if I unplug the device and plug it again it starts from 0.

Could anybody please help me?

You should import the input number from Home Assistant into the device, using this component: Home Assistant Sensor — ESPHome

Then on boot of the esp you should publish the state of the input number to the water sensor. Essentially, exactly the other way around from what you’re doing now.

However, it may be worth taking a look at using a number component: Template Number — ESPHome
These have a restore value setting which should also allow for the esp to restore the state after booting.

1 Like

So I should add a new sensor to esp32:

sensor:

  • platform: homeassistant
    id: total_consumo_agua_global
    entity_id: input_number.total_consumo_agua_global

and then modify the begining to:

esphome:
name: esphome-web-71afe8
on_boot:
priority: -10
then:
- lambda: |-
id(total_agua).publish_state(id(total_consumo_agua_global).state);

Is it right? Sorry but I know very little of esp32 and I’m getting help from AI

From what I can see, that should work yes. You should however remove the service call (homeassistant.service) from your on_boot automation.

A good way to test if it works is to set the input number to a random value in Home Assistant, and see if the esp indeed sets the sensor to that value at the start. I’d try setting the priority at -100 though.
If it doesn’t work, could you post the output of the logs?

No. Doesn’t work. As soon as it boots, it starts adding pulses (but water is closed) until a random number and then stops, but doesn’t fit the number I set in the input number. I attach the log:

https://justpaste.it/fsgqf

hmm, that it would be weird that it’s already measuring pulses. Could you try putting the display reset pin to a different pin? Using gpio0 can lead to problems since it’s a strapping pin. This site has a handy overview of which pins do what: ESP32 Pinout Reference: Which GPIO pins should you use? | Random Nerd Tutorials

Another thing you could try is, instead of on_boot, use the api_connected trigger: Native API Component — ESPHome
And use an if statement to only update the sensor to the input number when the ip is equal to your homeassistant instance.

api:
  # ...
  on_client_connected:
    - if:
        condition:
          lambda: 'return client_address.c_str() == "x.x.x.x";
       then:
        - lambda: |-
              id(total_agua).publish_state(id(total_consumo_agua_global).state);

I freehanded this a bit, so keep in mind the indentation and stuff may not be fully correct.

No, the screen was soldered so it has to be in PIN 0.

I tried your sugestion and didn’t get the value either:

https://justpaste.it/bu18a

Could you post your current yaml config? Maybe I can find something in that, I don’t currently have the time to test it out on a device of my own.

Of course:

esphome:
  name: esphome-web-71afe8
  friendly_name: ESPHome Pantalla
#  on_boot:
#    priority: -100
#    then:
#      - lambda: |-
#          id(total_agua).publish_state(id(total_consumo_agua_global).state);

esp32:
  board: esp32dev
  framework:
    type: arduino

# Enable logging
logger:
  level: DEBUG

# Enable Home Assistant API
api:
  encryption:
    key: "****"
  services:
    - service: set_total_agua
      variables:
        value: float
      then:
        - lambda: |-
            id(total_agua).publish_state(value);

    - service: update_total_agua
      then:
        - lambda: |-
            id(total_agua).publish_state(id(total_agua).state);
  on_client_connected:
    - if:
        condition:
          lambda: 'return client_address.c_str() == "192.168.0.128";'
        then:
          - lambda: |-
              id(total_agua).publish_state(id(total_consumo_agua_global).state);

ota:

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "****"
    password: "****"

captive_portal:

sensor:
  - platform: pulse_counter
    pin: 
      number: 19
      allow_other_uses: true
    update_interval : 6s
    name: "pulso agua"
    id: pulso_agua
    filters:
      - multiply: 0.1

  - platform: pulse_meter
    pin: 
      number: 19
      allow_other_uses: true
    name: "Medidor pulsos agua"
    unit_of_measurement: "litros/min"
    icon: "mdi:water"
    total:
      name: "Total Agua"
      unit_of_measurement: "litros"
    filters:
      - multiply: 0.1

  - platform: template
    name: "Flujo de agua"
    id: flujo_agua
    accuracy_decimals: 1
    unit_of_measurement: "l/min"
    icon: "mdi:water"
    lambda: return (id(pulso_agua).state * 1);
    update_interval: 6s

  - platform: homeassistant
    id: total_agua
    entity_id: sensor.esphome_web_71afe8_total_agua
    on_value:
      then:
        - homeassistant.service:
            service: input_number.set
            data:
              entity_id: input_number.total_consumo_agua_global
              value: !lambda 'return x;'

  - platform: homeassistant
    id: total_consumo_agua_global
    entity_id: input_number.total_consumo_agua_global


  - platform: wifi_signal
    name: "WiFi ESP"
    update_interval: 60s
    id: intensidad_wifi_esp

font:
  - file: "fonts/roboto.ttf"
    id: robotofont
    size: 15

  - file: "fonts/roboto.ttf"
    id: robotofont_large
    size: 25
  - file: "fonts/roboto.ttf"
    id: robotofont_small
    size: 12
    
i2c:
  sda: GPIO21
  scl: GPIO22

# Añadir el componente de pantalla OLED
display:
  - platform: ssd1306_i2c
    model: "SSD1306 128x64"
    address: 0x3C
    reset_pin: GPIO0
    lambda: |-
      // Imprime "Consumo Total:"
      it.print(0, 0, id(robotofont), "Consumo Total:");
      // Imprime el valor del sensor "total_agua"
      if (id(total_agua).has_state()) {
        it.printf(0, 15, id(robotofont_large), "%.0f", id(total_agua).state);
      }
      // Imprime "Wifi:" un poco más arriba
      it.print(0, 50, id(robotofont), "Wifi:  ");
      // Imprime la intensidad de la señal WiFi como una barra horizontal más gruesa
      if (id(intensidad_wifi_esp).has_state()) {
        float signal_strength = min(max(2 * (id(intensidad_wifi_esp).state + 100.0), 0.0), 100.0);
        for (int i = 0; i < signal_strength / 10; i++) {
          it.print(35 + i * 10, 50, id(robotofont_small), "|");
          it.print(35 + i * 10, 55, id(robotofont_small), "|");
        }
      }

Okay I think this part could be causing the problem. I’d try putting a condition on it that the API has to be connected to Home Assistant.

Something like this?:

  - platform: homeassistant
    id: total_agua
    entity_id: sensor.esphome_web_71afe8_total_agua
    on_value:
      then:
        - if:
            condition:
              api.connected:
            then:
              - homeassistant.service:
                  service: input_number.set
                  data:
                    entity_id: input_number.total_consumo_agua_global
                    value: !lambda 'return x;'

Sorry if being too dumb but I don’t dominate this part at all.

Yeah I think that should work!

Also no worries, we all have to start somewhere with learning. We’ll get to a solution eventually:D

Thanks for your help. I entered it and doesn’t work either. It doesn’t get the number from the input number in home assistant at boot, but I don’t know why

hmm that’s odd indeed. This started boggling my mind more than I initially expected, but I think I found a thread that deals with a similar problem:

So the idea is to use global variables, because those can be restored upon boot:

And I think then, if you want to show it in the front end, you can use a template sensor that simply returns the global, or you can use the on_boot automation we discussed before, and publish the value of the global. I thought of something like this for the configuration of the global variable:

globals:
  - id: total_agua_var
    type: float
    restore_value: yes
    initial_value: '0'

And then use use the pulse sensor to update the global:

  - platform: pulse_meter
    pin: 
      number: 19
      allow_other_uses: true
    name: "Medidor pulsos agua"
    unit_of_measurement: "litros/min"
    icon: "mdi:water"
    id: total_agua_meter
    total:
      id: total_agua
      name: "Total Agua"
      unit_of_measurement: "litros"
    filters:
      - multiply: 0.1
    on_value:
      then:
        - lambda: |-
            id(total_agua_var) = id(total_agua).state;
            ESP_LOGD(DEBUG, "Updated water consumption global variable to %f", id(total_agua_var));

And the automation like:

  on_boot:
   then:
    - pulse_meter.set_total_pulses:
                id: total_agua_meter
                value: !lambda 'return id(total_agua_var);'

I hope this works out of the box. It might take some playing around with the configuration though to get everything to play together nicely.

Well, I may be missing something. I cleaned the config and added what you suggested:

esphome:
  name: esphome-web-71afe8
  friendly_name: ESPHome Pantalla
  on_boot:
   then:
    - pulse_meter.set_total_pulses:
                id: total_agua_meter
                value: !lambda 'return id(total_agua_var);'

esp32:
  board: esp32dev
  framework:
    type: arduino

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "***"

globals:
  - id: total_agua_var
    type: float
    restore_value: yes
    initial_value: '0'

ota:

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "***"
    password: "***"

captive_portal:

sensor:
  - platform: pulse_counter
    pin: 
      number: 19
      allow_other_uses: true
    update_interval : 6s
    name: "pulso agua"
    id: pulso_agua
    filters:
      - multiply: 0.1

  - platform: pulse_meter
    pin: 
      number: 19
      allow_other_uses: true
    name: "Medidor pulsos agua"
    unit_of_measurement: "litros/min"
    icon: "mdi:water"
    id: total_agua_meter
    total:
      id: total_agua
      name: "Total Agua"
      unit_of_measurement: "litros"
    filters:
      - multiply: 0.1
    on_value:
      then:
        - lambda: |-
            id(total_agua_var) = id(total_agua).state;
            ESP_LOGD(DEBUG, "Updated water consumption global variable to %f", id(total_agua_var));

  - platform: template
    name: "Flujo de agua"
    id: flujo_agua
    accuracy_decimals: 1
    unit_of_measurement: "l/min"
    icon: "mdi:water"
    lambda: return (id(pulso_agua).state * 1);
    update_interval: 6s

  - platform: homeassistant
    id: total_agua_ha
    entity_id: sensor.esphome_web_71afe8_total_agua
    on_value:
      then:
        - homeassistant.service:
            service: input_number.set
            data:
              entity_id: input_number.total_consumo_agua_global
              value: !lambda 'return x;'

  - platform: wifi_signal
    name: "WiFi ESP"
    update_interval: 60s
    id: intensidad_wifi_esp

font:
  - file: "fonts/roboto.ttf"
    id: robotofont
    size: 15

  - file: "fonts/roboto.ttf"
    id: robotofont_large
    size: 25
  - file: "fonts/roboto.ttf"
    id: robotofont_small
    size: 12
    
i2c:
  sda: GPIO21
  scl: GPIO22

# Añadir el componente de pantalla OLED
display:
  - platform: ssd1306_i2c
    model: "SSD1306 128x64"
    address: 0x3C
    reset_pin: GPIO0
    lambda: |-
      // Imprime "Consumo Total:"
      it.print(0, 0, id(robotofont), "Consumo Total:");
      // Imprime el valor del sensor "total_agua"
      if (id(total_agua).has_state()) {
        it.printf(0, 15, id(robotofont_large), "%.0f", id(total_agua).state);
      }
      // Imprime "Wifi:" un poco más arriba
      it.print(0, 50, id(robotofont), "Wifi:  ");
      // Imprime la intensidad de la señal WiFi como una barra horizontal más gruesa
      if (id(intensidad_wifi_esp).has_state()) {
        float signal_strength = min(max(2 * (id(intensidad_wifi_esp).state + 100.0), 0.0), 100.0);
        for (int i = 0; i < signal_strength / 10; i++) {
          it.print(35 + i * 10, 50, id(robotofont_small), "|");
          it.print(35 + i * 10, 55, id(robotofont_small), "|");
        }
      }

And got this error:

ERROR Circular dependency detected! Please run with -v option to see what functions failed to complete.

Edit: don’t know how to run with -v option as I’m using ESPHome in Home Assistant.

I ran it like that, and it seems like esphome treats total_agua like a variable? I have no clue why however. The logs would simply print the following over and over again:

DEBUG Waiting for variable total_agua
DEBUG Running to_code in esphome.components.pulse_meter.sensor (num 11)

The sensor as defined below would compile. However logic wise you’d have to test if it works. I’m mainly not sure if it would follow the correct state, so you’d have to check that.

  - platform: pulse_meter
    pin: 
      number: 19
      allow_other_uses: true
    name: "Medidor pulsos agua"
    unit_of_measurement: "litros/min"
    icon: "mdi:water"
    id: total_agua_meter
    total:
      #id: total_agua
      name: "Total Agua"
      unit_of_measurement: "litros"
    filters:
      - multiply: 0.1
    on_value:
      then:
        - lambda: |-
            if (id(total_agua_var) != id(total_agua_meter).state) {
              id(total_agua_var) = id(total_agua_meter).state;
              ESP_LOGD("DEBUG", "Updated water consumption global variable to %f", id(total_agua_var));
            }

Yes, this way it compiles but it keeps refusing to restore previous value of the global we set.

What’s more, if I try to use the service I set:

service: esphome.esphome_web_71afe8_set_total_agua
data:
  value: 282648.9

it is sent but as long as I open the water, the value jumps to a number that I don’t know where it came.

Can you post the logs again? It may also be an idea to compile it with

logger:
  level: VERBOSE

It may give more insights in what is going on. I assume somewhere an automation is working back on itself, perhaps. Also, I assume you mean the sensor value is the one that is jumping?

Of course, and thanks again for taking so much time in helping me:

https://justpaste.it/e7ti0

Could you post it again, but this time when opening the water? The pulse meter hasn’t updated here so it’s not possible to figure out what happens with it when it updates.

Best would be to post logs where you first call the service in home assistant, and then open the water so we can inspect the number and its origins.