Display w/ MQTT messages

I am working on a project for my garage. The function is for the following:

  1. Have an 8x32 ws2812 matrix display, and display messages from MQTT.
  2. Depending on the message received, the display will change and display the message. The MQTT messages will be triggered by external sensors which I do not have linked.
    I am doing all testing with the MQTT integration:

The sensors I will be using is a photoelectric switch like this one. This will be placed at a distance to clear the gate opening. If a car pulls up, and breaks the sensor and tried to open the gate, the gate will not open and the display will print a message.
I also want to have a green arrow, a red X, etc., depending on the case.

Example:

  1. When a car pulls up, the matrix is full solid red. button pressed, when gate is open, display shows green arrow.
  2. When car pulls up, break the sensor line, display turns red, prints back up for 1 second, then red display again, etc.

The code below prints a message on the display which is received from MQTT. This works well. So any message I send from MQTT under that topic will be displayed on the matrix. So far so good.

#Name the Project here to dentify the specific node
substitutions:
  project: Garage-LED-Sign-01
  id: garage-led-sign-01
  xscrollpadding: "4" # in pix
  
mqtt:
  broker: !secret mqtt_broker
  username: !secret mqtt_user
  password: !secret mqtt_pwd
  id: mqtt_client

      
      
#Include the board used  
<<: !include common/esphome/esp32.yaml
#include common Wifi-OTA-API, etc
<<: !include common/common.yaml

#Include the platform used for the hardware
<<: !include common/templates/standard_template.yaml

binary_sensor:
#Include the status 
  - !include common/binary_sensors/status.yaml
  - platform: gpio
    pin: GPIO34
    name: "PIR Sensor"
    device_class: motion
    id: garage_pir_sensor
    
    
switch:
  #Include restart button
  - !include common/switches/restart.yaml
  
sensor:
#Include wifi signal sensor
  - !include common/sensors/wifi_signal.yaml
  - !include common/sensors/uptime.yaml
 
text_sensor:
  - platform: mqtt_subscribe
    name: "Garage Sign Text"
    id: garage_led_sign_text
    topic: garage/led_sign/text
    
font:
  - file: "fonts/arial.ttf"
    id: text_font_tiny
    size: 10
  - file: "fonts/materialdesignicons-webfont.ttf"
    id: icon_font_40
    size: 35
    glyphs: [
      ó°Ÿ€, #mdi-molecule-co2
      󰟏, #mdi-molecule
      󰈏, #mdi-factory
      󰃹, #mdi-broom
      󰔃, #mdi-air-filter
      󰖙, #mdi-weather-sunny
      󰖗, #mdi-weather-rainy
      󰖕,#mdi-weather-partly-cloudy
      󰖘, #mdi-weather-snowy-heavy
      󰖐, #mdi-weather-cloudy
      󰌇, #mdi-error
      󰩖 #mdi-progress-clock
    ]

color:
  - id: my_red
    red: 100%
    green: 0%
    blue: 0%
  - id: my_green
    red: 0%
    green: 100%
    blue: 0%
  - id: my_blue
    red: 0%
    green: 0%
    blue: 100%
  - id: my_yellow
    red: 100%
    green: 100%
    blue: 0%
    
# Configure WS2815
light:
  - platform: fastled_clockless
    chipset: WS2812b
    id: led_matrix_light
    pin: GPIO5
    num_leds: 256
    rgb_order: GRB
    name: "Garage Traffic Sign"
    default_transition_length: 0s
    color_correct: [50%, 50%, 50%]
    restore_mode: ALWAYS_ON
    effects:
      - addressable_rainbow:
      - addressable_color_wipe:    
      - flicker:
      - strobe:
      - random:
      - addressable_scan:
      - addressable_twinkle:
      - addressable_random_twinkle:
      - addressable_fireworks:
      - addressable_flicker:
      - addressable_rainbow:
          name: Rainbow Effect With Custom Values
          speed: 10
          width: 50
          
display:
  - platform: addressable_light
    id: led_matrix_display
    addressable_light_id: led_matrix_light
    width: 32
    height: 8
    pixel_mapper: |-
      if (x % 2 == 0) {
        return (x * 8) + y;
      }
      return (x * 8) + (7 - y);
    rotation: 180°
    update_interval: 200ms
    pages:
      - id: page1
        lambda: |-
              if (id(mqtt_client)->is_connected()) {
                // do something if MQTT is connected
                static uint16_t xpos = 0;
                
                int x_start, y_start;
                int width, height;
                it.get_text_bounds(0, 0, "MQTT Connected", id(text_font_tiny), 
                    TextAlign::TOP_LEFT, &x_start, &y_start, &width, &height); 
                it.print(-(xpos % (width + $xscrollpadding)), -2, 
                  id(text_font_tiny), Color(id(my_blue)), 
                  TextAlign::TOP_LEFT, "MQTT Connected"); 
                xpos++;
              }
      - id: page2
        lambda: |-
              static uint16_t xpos = 0;
              const char * text = id(garage_led_sign_text).state.c_str();
              int x_start, y_start;
              int width, height;
              it.get_text_bounds(0, 0, text, id(text_font_tiny), 
                  TextAlign::TOP_LEFT, &x_start, &y_start, &width, &height); 
              it.print(-(xpos % (width + $xscrollpadding)), -2, 
                id(text_font_tiny), Color(id(my_red)), 
                TextAlign::TOP_LEFT, text); 
              xpos++;      

