How to send an array of strings from HA to esphome

I’m trying to send a list of upcoming events from a service call in Home Assistant to an esp32 running esphome.

I can’t for the life of me figure out how to do this. Even the initial value does not work

  services:
    - service: send_upcomingevents
      variables:
        thelist: string[]
        #thelist: std::vector<std::string> #fails compile
      then:
        - logger.log: thelist
        - globals.set:
            id: globalupcomingevents
            value: !lambda 'return thelist;'

globals:
  - id: globalupcomingevents
    type: std::vector<std::string>
    #type: string[]
    restore_value: no
    initial_value: "{'ASD:123', 'QQQ:456', 'WWW:789'}"


display:
  - platform: waveshare_epaper
    id: eink_display
    cs_pin: GPIO15
    dc_pin: GPIO27
    busy_pin:
      number: GPIO25
      #inverted: true
    reset_pin: GPIO26
    reset_duration: 2ms
    model: 7.50inv2b
    update_interval: never
    rotation: 90°
    lambda: |-
      std::map<std::string, std::string> glyph_map
        {
          {"birthday", "\U000F00EB"},
          {"anniversary", "\U000F02E8"},
          {"event", "\U000F00F5"},
          {"checkboxempty", "\U000F0131"},
          {"checkboxchecked", "\U000F0135"},
          {"anniversary", "\U000F02E8"},
          {"wifi", "\U000F05A9"},
          {"wifi0", "\U000F092B"},
          {"wifi1", "\U000F091F"},
          {"wifi2", "\U000F0922"},
          {"wifi3", "\U000F0925"},
          {"wifi4", "\U000F0928"}
        };

      if (id(display_mode).state == "Upcoming Events") {
        for (int i = 0; i < sizeof(id(globalupcomingevents)); i++) {
          it.printf(240, 380 + (i * 20), id(font_head_smaller), color_red, TextAlign::TOP_CENTER, "%s", globalupcomingevents[i].c_str());
        }

Anyone have any ideas?

What I usually do is to have a sensor in HA with text and a delimiter.
Then in ESP-Home I split the text on the delimiter.

HI @Hellis81 ,

I am stuck with something similar. I managed to get the events of today in a calendar and “hand it over to ESPHome”.

I than wanted to print them on my epaper.

The dates are in calendar_scheduled_events

and this code

        int maxdates = atoi(id(calendar_scheduled_events).state.c_str());
        for (int i = 0; i < maxdates; i++) {
          it.printf(50 + (i * 5), 302 + (i * 40), id(font_medium_bold), TextAlign::TOP_LEFT, "%3.2f TEST", i);
        }

prints as many lines as I have events on this day. (Just to see if it could work)

But I have absolutely zero idea on how to print the events, like I see them in HomeAssistant like this

screen-2024-05-16-18-50-58

I was hoping do to something like

        int maxdates = atoi(id(calendar_scheduled_events).state.c_str());
        for (int i = 0; i < maxdates; i++) {
          it.printf(50 + (i * 5), 302 + (i * 40), id(font_medium_bold), TextAlign::TOP_LEFT, "%3.2f TEST", id(calendar_scheduled_events[i]).state.c_str());
        }

but this obviously does not work.

text_sensor:
# get the calendar data over to ESP
  - platform: homeassistant
    id: calendar_scheduled_events
    entity_id: sensor.calendar_scheduled_events
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'
        - lambda: |-
            ESP_LOGI("main", "Scheduled Events: %s", id(calendar_scheduled_events).state.c_str());
            id(data_updated) = true;

is how I get the data in ESPHome

thanks
Juergen

Yours is actually a different question although trying to achieve the same result. What is the value in the text sensor? You’ll most likely have to read off the data and then manipulate it.

I’ve been searching for the answer for a long time and frankly Hellis81’s answer right off the bat demoralised me because it basically confirmed all I’ve been finding so far.

So I’ve taken his advice but kept to my guns a little. It would have been great to be able to send an array/list or even json (which was my first attempt but the json lib is not within the scope of the lambda in display).

Here’s what I’ve done

#define the global var to be updated
globals:
  - id: globalupcomingevents
    type: std::string
    restore_value: yes

#expose the service on the device
api:
  #... other stuff goes here
  services:
    - service: send_upcomingevents
      variables:
        thelist: string
      then:
        - globals.set:
            id: globalupcomingevents
            value: !lambda 'return thelist;'

display:
  - platform: waveshare_epaper
    #... other stuff goes here
    lambda: |-
        //... other stuff goes here
        /*
          I then format the string with the relevant data in
          node-red (much easier) and send it to the device
          by calling the service.

          The string is formatted as:
          "yesterday|bday| |Person A;tomorrow|gevt|18:00|Dinner with Wife;"
          which will mean
          1. yesterday, birthday of person A
          2. tomorrow @ 6pm, dinner with wife (generic event, for icons)
        */
        //... other stuff goes here
        
        char * upcomingevents = const_cast<char*>(id(globalupcomingevents).c_str());
        
        const char eventline_delim[] = ";";
        const char eventdetails_delim[] = "|";
        char * eventline_ptr = NULL;
        char * eventdetails_ptr = NULL;

        char * evtline = strtok_r(upcomingevents, eventline_delim, &eventline_ptr);
        while (evtline != NULL)
        {
          ESP_LOGD("custom", "Event Line: %s", evtline); //the entire line
          char * evtdetails = strtok_r(evtline, eventdetails_delim, &eventdetails_ptr);
          while (evtdetails != NULL) {
            ESP_LOGD("custom", "Event Details: %s", evtdetails); //one event, with details coming piece by piece
            evtdetails = strtok_r(NULL, eventdetails_delim, &eventdetails_ptr);
          }
          evtline = strtok_r(NULL, eventline_delim, &eventline_ptr);
        }
        ESP_LOGD("custom", "The original global variable now is: %s", id(globalupcomingevents).c_str());

You’ll see an entire event line being printed in the logs first then you’ll see the details. I’m going to add a few more local variables to organise it and then print it out line by line (track the day header etc); but the data is all there now.

I only have to make sure the wife doesn’t put the | or ; symbol in her descriptions

HI @fusionstream

Can you post here the HA configuration.yaml entry, that puts together the data?

I digged around for 3 days in the mud, and IMHO it is a mixture between very old configs, and “too damn easy” if you know how to do it :slight_smile:

thank you
Juergen

Hi @fusionstream ,
I assume the data in the textstring has all the data that is shown in the screenshot. But I have no idea on how to test, what data it really contains. This is exactly my problem. If I for debugging purtposes I need to know which data structure is there, I would know how to do it on tzhe epaper, but as I have zero idea what the ESP32 is getting (Except the correct amount of data transferred), I habe no idea on how to debug it.
I am searching for a function that I have in perl :slight_smile: like use Data::Dump qw(dump); where I simply throw in any variable, and it dumbs it in a readable way to wherever I want it.
This would help me a lot, as I than would not quess if everything is there, but know it is or it isn’t and could tweak my configuration.yaml

thanks
Juergen

I’m using node red to get calendar events from HA and then formatting it all there, then sending it to the device by calling it’s service.

If the service you expose to HA specifies a string, then you will be receiving a string. HA will not allow you to send anything else (will show an error).

Since you’re receiving a string you can actually just log what you’re getting:

api:
  #... other stuff goes here
  services:
    - service: send_upcomingevents
      variables:
        thelist: string
      then:
        - globals.set:
            id: globalupcomingevents
            value: !lambda 'return thelist;'

        #- lambda: |-
        #    ESP_LOGD("custom", "The value received is: %s", thelist.c_str());

        #- lambda: |-
        #    ESP_LOGD("custom", "The value received is: %s", id(globalupcomingevents).c_str());

See the last 2 commented blocks.

The first logs the received string (you specified a string so you get a string); The second logs the value of the global variable that I set with the string I got.

It is helpful to remember that because it’s not possible to send arrays/lists, we send strings. We then format the string in a way that we can then later parse and “tokenise”.

It may be helpful to you to base your work of the following project as your flow is fundamentally different to mine.

You may also check out this project which I’ve initially based mine off though it now differs significantly.

HI,
I am coming from this project, and stripped it down to something I could understand :slight_smile:

I am still not sure, if I do the things “just right, as they are expected”, or in the way they “just work”. Debuggin ESP is the hardest, as I could nod find a way to do it, except prints…

thank you
Juergen

Hi @fusionstream,

I use GitHub - franc6/ics_calendar: Provides an ICS (icalendar) platform for the Home Assistant calendar Calendar to get my personal M$ Calender data from this date. This way HA is “doing the job” for me. I have no idea, if I than could do what you suggest.
My “calender journey” starts here:
ICS Calendar platform

the HA part is pretty streght forward and works. I also assume that I have all data in the ESP, but I still have no clue on how to get it. At least I can dynamically show how MANY appointments I have on this day :slight_smile:

thanks
Juergen

Hi @fusionstream ,
many many thanks for this, I check it out. But I do want to understand, how I can manipulate the data best (I guess in the HA), as I want to make “room occupation sign” for some of our office rooms. An need to clear out some data. (If a room is blcoked from
8:00-9:00 and from 9:00-10:00 I would love to show blocked from 8:00-10:00, and I think I am more flexible in HA as memory is not the limit there.

thak you
Juergen

ESP_LOGD or logger.log is your answer

As for your conditionals, you could do it esphome with a conditional there, a normal c++ if/else in a lambda, or you could simply display a string (BLOCKED/AVAILABLE) which is defined from HA or from node-red.