ESP does not run a homeassistant event on boot after api is connected

Hi,
I am trying to make a room occupation picture frame with an ePaper, that shows the occupation of an Office calendar for this room and has a physical button to book the next free slot. Later on I am planning to put the ESP in deepsleep, but this is going to be another story. At the moment I am trying to understand how I can “see” in HomeAssistant that the ESP started, do some calculations in HomeAssistant the let the ESP refresh its screen.

My basic idea, to keep the ESP “as dumb as possible”, and do all calculations of free slots, calendar data, … in HomeAssistant, which has the nice side effekt, that I can have a user interface in HomeAssistant as well.

What happens during boot:
If the ESP is restarted by “power on” it prints “Warte auf Daten” which is the default behaviour and “ok in general”, but it should than call the
esphome.${rmgr_device_name}.booted
and
event: esphome.${rmgr_device_name}_refresh_clicked

I use here

api:
  on_client_connected:
    then:
      - logger.log: "Connected to HomeAssistant."
      - homeassistant.event:
          event: esphome.${rmgr_device_name}.booted
      - logger.log: "simulte refresh to refresh the screen on first connect"
      - homeassistant.event:
          event: esphome.${rmgr_device_name}_refresh_clicked

I see the logger.log events on the ESP console, but in HomeAssistant I cannot see the event fired in the developer tools if I subscribe to
esphome.framery1_refresh_clicked
e.g. . If I run the esphome.framery1_refresh_clicked manually in the developer tools on the HomeAssistant everything works as expected. (Screen on ePaper is updated, and the subscription show data.)
Any ideas? I am totally stuck.