Thanks to @fribse for the scrolling code. The message scrolls and all works great.

Now I am trying to get the other section, which is have different screen display depending on the message received.
I thought about making pages, so one page is solid red, one is solid green, one is arrow, one is text, etc.
Then depending on the MQTT message received, I will basically show the page, delay, show the text scrolling, show another page, etc. with an interval.

Is this the best way of doing it?
I have been trying to get it working for a while now, and I am not sure where to put this code from the help page:
image
So the thought is trigger on a message received from MQTT, then read the message, and depending on the message text, load the first page in the animation.
then have a few interval code snippets, so depending on which page gets loaded from the code above, it will keep looping until a new message is received.

Not sure if this all makes sense.

Any thoughts or pointer?

EDIT:
I got this to compile

text_sensor:
  - platform: mqtt_subscribe
    name: "Garage Sign Text"
    id: garage_led_sign_text
    topic: garage/led_sign/text
    on_value:
      then:
        - if:
            condition:
              lambda: return x == "START";
            then:
              - display.page.show: page3
              - component.update: led_matrix_display
            else:
              - display.page.show: page2
              - component.update: led_matrix_display

I tested and it works. Changes the pages. So got this figured out. Now a question on the condition.

Is -if limited to one condition and one else? Is there a way I can make a switch statement like in standard C? To have multiple conditions for each of the text strings received from MQTT.
if START → page 1
if STOP → page 2
if GO → page 3
or a nested if → else if → else if → else

Any way I can test more than one condition?

After a few hours, I got a solution which works.
Still not sure if this is the best.

I created a template sensor to save the value from the MQTT message.
Then used the interval to check the value and run the animation of the page transitions
Page transitions can be extended to any pages, in any order. Idea came from here.
Here is the code if anyone is interested. If you have a better way, please let me know. Always up for learning a better way of doing something.

My code:

#Name the Project here to dentify the specific node
substitutions:
  project: Garage-LED-Sign-01
  id: garage-led-sign-01
  xscrollpadding: "4" # in pix
  
mqtt:
  broker: !secret mqtt_broker
  username: !secret mqtt_user
  password: !secret mqtt_pwd
  id: mqtt_client
      
#Include the board used  
<<: !include common/esphome/esp32.yaml
#include common Wifi-OTA-API, etc
<<: !include common/common.yaml

#Include the platform used for the hardware
<<: !include common/templates/standard_template.yaml

binary_sensor:
#Include the status 
  - !include common/binary_sensors/status.yaml
  - platform: gpio
    pin: GPIO34
    name: "PIR Sensor"
    device_class: motion
    id: garage_pir_sensor
    
switch:
  #Include restart button
  - !include common/switches/restart.yaml
  
sensor:
#Include wifi signal sensor
  - !include common/sensors/wifi_signal.yaml
  - !include common/sensors/uptime.yaml
 
text_sensor:
  - platform: mqtt_subscribe
    id: garage_led_sign_text
    internal: true
    topic: garage/led_sign/text
    on_value:
      then:
        - if:
            condition:
              lambda: return true;
            then:
              lambda: id(garage_led_text).publish_state(x);
  - platform: template
    id: garage_led_text
    internal: true
    
