Layout library?

There are many cool projects showing ESPHome based dashboards on LCD or e-paper screens, but they each use custom rendering code based on the core Display Component — ESPHome API.

Of course the holy grail would be to be able to render Lovelace dashboards to ESPHome displays, :slight_smile: but this is not currently possible and there does not seem to be any support planned for displaying dynamically loading images, for instance.

With this context in mind, has anyone done, or thought about, building a basic layout engine for ESPHome?

Even if it just provided relative coordinates in a card- or table-based layout, it might reduce the need to hardcode everything when building an ESPHome-based dashboard, and encourage reusable “cards”.

UPDATE: Based on comments below, both OpenHASP (Layout library? - #7 by nickrout) and SwitchPlate (Layout library? - #10 by fvanroie) are interesting approaches to the problem.

It’s impossible to fit a CSS + HTML rendering engine inside an ESP chip. Thus most layouts are very simple or rely on an AppDaemon statically screenshotting a browser.

Yes, I realize it’s impossible to fit a full rendering engine on ESP*, especially for something as complex as HTML+CSS. That’s why I’m talking about a layout library, that maybe wraps common functionality and perhaps maintains a small amount of state (but nothing like the complexity of HTML).

It could also have a code gen component, to automate a lot of the boilerplate.

Whether commonly used patterns are abstracted dynamically or by code gen is probably not so critical.

Regarding the screenshotting of a browser: in esphome, how would you load the resulting image? Dynamic image loading is not (yet) solved, is it?

One other possibility might be something like VNC. I’ve seen some ESP implementation of a VNC client but I don’t know if esphome has enough space left for such a client.

Maybe it’s possible to do a subset of HTML+CSS that renders Lovelace YAML acceptably, or a compiler that renders draw instructions (not pixels) server side and rends them to ESP, for faster interaction.

Some basic “automatic” layouting I do:

    pages:
      - id: page_tcs34725
        lambda: |-    

          it.set_rotation(id(display_rotation));

          // CANVAS DIMENSIONS // width: 128 // height: 64
          int w = it.get_width();
          int h = it.get_height();

          int y_plus = round(h / 7);
          int padding = round(h / 7);

          int x = 0;
          int y = y_plus;

          int wx = w - 10;

          // If rotated, split to 2 rows 
          if ( id(display_rotation) == DISPLAY_ROTATION_90_DEGREES || id(display_rotation) == DISPLAY_ROTATION_270_DEGREES ) {
            y_plus = round(padding * 1.7);  // 240/135 display ratio
            y = padding;
            wx = w/2 - 10;
          }

          it.printf(x, y, id(normal), id(black), TextAlign::CENTER_LEFT, "Red");
          it.printf(wx, y, id(large), id(red), TextAlign::CENTER_RIGHT, "%.2f%", id(tzt_red).state);          

          y = y + y_plus;

          it.printf(x, y, id(normal), id(black), TextAlign::CENTER_LEFT, "Green");
          it.printf(wx, y, id(large), id(green), TextAlign::CENTER_RIGHT, "%.2f%", id(tzt_green).state);          
          
          y = y + y_plus;

          it.printf(x, y, id(normal), id(black), TextAlign::CENTER_LEFT, "Blue");
          it.printf(wx, y, id(large), id(blue), TextAlign::CENTER_RIGHT, "%.2f%", id(tzt_blue).state);                    
          
          y = y + y_plus;

          // If rotated, split to 2 rows 
          if ( id(display_rotation) == DISPLAY_ROTATION_90_DEGREES || id(display_rotation) == DISPLAY_ROTATION_270_DEGREES ) {
            y = padding;
            x = w/2;
            wx = w;
          }

          it.printf(x, y, id(normal), id(black), TextAlign::CENTER_LEFT, "Clear");
          it.printf(wx, y, id(large), id(black), TextAlign::CENTER_RIGHT, "%.2f%", id(tzt_clear).state);          

          y = y + y_plus;

          it.printf(x, y, id(normal), id(white), TextAlign::CENTER_LEFT, "Lx");
          it.printf(wx, y, id(large), id(white), TextAlign::CENTER_RIGHT, "%.2flx", id(tzt_illuminance).state);

          y = y + y_plus;

          it.printf(x, y, id(normal), id(black), TextAlign::CENTER_LEFT, "K.");
          it.printf(wx, y, id(large), id(white), TextAlign::CENTER_RIGHT, "%.2f", id(tzt_color_temperature).state);

But all in all it’s not really meant for designing stuff, better using the awful Nextion editor if your display supports it, or LVGL - Espressif (ESP32) — LVGL documentation

OpenHASP has very good graphics and has LVGL for graphics.

Take a look at the demo screens

https://openhasp.haswitchplate.com/0.6.3/

1 Like

Thanks, this is looking a lot more promising.

I didn’t realize that PNG loading was implemented – that’s good news. :+1:

Secondly, LVGL seems on point, and a quick search reveals multiple efforts to combine it with esphome, such as ESPHome GUI component using LVGL and GitHub - fvanroie/esphome-lvgl: ESPhome with LVGL component, among others.

@fvanroie is also the author of openhasp.

There is also the switchplate component by @nielsnl68. It doesn’t use LVGL but the standard espHome display infrastructure.

esphome-lvgl was my proof-of-concept to integrate LVGL into ESPhome. While I’m proficient at LVGL, the ESPhome component integration proved too much of a challange for me… Pulling it off requires a deep understanding of both libraries.

I’m not in favor of rendering HTML off the device and streaming the graphics as this is very chatty on the network if you have a lot of switchplates.

Ideally I would still like to see LVGL+ESPhome running on the ESP32, maybe with templates for the most used cards. The ESP32 is capable at driving displays up to 7".

5 Likes

Thanks for all the replies. IIUC, we have:

  1. OpenHASP implementing dynamic LVGL widgets, communicating with HA (or anything else) via MQTT.
  2. SwitchPlate component that integrates widgets into ESPHome without building on LVGL.

I think this basically answers my original question. :slight_smile: While it would be desirable, as you say, to integrate LVGL into ESPHome and map popular Lovelace cards to ESPHome cards, the above options are a good start for someone wanting a modular way to build an ESPHome interface.

Regarding your esphome-lvgl efforts: what was the sticking point in the integration? Are there competing event loops, or something like that, that make integrating the two libraries a significant challenge?

There is no technical limitation that I am aware of. I just meant to say that I did’t know the ins-and-outs of ESPhome well enough to make any progress beyond what I posted on the GitHub project. I come from an Arduino background, so the classes, objects and way ESPhome is set up posed a significant learning curve. I’m sure that an ESPhome dev can take this concept a lot further…

Regarding your esphome-lvgl efforts: what was the sticking point in the integration? Are there competing event loops, or something like that, that make integrating the two libraries a significant challenge?

For what it’s worth, both @nielsnl68 and I are working on the esphome-lvgl integration via custom component. Admittedly, I haven’t had much time to make any forward progress, but @nielsnl68 seems to have better luck with that, working on porting his switchplate styling and configuration to the lvgl-based custom component. Help is more than welcome, so if you’d like to contribute, fork away :slight_smile:

3 Likes

Great to see this making progress!

1 Like