Planning and setup - ESP32-S3 - 4848S040 - 480*480 IPS touchscreen

Hi guys, we have now bought a house and I would like to put some of these displays in the rooms. It should be possible to operate the whole thing with openHASP.

OpenHasp is completely new territory for me. HomeAssistant is running and I already have an NSPanel Pro with HomeAssistant in use.

Now I have two or three questions. How does the performance of the devices compare to the NSPanel Pro? How much effort does it take to integrate them into HomeAssistant? And would someone perhaps be willing to help me for a fee if I decide to buy some of them?

Kind regards and many thanks in advance.

https://de.aliexpress.com/item/1005006348529558.html?spm=a2g0o.productlist.0.0.55432ysd2ysdtA&mp=1&gatewayAdapt=glo2deu

THIS IS A OPEN SOURCE PROJECT THERE ARE NO FEES

Here is what I did:

Install Firmware

  1. Connect display with your computer
  2. Flash the Firmware: Use Chrome Browser while you on the Nightly Build-Site Select “Guition ESP32-S3-4848S040” and Install
  3. Setting Up Wifi: Follow Instructions on the screen

Install HomeAssistant Components

  1. HACS: Install HACS if you haven’t already
  2. MQTT: Install and setup Home Assistant Add-on: Mosquitto broker if you haven’t already (OpenHasp and the Display communicate via MQTT)
  3. openHASP Custom Component: Install The OpenHASP Component from within HACS