font:
  - file: 'fonts/arial.ttf'
    id: text_font_small
    size: 17
  - file: "fonts/arial.ttf"
    id: text_font_small_time_temp
    size: 15
    glyphs:  ['!', ',', '.', '"', '%', '-', '_', ':', 'Âș', '/','C',
       '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' ']
  - file: "fonts/arial.ttf"
    id: text_font_tiny
    size: 10
  - file: "fonts/arial.ttf"
    id: text_font_big
    size: 40
  - file: "fonts/materialdesignicons-webfont.ttf"
    id: icon_font_30
    size: 30
    glyphs: [
      ó°Ÿ€, #mdi-molecule-co2
      󰟏, #mdi-molecule
      󰈏, #mdi-factory
      󰃹, #mdi-broom
      󰔃, #mdi-air-filter
      󰖙, #mdi-weather-sunny
      󰖗, #mdi-weather-rainy
      󰖕,#mdi-weather-partly-cloudy
      󰖘, #mdi-weather-snowy-heavy 
      󰌇, #error
      󰖐, #mdi-weather-cloudy
      󰩖 #mdi-progress-clock
      
    ]
  - file: "fonts/materialdesignicons-webfont.ttf"
    id: icon_font_40
    size: 35
    glyphs: [
      ó°Ÿ€, #mdi-molecule-co2
      󰟏, #mdi-molecule
      󰈏, #mdi-factory
      󰃹, #mdi-broom
      󰔃, #mdi-air-filter
      󰖙, #mdi-weather-sunny
      󰖗, #mdi-weather-rainy
      󰖕,#mdi-weather-partly-cloudy
      󰖘, #mdi-weather-snowy-heavy
      󰖐, #mdi-weather-cloudy
      󰌇, #mdi-error
      󰩖 #mdi-progress-clock
    ]

color:
  - id: my_red
    red: 100%
    green: 0%
    blue: 0%
  - id: my_green
    red: 0%
    green: 100%
    blue: 0%
  - id: my_blue
    red: 0%
    green: 0%
    blue: 100%
  - id: my_yellow
    red: 100%
    green: 100%
    blue: 0%
  - id: my_black
    red: 0%
    green: 0%
    blue: 0%  
    
# Configure WS2815
light:
  - platform: fastled_clockless
    chipset: WS2812b
    id: led_matrix_light
    pin: GPIO5
    num_leds: 256
    rgb_order: GRB
    name: "Garage Traffic Sign"
    default_transition_length: 0s
    color_correct: [50%, 50%, 50%]
    restore_mode: ALWAYS_ON
    effects:
      - addressable_rainbow:
      - addressable_color_wipe:    
      - flicker:
      - strobe:
      - random:
      - addressable_scan:
      - addressable_twinkle:
      - addressable_random_twinkle:
      - addressable_fireworks:
      - addressable_flicker:
      - addressable_rainbow:
          name: Rainbow Effect With Custom Values
          speed: 10
          width: 50
          
display:
  - platform: addressable_light
    id: led_matrix_display
    addressable_light_id: led_matrix_light
    width: 32
    height: 8
    pixel_mapper: |-
      if (x % 2 == 0) {
        return (x * 8) + y;
      }
      return (x * 8) + (7 - y);
    rotation: 180°
    update_interval: 200ms
    pages:
      - id: page1
        lambda: |-
              if (id(mqtt_client)->is_connected()) {
                // do something if MQTT is connected
                static uint16_t xpos = 0;
                int x_start, y_start;
                int width, height;
                it.get_text_bounds(0, 0, "MQTT Connected", id(text_font_tiny), 
                    TextAlign::TOP_LEFT, &x_start, &y_start, &width, &height); 
                it.print(-(xpos % (width + $xscrollpadding)), -2, 
                  id(text_font_tiny), Color(id(my_blue)), 
                  TextAlign::TOP_LEFT, "MQTT Connected"); 
                xpos++;
              }
      - id: page2
        lambda: |-
              it.print(0, -2, id(text_font_tiny), Color(id(my_blue)), TextAlign::TOP_LEFT, "STOP");
      - id: page3
        lambda: |-
              // Draw the same rectangle, but this time filled.
              //it.filled_rectangle(0, 0, it.get_width(), it.get_height());    
              it.fill(my_red);
      - id: page4
        lambda: |-
              // Draw the same rectangle, but this time filled.
              //it.filled_rectangle(0, 0, it.get_width(), it.get_height());    
              it.fill(my_black);        
      - id: page5
        lambda: |-
              static uint16_t xpos = 0;
              const char * text = id(garage_led_sign_text).state.c_str();
              int x_start, y_start;
              int width, height;
              it.get_text_bounds(0, 0, text, id(text_font_tiny), 
                  TextAlign::TOP_LEFT, &x_start, &y_start, &width, &height); 
              it.print(-(xpos % (width + $xscrollpadding)), -2, 
                id(text_font_tiny), Color(id(my_green)), 
                TextAlign::TOP_LEFT, text); 
              xpos++;              
        
interval:        
  - interval: 2s
    then:
      if:
        condition: 
          lambda: |-
            return id(garage_led_text).state == "STOP";
        then:
          - display.page.show: !lambda |-
              static int pagenum = 0;
              static display::DisplayPage* pages[] = {id(page2), id(page3)};
              auto page = pages[pagenum++];
              if (pagenum >= 2)
                pagenum = 0;
              return page;
          - component.update: led_matrix_display
  - interval: 2s
    then:
      if:
        condition: 
          lambda: |-
            return id(garage_led_text).state == "START";
        then:
          - display.page.show: !lambda |-
              static int pagenum = 0;
              static display::DisplayPage* pages[] = {id(page5), id(page3)};
              auto page = pages[pagenum++];
              if (pagenum >= 2)
                pagenum = 0;
              return page;
          - component.update: led_matrix_display  
1 Like