Here the full code from the ESPHome. If HA yaml code is needed, just tell me. (In HA I get the data from the calendar, calculate a free slot and give all data to the epaper ESP, which works, “only” the firsst start does not work, which makes it impossible to run in deep sleep and wake up, which is what I tested yesterday. So this behaviour above, is what I nailed it down to.

# Calendar / Free Busy DASHBOARD
#  For Home Assistant and ESPHome
# Inspired by WEATHERMAN by Madelena Mak 2022 - https://mmak.es
#
#######################
# VARIABLES passed into the system:
# - rmgr_device_name: the common prefix for sensors etc (still a bad name)
#
# ESPHome for battery:
# https://ncrmnt.org/2021/12/06/optimizing-esp8266-esphome-for-battery-power-and-making-an-ice-bath-thermometer-as-well/

# Core Configuration Section: https://esphome.io/components/esphome.html
esphome:
  # https://esphome.io/components/esphome.html#changing-esphome-node-name
  name: "${rmgr_device_name}"
  # name is defined in specific instance config (e.g. framery1.yaml etc)

  # https://esphome.io/components/esphome.html#on-boot
  # on_boot is an *Automation* (https://esphome.io/automations/#automation)
  on_boot: # == "Trigger"
      priority: 200.0
      then: # == "Actions"
        - component.update: epaper_screen_print
        - logger.log: "Forcing API reconnect after boot..."
        - delay: 5s

        # this triggers a reload in Home Assistant (which pushes the data down to the device)
#        - homeassistant.event:
#            event: "esphome.${rmgr_device_name}.booted"

# https://esphome.io/components/esp32.html
esp32:
  # "if unsure choose a generic board from Espressif such as esp32dev."
  board: esp32dev
  framework:
    type: arduino


# Enable logging
# https://esphome.io/components/logger.html
logger: 
#  level: NONE
#  level: ERROR
#  level: WARN
#  level: INFO
  level: DEBUG
#  level: VERBOSE
#  level: VERY_VERBOSE



# Global variables for detecting if the display needs to be refreshed. (Thanks @paviro!)
# https://esphome.io/components/globals.html
globals:
  - id: initial_data_received
    type: bool
    restore_value: no
    initial_value: 'false'

  - id: headline_text
    type: std::string
    restore_value: no
  - id: subheadline_text
    type: std::string
    restore_value: no

  - id: timeslots
    type: std::vector<std::string>
    restore_value: no
  - id: is_occupied
    type: std::vector<bool>
    restore_value: no
  - id: rmgr_booking_slot_length_minutes
    type: std::string
    restore_value: no
  - id: last_display_refresh
    type: std::string
    restore_value: no
# Enable Home Assistant API
# https://esphome.io/components/api.html
api:
  on_client_connected:
    then:
      - logger.log: "Connected to HomeAssistant."
      - homeassistant.event:
          event: esphome.${rmgr_device_name}.booted
      - logger.log: "simulte refresh to refresh the screen on first connect"
      - homeassistant.event:
          event: esphome.${rmgr_device_name}_refresh_clicked
  services:
    - service: "draw_schedule"
      variables:
        param_headline_text: string
        param_subheadline_text: string
        # !!! you need the same number of entries in param_timeslots and param_is_occupied
        param_timeslots: string[]
        param_is_occupied: bool[]
        param_rmgr_booking_slot_length_minutes: string
        param_last_display_refresh: string

      then:
        - lambda: 'id(initial_data_received) = true;'
        - lambda: 'id(headline_text) = param_headline_text;'
        - lambda: 'id(subheadline_text) = param_subheadline_text;'
        - lambda: 'id(timeslots) = param_timeslots;'
        - lambda: 'id(is_occupied) = param_is_occupied;'
        - lambda: 'id(rmgr_booking_slot_length_minutes) = param_rmgr_booking_slot_length_minutes;'
        - lambda: 'id(last_display_refresh) = param_last_display_refresh;'
        - logger.log: ".-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. calfreebusy_draw_schedule Data"
        - logger.log:
            format: "param_headline_text has value >%s<"
            args: [ 'id(headline_text).c_str()' ]
        - logger.log:
            format: "param_subheadline_text has value >%s<"
            args: [ 'id(subheadline_text).c_str()' ]
        - logger.log:
            format: "param_timeslots has value >%i<"
            args: [ 'id(timeslots)' ]
        - component.update: epaper_screen_print

# https://esphome.io/components/ota/esphome
ota:
  - platform: esphome

# ota turns safe_mode on, which we do not want.
safe_mode:
  boot_is_good_after: 30s

# Wifi information
wifi:
  networks:
#    channel: 11
    ssid: !secret wifi_ssid
    password: !secret wifi_password



  # Enable fallback hotspot (captive portal) in case wifi connection fails
  # ausgeshcaltet, da es 
  # ap:
  #   ssid: !secret wifi_ssid
  #   password: !secret wifi_password


# Include custom fonts
font:
  - file: 'fonts/GothamRnd-Book.ttf'
    id: font_small_book
    size: 18
    glyphs: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'Ä', 'Ö', 'Ü', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', '-', ' ', '°', '.', '%', 'ä', 'ö', 'ü', 'ß', '(', ')']
  - file: 'fonts/GothamRnd-Bold.ttf'
    id: font_large_bold
    size: 108
    glyphs: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'Ä', 'Ö', 'Ü', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', '-', ' ', '°', '.','m', '%']
  - file: 'fonts/GothamRnd-Bold.ttf'
    id: font_title
    size: 54
    glyphs: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'Ä', 'Ö', 'Ü', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', '-', ' ', '°', '.','m', 'u', 'p', 'd', 't', '%']
  - file: 'fonts/GothamRnd-Bold.ttf'
    id: font_medium_bold
    size: 30
    glyphs: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'Ä', 'Ö', 'Ü', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', '-', ' ', '°', '.', '%', 'ä', 'ö', 'ü', 'ß', '(', ')']
  - file: 'fonts/GothamRnd-Bold.ttf'
    id: font_small_bold
    size: 18
    glyphs: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'Ä', 'Ö', 'Ü', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', '-', ' ', '°', '.','m', '%']

  # Include Material Design Icons font
  # Thanks to https://community.home-assistant.io/t/display-materialdesign-icons-on-esphome-attached-to-screen/199790/16
  - file: 'fonts/materialdesignicons-webfont.ttf'
    id: font_mdi_medlarge
    size: 18
    glyphs:
      - "\U000F05A9" # mdi-wifi
      - "\U000F0928" # mdi-wifi-strength-4
      - "\U000F0925" # mdi-wifi-strength-3 
      - "\U000F0922" # mdi-wifi-strength-2
      - "\U000F091F" # mdi-wifi-strength-1
      - "\U000F092B" # mdi-wifi-strength-alert-outline


# Check if epaper should be refreshed

sensor:
  
  - platform: wifi_signal
    name: "${rmgr_device_name} - WiFi Signal Strength"
    id: "${rmgr_device_name}_wifisignal"
    unit_of_measurement: "dBm"
    entity_category: "diagnostic"
    update_interval: 600s

binary_sensor:
# refresh durch HomeAssistant
  - platform: homeassistant
    entity_id: input_boolean.${rmgr_device_name}_refresh_clicked
    id: update_epaper_display
# book durch Button am ESP
  - platform: gpio
    name: "${rmgr_device_name}_book_now"
    pin:
      number: GPIO16
      inverted: true
      mode:
        input: true
        pullup: true
    filters:
      - delayed_on: 10ms
    on_multi_click:
      - timing:
          - ON for at most 1s
          - OFF for at most 1s
          - ON for at most 1s
          - OFF for at least 0.2s
        then:
          - logger.log: "Double Clicked"
          - homeassistant.event:
              event: esphome.${rmgr_device_name}_booknow_doubleclicked
      - timing: # INFO: this is currently not used functionally.
          - ON for 1s to 2s
          - OFF for at least 0.5s
        then:
          - logger.log: "Single Long Clicked"
          - homeassistant.event:
              event: esphome.${rmgr_device_name}_booknow_longclicked
      - timing:
          - ON for at most 1s
          - OFF for at least 0.5s
        then:
          - logger.log: "Single Short Clicked"
          - homeassistant.event:
              event: esphome.${rmgr_device_name}_booknow_clicked

## refresh durch Button am ESP
# Commented out, as this iwll later on be GPIO12 to wake the ESP up and
# during wakeup the refresh will happen.
#  - platform: gpio
#    name: "${rmgr_device_name}_refresh"
#    pin:
#      number: GPIO17
#      inverted: true
#      mode:
#        input: true
#        pullup: true
#    filters:
#      - delayed_on: 10ms
#    on_multi_click:
#      - timing:
#          - ON for at most 1s
#          - OFF for at most 1s
#          - ON for at most 1s
#          - OFF for at least 0.2s
#        then:
#          - logger.log: "Double Clicked"
#          - homeassistant.event:
#              event: esphome.${rmgr_device_name}_refresh_doubleclicked
#      - timing: # INFO: this is currently not used functionally.
#          - ON for 1s to 2s
#          - OFF for at least 0.5s
#        then:
#          - logger.log: "Single Long Clicked"
#          - homeassistant.event:
#              event: esphome.${rmgr_device_name}_refresh_longclicked
#      - timing:
#          - ON for at most 1s
#          - OFF for at least 0.5s
#        then:
#          - logger.log: "Single Short Clicked"
#          - homeassistant.event:
#              event: esphome.${rmgr_device_name}_refresh_clicked



# Pins for Waveshare ePaper ESP Board
spi:
  clk_pin: GPIO13                 # SCLK 	P13 	SPI CLK pin, clock signal input
  mosi_pin: GPIO14                # DIN 	P14 	SPI MOSI pin, data input

# Now render everything on the ePaper screen.
display:
  - platform: waveshare_epaper
# models from https://esphome.io/components/display/waveshare_epaper
#    model: 7.50inV2   # s/w board   evtl. älterer Treiber ???
#    model: 7.50in-nV2   # s/w board   
    model: 7.50in-bV3  # r/s/w board

    id: epaper_screen_print
    cs_pin: GPIO15                # CS 	P15 	Chip selection, low active
    dc_pin: GPIO27                # DC 	P27 	Data/command, low for commands, high for data
    busy_pin: 
      number: GPIO25              # BUSY 	P25 	Busy status output pin (means busy) 
      inverted: true
    reset_pin: GPIO26             # RST 	P26 	Reset, low active
    reset_duration: 2ms

    update_interval: never
    rotation: 90°
    lambda: |-
      int offsetY = 40;
      int offsetX = 0;
      
      //# Print loading screen before data is received.
      if (id(initial_data_received) == false) {
        it.printf(240, 390, id(font_small_bold), TextAlign::TOP_CENTER, "WARTE AUF DATEN");
      } else { 

        // headline
        int lineHeight = 35;
        it.printf(50, offsetY, id(font_medium_bold), TextAlign::TOP_LEFT, "%s", id(headline_text).c_str());
        offsetY += lineHeight;
        
        // subheadline
        lineHeight = 22;
        it.printf(150, offsetY, id(font_small_book), TextAlign::TOP_LEFT, "%s", id(subheadline_text).c_str());
        offsetY += lineHeight;

        lineHeight = 22;
        for (size_t i = 0; i < id(timeslots).size(); ++i) {
          offsetY += lineHeight;
          it.printf(50, offsetY, id(font_small_book), TextAlign::TOP_LEFT, "%s", id(timeslots)[i].c_str());
          if (id(is_occupied)[i]) {
            it.filled_rectangle(120, offsetY, 100, lineHeight);
          }
        }
      }



      //# decide for the WiFi-signal Symbol
      //# is printed printed below
      std::string wifiSymbol = "";
      offsetX = it.get_width() - 50;
      offsetY = it.get_height() - 150;
      if(id(${rmgr_device_name}_wifisignal).has_state ()) {
        if (id(${rmgr_device_name}_wifisignal).state >= -50) {
            // Excellent
            wifiSymbol = "\U000F0928";
        } else if (id(${rmgr_device_name}_wifisignal).state  >= -60) {
            //Good
            wifiSymbol = "\U000F0925";
        } else if (id(${rmgr_device_name}_wifisignal).state  >= -67) {
            //Fair
            wifiSymbol = "\U000F0922";
        } else if (id(${rmgr_device_name}_wifisignal).state  >= -70) {
            //Weak
            wifiSymbol = "\U000F091F";
        } else {
            //Unlikely working signal
            wifiSymbol = "\U000F092B";
        }
      }

      // Print booking slot length + Last refresh + Wifi
      // we write from bottom to top 
      offsetX = it.get_width() - 50;
      offsetY = it.get_height() - 130;
      int lineHeight = 22;
      // Print DBI and Wifi Signal
      it.printf(offsetX - 25, offsetY, id(font_small_book), TextAlign::TOP_RIGHT, "%.0f dB", id(${rmgr_device_name}_wifisignal).state);
      it.printf(offsetX, offsetY, id(font_mdi_medlarge), TextAlign::TOP_RIGHT, "%s", wifiSymbol.c_str());
      // 
      it.printf(offsetX - 150, offsetY, id(font_small_book), TextAlign::TOP_RIGHT, "%s", id(rmgr_booking_slot_length_minutes).c_str());
      offsetY -= lineHeight;
      it.printf(offsetX, offsetY, id(font_small_book), TextAlign::TOP_RIGHT, "%s", id(last_display_refresh).c_str());


captive_portal:

HI,
solved by using

  - platform: status
    name: "${rmgr_device_name} Connected"
    id: ${rmgr_device_name}_connected

in the ESPHome and checking this ins HA.

works