Set up HomeAssistant

  1. Make sure your Display is running and reachable (Default setup shows with text “plate” on the top left corner of the screen and a popup with the actual IP-Adress).
  2. Got to shown IP-Adress with browser goto: http://your_ip/config/mqtt and fill in mqtt credentials from step 2 (see above)
  3. Got to shown IP-Adress with browser goto: http://your_ip/edit delete the pre-defined pages.jsonl file
  4. Go to shown IP-Adress with browser goto: http://your_ip/config/gpio and add:
    GPIO Output
    PIN: 40
    TYPE: Power Relay
    Groupe: None
    Value: Normal
    I had issues with entity naming when I skipped this step
  5. SetUp your display within Homeassistant detailed information here and follow the example we’ll edit the files later on. If everything works as aspected the display will be auto-discovered by OpenHASP-Component
  6. Add an empty Automation and notice the entity_id of it
  7. Add a HA-Template sensor in configuration.yaml and edit to your needs (May use http://gridcalculator.dk/ to calculate col and gutter values)
template:
  - binary_sensor:
    - name: Display settings
      unique_id: 1b3a3dae-fd0c-4f47-9c8b-5b680478a977 # https://www.uuidgenerator.net/
      state: "{{ now() - state_attr(this.attributes.update_automation | default('automation.update_display_data'), 'last_triggered') > timedelta(seconds=this.attributes.update_time | default(120) |int ) }}" # change the default automation entity_id from step 3 before
      delay_on:
        seconds: 2
      attributes:
        update_automation: "automation.update_display_data" # add a the automation entity_id from step 3 before
        update_time: "80" # sending the config via mqtt needs some time to complete. This stops the automation from re.run before sending config is finished
        display_w: "480"
        display_h: "480"
        grid_cols: "12" # increase for more resolution of object
        grid_rows: "12" # increase for more resolution of object
        grid_gutter: "8" # This looked nice for me
        grid_margin: "4" # This looked nice for me
        bg_color: "#000000"
        primary_color: "#4193d5"
        secondary_color: "#ee9f52"
        accent_color: "#A175C4"
        success_color: "#6cbe58"
        warning_color: "#d67430"
        error_color: "#e25628"
        radius: "4"

  1. If you’re done you should have a working display which is integrated in Homeassistant and comunicates via MQTT as well as a New Template Sensor with the given attributes
  2. Further reading if everything worked as aspected

Customizing

OpenHASP is highly customizable. Every Object can be set pixel perfect. This means you have to define every Width/Height x-and y-position.
To get rid of this I wrote I simple Jinja-Template. So I set Up dimensions with a grid system instead of pixel values. EXAMPLE:

Get Postion and size like this

{% import 'openhasp.jinja' as grid %} 
w = {{grid.w(1)}}
h = {{grid.h(1)}}
x = {{grid.x(1)}}
y = {{grid.y(1)}}

This Example generates the size (1x1) and coordinates ( 1st from left, 1ist from top) for the given display settings

Get colors like this

color = {% from 'openhasp.jinja' import color %}{{color('primary')}}
light_color = {% from 'openhasp.jinja' import color %}{{color('primary_light')}}
dark_color = {% from 'openhasp.jinja' import color %}{{color('primary_dark')}}

This Example generates Color Values. So if you want to change colors, just change the attribute of the Display settings sensor instead of changing the openhasp config

Try yourself

  1. In the folder “custom_templates” add a new file: openhasp.jinja
{%- macro grid() -%}
    {%- set dw = (state_attr('binary_sensor.display_settings', 'display_w') | default(240)) | int -%}
    {%- set dh = (state_attr('binary_sensor.display_settings', 'display_h') | default(240)) | int -%}
    {%- set cols = (state_attr('binary_sensor.display_settings', 'grid_cols') | default(6)) | int -%}
    {%- set rows = (state_attr('binary_sensor.display_settings', 'grid_rows') | default(6)) | int -%}
    {%- set margin = (state_attr('binary_sensor.display_settings', 'grid_margin') | default(10)) | int -%}
    {%- set gutter = (state_attr('binary_sensor.display_settings', 'grid_gutter') | default(10)) | int -%}
    {%- set radius = (state_attr('binary_sensor.display_settings', 'radius') | default(5)) | int -%}
    {{- 
        {
            'w': (dw - (2 * margin) - ((cols-1) * gutter)) / cols, 
            'h': (dh - (2 * margin) - ((rows-1) * gutter)) / rows,
            'cols' : cols,
            'rows' : rows,
            'margin' : margin,
            'gutter' : gutter,
            'display_width' : dw,
            'display_height' : dh,
            'radius': radius
        } | to_json 
    -}}
{%- endmacro -%}

{%- macro w(w) -%}
    {%- set gutter = (grid() | from_json).gutter | default(10) | int -%}
    {%- set errors = namespace(w=[]) %}
    {%- if w > (grid() | from_json).cols or w <= -1 -%}
        {% set errors.w = 'cols must be between 1 and ' + (grid() | from_json).cols | string %}
    {%- endif -%}
    {{- errors.w if errors.w | count > 0 else (w * (grid() | from_json).w | int) + ((w-1) * gutter | int) -}}    
{%- endmacro -%}

{%- macro h(h) -%}
    {%- set gutter = (grid() | from_json).gutter | default(10) | int -%}
    {%- set errors = namespace(h=[]) %}
    {%- if h > (grid() | from_json).rows or h <= -1 -%}
        {% set errors.h = 'rows must be beetween 1 and ' + (grid() | from_json).rows | string %}
    {%- endif -%}
    {{- errors.h if errors.h | count > 0 else (h * (grid() | from_json).h | int) + ((h-1) * gutter | int) -}}    
{%- endmacro -%}

{%- macro x(x) -%}
    {%- set x = x-1 | default(1) -%}
    {%- set margin = (grid() | from_json).margin | default(10) | int -%}
    {%- set gutter = (grid() | from_json).gutter | default(10) | int -%}
    {%- set width = w(x) | int -%}
    {{- (margin if x == 0 else (margin + width + gutter)) if width is number else width -}}
{%- endmacro -%}

{%- macro y(y) -%}
    {%- set y = y-1 | default(1) -%}
    {%- set margin = (grid() | from_json).margin | default(10) | int -%}
    {%- set gutter = (grid() | from_json).gutter | default(10) | int -%}
    {%- set height = h(y) | int -%}
    {{- (margin if y == 0 else (margin + height + gutter)) if height is number else height -}}
{%- endmacro -%}

{%- macro AdjustColor(hex, brightness) -%}
    {%- set brightness = brightness|default(0.1) -%}
    {%- set hex = hex|replace('#', '')|replace('/[^0-9a-f]/gi', '') -%}
    {%- if hex|length < 6 -%}
        {%- set hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2] -%}
    {%- endif -%}
    {%- set color = namespace(rgb='') -%}
    {%- set color.rgb = '#' -%}
    {%- for i in range(0, 6, 2) -%}
        {%- set c = hex[i:i+2]|int(base=16) -%}
        {%- set c = (c + (c * brightness))|round|int -%} 
        {%- set c = 0 if c < 0 else ( 255 if c > 255 else c) -%}
        {%- set c = "%02x"|format(c) -%}
        {%- set color.rgb = color.rgb + c -%} 
    {%- endfor -%}
    {{- color.rgb -}}
{%- endmacro -%}

