# openHASP: An MQTT driven Touchscreen / Scene controller

**URL:** <https://discourse.nodered.org/t/openhasp-an-mqtt-driven-touchscreen-scene-controller/61937>\
**Category:** Share Your Projects\
**Created:** [28 April 2022 20:44 UTC](https://discourse.nodered.org/t/openhasp-an-mqtt-driven-touchscreen-scene-controller/61937 "2022-04-28T20:44:58Z")\
**Posts on this page:** 14\
**Page:** 1

<div class="post-metadata">

**Author:** ![fvanroie](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/fvanroie/32/60763_2.png) [@fvanroie](https://discourse.nodered.org/u/fvanroie)\
**Post date:** [28 April 2022 20:44 UTC](https://discourse.nodered.org/t/openhasp-an-mqtt-driven-touchscreen-scene-controller/61937/1 "2022-04-28T20:44:58Z")

</div>

Hi! I'm the creator of [openHASP](https://openhasp.haswitchplate.com/), an opensource firmware for Home-Automation SwitchPlates.

openHASP can be flashed to various ESP32 devices to create a GUI front-end which uses MQTT to send & receive commands and events. The firmware only contains a basic UI to setup the initial WiFi connection because users are expected to upload their **own page layouts** next.

Pages and objects can be defined using the `jsonl` (JSON lines) format and the parameters are interpreted on the fly. This allows users a great degree of flexibility in designing a personal layout.

One page definition might look something like this:  
 ![image](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/4/6/46a2adf40328d9e3db503ef4e7d153cec969121c.png)

```json
{"page":1,"comment":"---------- Page 1 ----------"}
{"id":1,"obj":"btn","x":10,"y":45,"w":220,"h":55,"toggle":true,"text":"Touch me \uE96B","text_font":32}
{"id":2,"obj":"checkbox","x":10,"y":100,"w":220,"h":55,"text":" My Checkbox"}
{"id":3,"obj":"label","x":10,"y":10,"w":220,"h":30,"text":"\uE75A My Label","align":1,"padh":50}
{"id":4,"obj":"switch","x":125,"y":220,"w":100,"h":55,"radius":40,"radius2":40}
{"id":5,"obj":"led","x":22,"y":220,"w":55,"h":55,"val":255}
{"id":6,"obj":"dropdown","x":10,"y":160,"w":130,"options":"\uE40A Apples\n\uE40A Oranges\n\uE40A Bananas"}
{"id":7,"obj":"spinner","x":160,"y":140,"w":70,"h":70}

```

All objects have standardized event handlers that communicate the state over MQTT, making it easy to integrate the device in a Home-Automation system like nodeRED, Home Assistant or openHAB. Objects can also be grouped with local GPIO’s _(such as relays or a dimmer)_ to interact with lights and switches.

There are pre-compiled binaries for a dozen of supported hardware devices and configuration files for many more in the _platformio_ project on Github…

Here is a recent review video of openHASP running on the Lanbon L8 in-wall switch:

[![](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/b/e/bea841ea9bbcdd6ebe249a421784f0f5a7a65856.jpeg "The Perfect In-Wall Touchscreen for a Self-Hosted Smart Home - Lanbon L8 running OpenHASP") ](https://www.youtube.com/watch?v=QgE4WrgHrWg)

---

<div class="post-metadata">

**Author:** ![Trying\_to\_learn](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/trying_to_learn/32/28400_2.png) [@Trying\_to\_learn](https://discourse.nodered.org/u/Trying_to_learn)\
**Post date:** [28 April 2022 21:17 UTC](https://discourse.nodered.org/t/openhasp-an-mqtt-driven-touchscreen-scene-controller/61937/2 "2022-04-28T21:17:40Z")

</div>

I haven't watched the video, but it looks good.

---

<div class="post-metadata">

**Author:** ![TotallyInformation](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/totallyinformation/32/31_2.png) [@TotallyInformation](https://discourse.nodered.org/u/TotallyInformation)\
**Post date:** [28 April 2022 22:19 UTC](https://discourse.nodered.org/t/openhasp-an-mqtt-driven-touchscreen-scene-controller/61937/3 "2022-04-28T22:19:38Z")

</div>

Nice. Just wish there was a version that would integrate with ESPHome which I now use on all of my ESP devices.

---

<div class="post-metadata">

**Author:** ![fvanroie](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/fvanroie/32/60763_2.png) [@fvanroie](https://discourse.nodered.org/u/fvanroie)\
**Post date:** [1 May 2022 09:16 UTC](https://discourse.nodered.org/t/openhasp-an-mqtt-driven-touchscreen-scene-controller/61937/4 "2022-05-01T09:16:31Z")

</div>

This comes up from time to time and I actually did a [proof-of-concept](https://github.com/fvanroie/esphome-lvgl) of ESPhome running LVGL. It requires writing custom components for ESPhome, which is a whole other topic and area of expertise. It's a different approach/project altogether.

The current display components for ESPhome are rather basic. But Tasmota is doing cool LVGL stuff now too.

---

<div class="post-metadata">

**Author:** ![TotallyInformation](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/totallyinformation/32/31_2.png) [@TotallyInformation](https://discourse.nodered.org/u/TotallyInformation)\
**Post date:** [1 May 2022 10:43 UTC](https://discourse.nodered.org/t/openhasp-an-mqtt-driven-touchscreen-scene-controller/61937/5 "2022-05-01T10:43:12Z")

</div>

I don't have a large enough screen on anything to make that worth while really. What I've done for my M5stack Basic is write some bespoke outputs that let me more easily create simple block screen layouts. The Basic is not touch screen, it just has 3 buttons.

So I have a `common.h` with things like this in it:

```nohighlight
#ifdef FONT_SIZE_22
  //auto PRINT_FONT = id(print_font);
  //#define ICON_FONT id(icon_font)
  const int FONT_SIZE = 22;
  const int HEADER_HT = FONT_SIZE+4; // Header height
  const int FOOTER_HT = HEADER_HT; // Footer height
  const int LINE_SIZE = FONT_SIZE+2; // fontsize + 2
  const int LINE_1 = HEADER_HT+1; // header + 1
  const int LINE_2 = LINE_1 + FONT_SIZE + 2; // Previous line + 2
  const int LINE_3 = LINE_2 + FONT_SIZE + 2; // Previous line + 2
  const int LINE_4 = LINE_3 + FONT_SIZE + 2; // Previous line + 2
  const int LINE_5 = LINE_4 + FONT_SIZE + 2; // Previous line + 2
  const int LINE_6 = LINE_5 + FONT_SIZE + 2; // Previous line + 2
  const int TEXT_COL_LEFT = 1;
  const int TEXT_BLOCK_CENTER = 10 * FONT_SIZE;
#endif

```

And then a number of "page" yaml files like this:

```nohighlight
- id: page1
  # Initial page. Header (Pg, status, time), Footer (buttons)
  lambda: |-

    static int num_executions = 0;
    num_executions += 1;
    
    /* @see lib\common.h for constant definitions such as lines and columns */

    /* ---- Header ---- */
    auto nowtime = id(sntp_time).now();
    auto wifiStatus = WiFi.status();
    it.filled_rectangle(0,0, it.get_width(), HEADER_HT, id(COLOR_CSS_DEEPSKYBLUE));
    if (WiFi.status() == WL_CONNECTED) {
      it.print(TEXT_BLOCK_1, HEADER_Y, id(icon_font), id(COLOR_CSS_BLACK), TextAlign::LEFT, "");
    } else {
      it.print(TEXT_BLOCK_1, HEADER_Y, id(icon_font), id(COLOR_CSS_RED), TextAlign::LEFT, "");
    }
    if (nowtime.is_valid()) {
      it.strftime(it.get_width()-1, HEADER_Y, id(print_font), id(COLOR_CSS_BLACK), TextAlign::TOP_RIGHT, "%Y-%m-%d %H:%M", nowtime);
    } else {
      it.print(it.get_width()-1, HEADER_Y, id(print_font), id(COLOR_CSS_BLACK), TextAlign::TOP_RIGHT, "----/--/-- --:--");
    }

    /* ---- Footer ---- */
    it.filled_rectangle(0,it.get_height()-FOOTER_HT, it.get_width(), FOOTER_HT, id(COLOR_CSS_LIGHTBLUE));
    

    /* ---- Main ---- */
    //it.printf(TEXT_BLOCK_1, LINE_1, id(print_font), id(COLOR_CSS_WHITE), TextAlign::LEFT, "%s", me.c_str());

    // Here
    double t_float = id(temperature).state;
    double h_float = id(humidity).state;
    it.print(TEXT_BLOCK_1, LINE_1, id(print_font), id(COLOR_CSS_WHITE), TextAlign::TOP_LEFT, "Here");
    it.print(TEXT_BLOCK_1+6, LINE_2, id(icon_font), id(COLOR_CSS_WHITE), TextAlign::TOP_CENTER, "");
    it.print(TEXT_BLOCK_1+6, LINE_3, id(icon_font), id(COLOR_CSS_WHITE), TextAlign::TOP_CENTER, "");
    it.printf(TEXT_BLOCK_1+LINE_SIZE, LINE_2, id(print_font), t_float>22.0?id(COLOR_CSS_RED):id(COLOR_CSS_WHITE), TextAlign::LEFT, "%.1f°C", t_float);
    it.printf(TEXT_BLOCK_1+LINE_SIZE, LINE_3, id(print_font), h_float>70?id(COLOR_CSS_RED):id(COLOR_CSS_WHITE), TextAlign::LEFT, "%.1f%%", h_float);

    // Landing (D1M04)
    t_float = strToFloat( id(landing_temperature).state );
    h_float = strToFloat( id(landing_humidity).state );
    it.print(TEXT_BLOCK_2, LINE_1, id(print_font), id(COLOR_CSS_WHITE), TextAlign::TOP_LEFT, "Landing");
    it.print(TEXT_BLOCK_2+6, LINE_2, id(icon_font), id(COLOR_CSS_WHITE), TextAlign::TOP_CENTER, "");
    it.print(TEXT_BLOCK_2+6, LINE_3, id(icon_font), id(COLOR_CSS_WHITE), TextAlign::TOP_CENTER, "");
    if (t_float == 0.00) {
      it.print(TEXT_BLOCK_2+LINE_SIZE, LINE_2, id(print_font), id(COLOR_CSS_LINEN), TextAlign::LEFT, "--.-°C");
    } else {
      it.printf(TEXT_BLOCK_2+LINE_SIZE, LINE_2, id(print_font), t_float>22.0?id(COLOR_CSS_RED):id(COLOR_CSS_WHITE), TextAlign::LEFT, "%.1f°C", t_float);      
    }
    if (h_float == 0.00) {
      it.print(TEXT_BLOCK_2+LINE_SIZE, LINE_3, id(print_font), id(COLOR_CSS_LINEN), TextAlign::LEFT, "--.-%");
    } else {
      it.printf(TEXT_BLOCK_2+LINE_SIZE, LINE_3, id(print_font), h_float>70?id(COLOR_CSS_RED):id(COLOR_CSS_WHITE), TextAlign::LEFT, "%.1f%%", h_float);
    }
    

    // Bathroom
    t_float = strToFloat( id(bathroom_temperature).state ); 
    h_float = strToFloat( id(bathroom_humidity).state );
    it.print(TEXT_BLOCK_3, LINE_1, id(print_font), id(COLOR_CSS_WHITE), TextAlign::TOP_LEFT, "Bathroom");
    it.print(TEXT_BLOCK_3+6, LINE_2, id(icon_font), id(COLOR_CSS_WHITE), TextAlign::TOP_CENTER, "");
    it.print(TEXT_BLOCK_3+6, LINE_3, id(icon_font), id(COLOR_CSS_WHITE), TextAlign::TOP_CENTER, "");
    if (t_float == 0.00) {
      it.print(TEXT_BLOCK_3+LINE_SIZE, LINE_2, id(print_font), id(COLOR_CSS_LINEN), TextAlign::LEFT, "--.-°C");
    } else {
      it.printf(TEXT_BLOCK_3+LINE_SIZE, LINE_2, id(print_font), t_float>22.0?id(COLOR_CSS_RED):id(COLOR_CSS_WHITE), TextAlign::LEFT, "%.1f°C", t_float);      
    }
    if (h_float == 0.00) {
      it.print(TEXT_BLOCK_3+LINE_SIZE, LINE_3, id(print_font), id(COLOR_CSS_LINEN), TextAlign::LEFT, "--.-%");
    } else {
      it.printf(TEXT_BLOCK_3+LINE_SIZE, LINE_3, id(print_font), h_float>70?id(COLOR_CSS_RED):id(COLOR_CSS_WHITE), TextAlign::LEFT, "%.1f%%", h_float);
    }

    // Front Hall
    t_float = strToFloat( id(front_hall_temperature).state ); 
    //h_float = strToFloat( id(front_hall_humidity).state );
    it.print(TEXT_BLOCK_1, LINE_4, id(print_font), id(COLOR_CSS_WHITE), TextAlign::TOP_LEFT, "F Hall");
    it.print(TEXT_BLOCK_1+6, LINE_5, id(icon_font), id(COLOR_CSS_WHITE), TextAlign::TOP_CENTER, "");
    it.print(TEXT_BLOCK_1+6, LINE_6, id(icon_font), id(COLOR_CSS_WHITE), TextAlign::TOP_CENTER, "");
    if (t_float == 0.00) {
      it.print(TEXT_BLOCK_1+LINE_SIZE, LINE_5, id(print_font), id(COLOR_CSS_LINEN), TextAlign::LEFT, "--.-°C");
    } else {
      it.printf(TEXT_BLOCK_1+LINE_SIZE, LINE_5, id(print_font), t_float>22.0?id(COLOR_CSS_RED):id(COLOR_CSS_WHITE), TextAlign::LEFT, "%.1f°C", t_float);      
    }
    it.print(TEXT_BLOCK_1+LINE_SIZE, LINE_6, id(print_font), id(COLOR_CSS_LINEN), TextAlign::LEFT, "--.-%");

    // Rear Hall
    t_float = strToFloat( id(rear_hall_temperature).state ); 
    h_float = strToFloat( id(rear_hall_humidity).state );
    it.print(TEXT_BLOCK_2, LINE_4, id(print_font), id(COLOR_CSS_WHITE), TextAlign::TOP_LEFT, "R Hall");
    it.print(TEXT_BLOCK_2+6, LINE_5, id(icon_font), id(COLOR_CSS_WHITE), TextAlign::TOP_CENTER, "");
    it.print(TEXT_BLOCK_2+6, LINE_6, id(icon_font), id(COLOR_CSS_WHITE), TextAlign::TOP_CENTER, "");
    if (t_float == 0.00) {
      it.print(TEXT_BLOCK_2+LINE_SIZE, LINE_5, id(print_font), id(COLOR_CSS_LINEN), TextAlign::LEFT, "--.-°C");
    } else {
      it.printf(TEXT_BLOCK_2+LINE_SIZE, LINE_5, id(print_font), t_float>22.0?id(COLOR_CSS_RED):id(COLOR_CSS_WHITE), TextAlign::LEFT, "%.1f°C", t_float);      
    }
    if (h_float == 0.00) {
      it.print(TEXT_BLOCK_2+LINE_SIZE, LINE_6, id(print_font), id(COLOR_CSS_LINEN), TextAlign::LEFT, "--.-%");
    } else {
      it.printf(TEXT_BLOCK_2+LINE_SIZE, LINE_6, id(print_font), h_float>70?id(COLOR_CSS_RED):id(COLOR_CSS_WHITE), TextAlign::LEFT, "%.1f%%", h_float);
    }

    // Kitchen
    t_float = strToFloat( id(kitchen_temperature).state ); 
    //h_float = strToFloat( id(kitchen_humidity).state );
    it.print(TEXT_BLOCK_3, LINE_4, id(print_font), id(COLOR_CSS_WHITE), TextAlign::TOP_LEFT, "Kitchen");
    it.print(TEXT_BLOCK_3+6, LINE_5, id(icon_font), id(COLOR_CSS_WHITE), TextAlign::TOP_CENTER, "");
    it.print(TEXT_BLOCK_3+6, LINE_6, id(icon_font), id(COLOR_CSS_WHITE), TextAlign::TOP_CENTER, "");
    if (t_float == 0.00) {
      it.print(TEXT_BLOCK_3+LINE_SIZE, LINE_5, id(print_font), id(COLOR_CSS_LINEN), TextAlign::LEFT, "--.-°C");
    } else {
      it.printf(TEXT_BLOCK_3+LINE_SIZE, LINE_5, id(print_font), t_float>22.0?id(COLOR_CSS_RED):id(COLOR_CSS_WHITE), TextAlign::LEFT, "%.1f°C", t_float);      
    }
    it.print(TEXT_BLOCK_3+LINE_SIZE, LINE_6, id(print_font), id(COLOR_CSS_LINEN), TextAlign::LEFT, "--.-%");

```

So I can get things to line up pretty easily. Nothing like as nice as your touchscreen setup though of course.

---

<div class="post-metadata">

**Author:** ![fvanroie](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/fvanroie/32/60763_2.png) [@fvanroie](https://discourse.nodered.org/u/fvanroie)\
**Post date:** [1 May 2022 11:40 UTC](https://discourse.nodered.org/t/openhasp-an-mqtt-driven-touchscreen-scene-controller/61937/6 "2022-05-01T11:40:08Z")

</div>

Thanks for sharing. I don't doubt similar things can be achieved with ESPhome. Of course, you can use whichever firmware works in your situation.

The openHASP concept is that you can create, change, move or delete any UI element on the screen _on-the-fly_ using a simple JSON format over MQTT. Users don't need to recompile and upload new firmware when the layout changes. Besides text labels, you can use 20 common UI elements to quickly create interactive screens.

---

<div class="post-metadata">

**Author:** ![stefan24](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/stefan24/32/23075_2.png) [@stefan24](https://discourse.nodered.org/u/stefan24)\
**Post date:** [1 May 2022 16:12 UTC](https://discourse.nodered.org/t/openhasp-an-mqtt-driven-touchscreen-scene-controller/61937/7 "2022-05-01T16:12:53Z")

</div>

Thanks for that introduction, @fvanroie. I just ordered a Lanbon L8 switch from Amazon UK, which should arrive a Wednesday!!

---

<div class="post-metadata">

**Author:** ![Steve-Mcl](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/steve-mcl/32/4826_2.png) [@Steve-Mcl](https://discourse.nodered.org/u/Steve-Mcl)\
**Post date:** [1 May 2022 16:53 UTC](https://discourse.nodered.org/t/openhasp-an-mqtt-driven-touchscreen-scene-controller/61937/8 "2022-05-01T16:53:10Z")

</div>

Hi @fvanroie

I have one of these [320-320-4-inch wireless-tag devices](https://www.alibaba.com/product-detail/3-92-320-320-4-inch_1700007792543.html) (ESP32-S2-WROVER based) would you know if (and how) this firmware could be used for this?

---

<div class="post-metadata">

**Author:** ![fvanroie](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/fvanroie/32/60763_2.png) [@fvanroie](https://discourse.nodered.org/u/fvanroie)\
**Post date:** [1 May 2022 19:55 UTC](https://discourse.nodered.org/t/openhasp-an-mqtt-driven-touchscreen-scene-controller/61937/9 "2022-05-01T19:55:31Z")

</div>

@Steve-Mcl We are indeed testing the WT-86-32-3ZW1 in the [development branch](https://github.com/HASwitchPlate/openHASP/actions). Feel free to try it and join the [discussion](https://github.com/HASwitchPlate/openHASP/discussions/311) on Github.

---

<div class="post-metadata">

**Author:** ![stefan24](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/stefan24/32/23075_2.png) [@stefan24](https://discourse.nodered.org/u/stefan24)\
**Post date:** [3 June 2022 13:57 UTC](https://discourse.nodered.org/t/openhasp-an-mqtt-driven-touchscreen-scene-controller/61937/10 "2022-06-03T13:57:13Z")

</div>

I finally managed to flash OpenHASP onto my Lanbon L8. I checked the demo pages and love the possibilities to build screens with so many options. I can control the relays and the moodlight of the Lanbon switch. Up to 12 pages can easily be filled with various GUI elements and either selected via the navigation on the bottom (or some other place you prefer) and also via swipe.

![plate1](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/8/c/8c23c18e4672cd356603979a560dd64e2d078494.png)  
 ![plate2](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/0/7/0710351dc2d979a55c21d2397cf95f41c4154dea.png)

see my adapted demo page. Top left there is a toggle button, on the right a touch button. The three bulbs control the three relays via MQTT (so there is no direct control, everything is managed from Node-RED).  
Time and temperature are also published by Node-RED.

@fvanroie, have you plans to support a color change for the LED object? This is something I use quite often in my Web GUI.

---

<div class="post-metadata">

**Author:** ![fvanroie](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/fvanroie/32/60763_2.png) [@fvanroie](https://discourse.nodered.org/u/fvanroie)\
**Post date:** [4 June 2022 09:34 UTC](https://discourse.nodered.org/t/openhasp-an-mqtt-driven-touchscreen-scene-controller/61937/11 "2022-06-04T09:34:20Z")

</div>

Nice @stefan24! That's looking good.

> [@](#):
>
> @fvanroie, have you plans to support a color change for the LED object? This is something I use quite often in my Web GUI.

Try setting `p5b5.bg_color=orange` and `p5b5.shadow_color=orange`. The `bg_color` and `shadow_color` properties control the LED color.

---

<div class="post-metadata">

**Author:** ![stefan24](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/stefan24/32/23075_2.png) [@stefan24](https://discourse.nodered.org/u/stefan24)\
**Post date:** [4 June 2022 21:19 UTC](https://discourse.nodered.org/t/openhasp-an-mqtt-driven-touchscreen-scene-controller/61937/12 "2022-06-04T21:19:28Z")

</div>

... and additionally the `border_color`, otherwise it seems not to be perfect orange.  
Got it, but not obvious.  
Thank you for the quick reply!

---

<div class="post-metadata">

**Author:** ![fvanroie](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/fvanroie/32/60763_2.png) [@fvanroie](https://discourse.nodered.org/u/fvanroie)\
**Post date:** [19 June 2022 09:04 UTC](https://discourse.nodered.org/t/openhasp-an-mqtt-driven-touchscreen-scene-controller/61937/13 "2022-06-19T09:04:28Z")

</div>

I’m happy to announce the release of **[openHASP 0.6.3](https://github.com/HASwitchPlate/openHASP/releases/tag/v0.6.3)**. Please check out the release notes for the notable changes. It includes quite a list of improvements, new features and bug fixes.

We can now also be found on [www.openhasp.com](http://www.openhasp.com) and you can install the firmware using the [openHASP Web Installer](https://install.openhasp.com).

---

<div class="post-metadata">

**Author:** ![mantonucci](https://avatars.discourse-cdn.com/v4/letter/m/ecc23a/32.png) [@mantonucci](https://discourse.nodered.org/u/mantonucci)\
**Post date:** [29 November 2022 14:58 UTC](https://discourse.nodered.org/t/openhasp-an-mqtt-driven-touchscreen-scene-controller/61937/14 "2022-11-29T14:58:43Z")

</div>

Great development! I'm going to experiment openHASP on my AZ-Touch plate. Also, nice video - but being that such a "dense" matter, I really believe you should speak MUCH slower...