{%- macro color(name) -%}
    {%- set s = name.split("_") -%}
    {%- set base = state_attr('binary_sensor.display_settings',  s[0]+'_color') | default('#000000') -%}
    {%- if s | count > 1 and s[1] == 'light' -%}
        {{ AdjustColor(base, 0.2) }}
    {%- elif s | count > 1 and s[1] == 'dark' -%}
        {{ AdjustColor(base, -0.2) }}
    {%-else-%}
        {{ base  -}}
    {%- endif %}
{%- endmacro -%}
  1. In your plate.json file:
[
    "SEITE 0 - SETTINGS",
    {
        "page": 1,
        "id": 0,
        "prev": 4
    },
    "SEITE 0 - PREVIOUS -BUTTON",
    {
        "page": 0,
        "id": 1,
        "obj": "btn",
        "outline_opa": 0,
        "border_opa": 0,
        "text_font": 32,
        "toggle": false,
        "action": "prev"
    },
    "SEITE 0 - HOME - BUTTON",
    {
        "page": 0,
        "id": 2,
        "obj": "btn",
        "outline_opa": 0,
        "border_opa": 0,
        "text_font": 32,
        "toggle": false,
        "action": {
            "down": "back",
            "hold": "restart"
        }
    },
    "SEITE 0 - NEXT - BUTTON",
    {
        "page": 0,
        "id": 3,
        "obj": "btn",
        "text": "\uE054",
        "outline_opa": 0,
        "border_opa": 0,
        "text_font": 32,
        "action": "next",
        "toggle": false
    },
    "SEITE 1 - CONTAINER",
    {
        "page": 1,
        "id": 2,
        "obj": "obj",
        "border_opa": 0,
        "bg_opa": 0,
        "click": 0
    }
]
  1. In your homeassistant configuration.yaml add:
openhasp:
  display02: # depends on you given device Name!!!
    objects:
      # SEITE 0
      - obj: "p0b0" # SETTINGS
        properties:
          "bg_color" : "{% from 'openhasp.jinja' import color %}{{color('bg')}}"
      - obj: "p0b1" # PREVIOUS - BUTTON"
        properties:
          "w": "{% import 'openhasp.jinja' as grid %}{{grid.w(4)}}"
          "h": "{% import 'openhasp.jinja' as grid %}{{grid.h(2)}}"
          "x": "{% import 'openhasp.jinja' as grid %}{{grid.x(1)}}"
          "y": "{% import 'openhasp.jinja' as grid %}{{grid.y(11)}}"
          "text": "\uE04D"
          "radius": "{% from 'openhasp.jinja' import grid %} {{(grid() | from_json).radius |int}}"
          "bg_color": "{% from 'openhasp.jinja' import color %}{{color('primary')}}"
          "bg_color02": "{% from 'openhasp.jinja' import color %}{{color('primary_light')}}"
      - obj: "p0b2" # HOME - BUTTON
        properties:
          "w": "{% import 'openhasp.jinja' as grid %}{{grid.w(4)}}"
          "h": "{% import 'openhasp.jinja' as grid %}{{grid.h(2)}}"
          "x": "{% import 'openhasp.jinja' as grid %}{{grid.x(5)}}"
          "y": "{% import 'openhasp.jinja' as grid %}{{grid.y(11)}}"
          "text": "\uE6A1"
          "radius": "{% from 'openhasp.jinja' import grid %} {{(grid() | from_json).radius |int}}"
          "bg_color": "{% from 'openhasp.jinja' import color %}{{color('primary')}}"
          "bg_color02": "{% from 'openhasp.jinja' import color %}{{color('primary_light')}}"
      - obj: "p0b3" # NEXT - BUTTON
        properties:
          "w": "{% import 'openhasp.jinja' as grid %}{{grid.w(4)}}"
          "h": "{% import 'openhasp.jinja' as grid %}{{grid.h(2)}}"
          "x": "{% import 'openhasp.jinja' as grid %}{{grid.x(9)}}"
          "y": "{% import 'openhasp.jinja' as grid %}{{grid.y(11)}}"
          "radius": "{% from 'openhasp.jinja' import grid %} {{(grid() | from_json).radius |int}}"
          "text": "\uE054"
          "bg_color": "{% from 'openhasp.jinja' import color %}{{color('primary')}}"   
          "bg_color02": "{% from 'openhasp.jinja' import color %}{{color('primary_light')}}"
      # SEITE 1
      - obj: "p1b0" # SETTINGS
        properties:
          "bg_color" : "{% from 'openhasp.jinja' import color %}{{color('bg')}}"
      - obj: "p1b2" # CONTAINER
        properties:
          "w": "{% from 'openhasp.jinja' import grid %} {{(grid() | from_json).display_width}}"
          "h": "{% import 'openhasp.jinja' as grid %}{{grid.h(10)}}"
          "x": 0
          "y": 0 
  1. Restart The Display and/or Open your Home Assistant instance and show your service developer tools with a specific service selected. reload_config_entry for your device to see your changes

  2. From now on you’re on your own. Feel free to share yours

Here is my display:

screenshot
screenshot-1
screenshot-2
screenshot-3
screenshot-4

5 Likes

Hello guys! I got this pretty thing last day :slight_smile:
Now i migrate my Lanbon L8 to here. Now first page are done :slight_smile:

Please, somebody send me the GPIO config. Thanks!

1 Like

Somebody afffect random display wake up? I affect this… It is software or hardware problem?

GPIO Output
PIN: 40
TYPE: Power Relay
Groupe: None
Value: Normal

MQTT: hasp/plate/command/output40
PAYLOAD: {"state":"on"} or {"state":"off"}

thanks! working.

Which web-ui is this?

Lanbon L8 is the device name :slight_smile:

https://aliexpress.com/item/1005001943888230.html

No. I mean the Web-UI so the interface with teh sliders you can see on the picture. Is this openHASP?

Yes, it is.

Hi I got my display today but I have no Power Relay to select in the Dropdown Menu. What sholud I select? and i bought the version with 3 relays. do i have to add more pin outs?

make sure you add OUTPUT

1 Like

@xdw15c
my bad. I was on input :smiley:
Which are the pins for the second and third relay?

and the on and off switch via home assistant does not work from the relay either, i.e. output 40

or should I use pin 1?

you have to try

@xdw15c Can I damage anything when I try and error from pin 1 to 40 ?

Relay connection
GPIO01 | RELAY L3
GPIO02 | RELAY L2
GPIO40 | RELAY L1

2 Likes

Hello and thanks a lot for your share on this jinja layout, its awesome !

I’m just to stupid to understand that line in “setup homeAssistant > 7 Add HA-template”

i don’t understand what does mean “# also change in automation trigger” ?
is than an entity ?
is it relative to the empty automation added in step 6 just before ?

for a 1st attempt i’d just like to display a different text or sensor value per page

for now i just display the 3 bottom buttons ← | home | → but the grid system seems to work as i can resize or move buttons.

and it seems i cant upload dynamically my HA local pages.json to the plate through the integration, for now i must put it manually as a jsonl directly into the plate itself.

hope you can help me.
best regards.

This is a comment that reminds me to also change my automation, as this triggers when the Entity Picture (Cover Art) of the media_player changes. I did not post the complete automation here, because it is very personalized and not relevant to get the display to work

trigger:
   ...
  - platform: homeassistant
    event: start
    id: ha_restart
  - platform: state
    entity_id:
      - media_player.kuche
    attribute: entity_picture
    id: new_coverart_kueche
    for:
      seconds: 1
...

EDIT: I removed this from the comment above, in order to prevent confusions.
Thanks @t-minik

:+1:

did you forget the following (from the Openhasp docs):

The file must be located in an authorized location defined by allowlist_external_dirs (in case of hassio /config/ is the directory where Home Assistant’s configuration.yaml resides, so in case of a subdirectory called openhasp the full path would be e.g. /config/openhasp/pages.jsonl , and you need to add /config/openhasp/ to your allowlist_external_dirs ).

homeassistant:
  allowlist_external_dirs:
    - "/config/openhasp"
1 Like

Thanks a lot.

I’ll give a try.

Best regards

It’s all good now.

Thanks a lot for the “allowlist_external_dirs” config, I didn’t saw this.

As it’s my 1st openhasp plate, it’s a bit tricky.
I only get 2 pages with 2 different sensors values for now, for learning principles I probably had to start with vanilla openhasp.

Thanks a lot though.
Best regards.