THIS IS A OPEN SOURCE PROJECT THERE ARE NO FEES
Here is what I did:
Install Firmware
- Connect display with your computer
- Flash the Firmware: Use Chrome Browser while you on the Nightly Build-Site Select âGuition ESP32-S3-4848S040â and Install
- Setting Up Wifi: Follow Instructions on the screen
Install HomeAssistant Components
- HACS: Install HACS if you havenât already
- MQTT: Install and setup Home Assistant Add-on: Mosquitto broker if you havenât already (OpenHasp and the Display communicate via MQTT)
- openHASP Custom Component: Install The OpenHASP Component from within HACS
Set up HomeAssistant
- 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).
- Go to shown IP-Adress with browser goto: http://your_ip/config/mqtt and fill in mqtt credentials from step 2 (see above)
- Got to shown IP-Adress with browser goto: http://your_ip/edit delete the pre-defined
pages.jsonl file (Optional, otherwise Display loads this during startup)
- Go to shown IP-Adress with browser goto: http://your_ip/config/gpio and add:
GPIO Output
PIN: 40 (May be different on other Displays)
TYPE: Power Relay
Groupe: None
Value: Normal
â I had issues with entity naming when I skipped this step
- 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
- 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() }}"
attributes:
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"
- 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
- 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
- In the folder âcustom_templatesâ add a new file:
openhasp.jinja
{# 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)}}
#}
{# Konstanten fĂŒr Default-Werte #}
{%- set DEFAULT_DISPLAY = {
'width': 240,
'height': 240,
'cols': 6,
'rows': 6,
'margin': 10,
'gutter': 10,
'radius': 5
} -%}
{%- macro grid() -%}
{%- set settings = 'binary_sensor.display_settings' -%}
{# Display-Dimensionen #}
{%- set display = {
'width': state_attr(settings, 'display_w') | default(DEFAULT_DISPLAY.width) | int,
'height': state_attr(settings, 'display_h') | default(DEFAULT_DISPLAY.height) | int,
'cols': state_attr(settings, 'grid_cols') | default(DEFAULT_DISPLAY.cols) | int,
'rows': state_attr(settings, 'grid_rows') | default(DEFAULT_DISPLAY.rows) | int,
'margin': state_attr(settings, 'grid_margin') | default(DEFAULT_DISPLAY.margin) | int,
'gutter': state_attr(settings, 'grid_gutter') | default(DEFAULT_DISPLAY.gutter) | int,
'radius': state_attr(settings, 'radius') | default(DEFAULT_DISPLAY.radius) | int
} -%}
{# Berechnung der ZellgröĂen #}
{%- set cell = {
'width': (display.width - (2 * display.margin) - ((display.cols-1) * display.gutter)) / display.cols,
'height': (display.height - (2 * display.margin) - ((display.rows-1) * display.gutter)) / display.rows
} -%}
{{- {
'w': cell.width,
'h': cell.height,
'cols': display.cols,
'rows': display.rows,
'margin': display.margin,
'gutter': display.gutter,
'display_width': display.width,
'display_height': display.height,
'radius': display.radius
} | to_json -}}
{%- endmacro -%}
{%- macro w(w) -%}
{%- set g = grid() | from_json -%}
{%- set gutter = g.gutter if g.gutter is not none else 10 -%}
{%- set cell_w = g.w if g.w is not none else 0 -%}
{%- set cols = g.cols if g.cols is not none else 6 -%}
{%- set errors = namespace(w=[]) %}
{%- if w > cols or w <= -1 -%}
{% set errors.w = 'cols must be between 1 and ' + cols | string %}
{%- endif -%}
{{- errors.w if errors.w | count > 0 else (w * cell_w | int) + ((w-1) * gutter | int) -}}
{%- endmacro -%}
{%- macro h(h) -%}
{%- set g = grid() | from_json -%}
{%- set gutter = g.gutter if g.gutter is not none else 10 -%}
{%- set cell_h = g.h if g.h is not none else 0 -%}
{%- set rows = g.rows if g.rows is not none else 6 -%}
{%- set errors = namespace(h=[]) %}
{%- if h > rows or h <= -1 -%}
{% set errors.h = 'rows must be between 1 and ' + rows | string %}
{%- endif -%}
{{- errors.h if errors.h | count > 0 else (h * cell_h | int) + ((h-1) * gutter | int) -}}
{%- endmacro -%}
{%- macro x(x) -%}
{%- set x = x - 1 -%}
{%- set g = grid() | from_json -%}
{%- set margin = g.margin if g.margin is not none else 10 -%}
{%- set gutter = g.gutter if g.gutter is not none else 10 -%}
{%- set width_raw = w(x) -%}
{%- set width = width_raw | int(0) if width_raw is not none and width_raw | string | regex_match('^-?[0-9]+$') else 0 -%}
{{- (margin if x == 0 else (margin + width + gutter)) | int -}}
{%- endmacro -%}
{%- macro y(y) -%}
{%- set y = y - 1 -%}
{%- set g = grid() | from_json -%}
{%- set margin = g.margin if g.margin is not none else 10 -%}
{%- set gutter = g.gutter if g.gutter is not none else 10 -%}
{%- set height_raw = h(y) -%}
{%- set height = height_raw | int(0) if height_raw is not none and height_raw | string | regex_match('^-?[0-9]+$') else 0 -%}
{{- (margin if y == 0 else (margin + height + gutter)) | int -}}
{%- endmacro -%}
{# 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')}}
#}
{%- macro AdjustColor(hex, brightness) -%}
{%- set brightness = brightness|default(0.1) -%}
{%- set hex = hex|replace('#', '')|lower -%}
{%- if hex|length != 6 or not hex|regex_match('^[0-9a-f]{6}$') -%}
{%- set hex = '000000' -%}
{%- endif -%}
{%- set color = namespace(rgb=[]) -%}
{%- for i in range(0, 6, 2) -%}
{%- set c = hex[i:i+2]|int(base=16, default=0) -%}
{%- set c = (c + (c * brightness|float))|round|int -%}
{%- if c < 0 %}{% set c = 0 %}{% endif -%}
{%- if c > 255 %}{% set c = 255 %}{% endif -%}
{%- set color.rgb = color.rgb + ["%02x"|format(c)] -%}
{%- endfor -%}
{{- '#' + ''.join(color.rgb) -}}
{%- endmacro -%}
{%- macro color(name) -%}
{%- set parts = name.split('_') -%}
{%- set base_color = state_attr('binary_sensor.display_settings', parts[0] + '_color') -%}
{%- set base_color = '#000000' if base_color is none else base_color -%}
{%- set base_color = base_color|replace('#', '')|lower -%}
{%- if base_color|length != 6 or not base_color|regex_match('^[0-9a-f]{6}$') -%}
{%- set base_color = '000000' -%}
{%- endif -%}
{%- set base_color = '#' + base_color -%}
{%- if parts|length > 1 -%}
{%- if parts[1] == 'light' -%}
{{- AdjustColor(base_color, 0.2) -}}
{%- elif parts[1] == 'dark' -%}
{{- AdjustColor(base_color, -0.2) -}}
{%- else -%}
{{- base_color -}}
{%- endif -%}
{%- else -%}
{{- base_color -}}
{%- endif -%}
{%- endmacro -%}
- 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
}
]
- In your homeassistant
configuration.yaml add:
openhasp:
display02: # depends on you given device Name!!!
objects:
# SEITE 0
- obj: "p0b0" # SETTINGS
properties:
"jsonl": >
{% from 'openhasp.jinja' import color,grid,h,w,x,y %}
{% set ns_colors = namespace(primary=color('primary')|default('#000000'), primary_light=color('primary_light')|default('#FFFFFF'), primary_dark=color('primary_dark')|default('#000000'), secondary=color('secondary')|default('#000000'), secondary_light=color('secondary_light')|default('#FFFFFF'), secondary_dark=color('secondary_dark')|default('#000000'), accent=color('accent')|default('#000000'), accent_light=color('accent_light')|default('#FFFFFF'), accent_dark=color('accent_dark')|default('#000000'), error=color('error')|default('#000000'), error_light=color('error_light')|default('#FFFFFF'), error_dark=color('error_dark')|default('#000000'), warning=color('warning')|default('#000000'), warning_light=color('warning_light')|default('#FFFFFF'), success=color('success')|default('#000000'), success_light=color('success_light')|default('#FFFFFF'), bg=color('bg')|default('#000000')) %}
{{
{
'bg_color' : ns_colors.bg
} |to_json |string
}}
- obj: "p0b1" # PREVIOUS - BUTTON"
properties:
"jsonl": >
{% from 'openhasp.jinja' import color,grid,h,w,x,y %}
{% set ns = namespace(w=w(4), h=h(2), x=x(1), y=y(11), g=grid()) %}
{% set ns_colors = namespace(primary=color('primary')|default('#000000'), primary_light=color('primary_light')|default('#FFFFFF'), primary_dark=color('primary_dark')|default('#000000'), secondary=color('secondary')|default('#000000'), secondary_light=color('secondary_light')|default('#FFFFFF'), secondary_dark=color('secondary_dark')|default('#000000'), accent=color('accent')|default('#000000'), accent_light=color('accent_light')|default('#FFFFFF'), accent_dark=color('accent_dark')|default('#000000'), error=color('error')|default('#000000'), error_light=color('error_light')|default('#FFFFFF'), error_dark=color('error_dark')|default('#000000'), warning=color('warning')|default('#000000'), warning_light=color('warning_light')|default('#FFFFFF'), success=color('success')|default('#000000'), success_light=color('success_light')|default('#FFFFFF'), bg=color('bg')|default('#000000')) %}
{{
{
'w': ns.w | default(0) | int,
'h': ns.h | default(0) | int,
'x': ns.x | default(0) | int,
'y': ns.y | default(0) | int,
'radius': (ns.g | default('{}') | from_json)['radius'] | default(0) | int,
'bg_color': ns_colors.primary,
'bg_color02': ns_colors.primary_light,
'text': "\uE04D"
} |to_json |string
}}
- obj: "p0b2" # HOME - BUTTON
properties:
"jsonl": >
{% from 'openhasp.jinja' import grid,w,h,x,y,color %}
{% set ns = namespace(w=w(4), h=h(2), x=x(5), y=y(11), g=grid()) %}
{% set ns_colors = namespace(primary=color('primary')|default('#000000'), primary_light=color('primary_light')|default('#FFFFFF'), primary_dark=color('primary_dark')|default('#000000'), secondary=color('secondary')|default('#000000'), secondary_light=color('secondary_light')|default('#FFFFFF'), secondary_dark=color('secondary_dark')|default('#000000'), accent=color('accent')|default('#000000'), accent_light=color('accent_light')|default('#FFFFFF'), accent_dark=color('accent_dark')|default('#000000'), error=color('error')|default('#000000'), error_light=color('error_light')|default('#FFFFFF'), error_dark=color('error_dark')|default('#000000'), warning=color('warning')|default('#000000'), warning_light=color('warning_light')|default('#FFFFFF'), success=color('success')|default('#000000'), success_light=color('success_light')|default('#FFFFFF'), bg=color('bg')|default('#000000')) %}
{{
{
'w': ns.w | default(0) | int,
'h': ns.h | default(0) | int,
'x': ns.x | default(0) | int,
'y': ns.y | default(0) | int,
'radius': (ns.g | default('{}') | from_json)['radius'] | default(0) | int,
'bg_color': ns_colors.primary,
'bg_color02': ns_colors.primary_light,
'text': "\uE6A1"
} |to_json |string
}}
- obj: "p0b3" # NEXT - BUTTON
properties:
"jsonl": >
{% from 'openhasp.jinja' import grid,w,h,x,y,color %}
{% set ns = namespace(w=w(4), h=h(2), x=x(9), y=y(11), g=grid()) %}
{% set ns_colors = namespace(primary=color('primary')|default('#000000'), primary_light=color('primary_light')|default('#FFFFFF'), primary_dark=color('primary_dark')|default('#000000'), secondary=color('secondary')|default('#000000'), secondary_light=color('secondary_light')|default('#FFFFFF'), secondary_dark=color('secondary_dark')|default('#000000'), accent=color('accent')|default('#000000'), accent_light=color('accent_light')|default('#FFFFFF'), accent_dark=color('accent_dark')|default('#000000'), error=color('error')|default('#000000'), error_light=color('error_light')|default('#FFFFFF'), error_dark=color('error_dark')|default('#000000'), warning=color('warning')|default('#000000'), warning_light=color('warning_light')|default('#FFFFFF'), success=color('success')|default('#000000'), success_light=color('success_light')|default('#FFFFFF'), bg=color('bg')|default('#000000')) %}
{{
{
'w': ns.w | default(0) | int,
'h': ns.h | default(0) | int,
'x': ns.x | default(0) | int,
'y': ns.y | default(0) | int,
'radius': (ns.g | default('{}') | from_json)['radius'] | default(0) | int,
'bg_color': ns_colors.primary,
'bg_color02': ns_colors.primary_light,
'text': "\uE054"
} |to_json |string
}}
# SEITE 1
- obj: "p1b0" # SETTINGS
properties:
"jsonl": >
{% from 'openhasp.jinja' import color,grid,h %}
{% set ns_colors = namespace(primary=color('primary')|default('#000000'), primary_light=color('primary_light')|default('#FFFFFF'), primary_dark=color('primary_dark')|default('#000000'), secondary=color('secondary')|default('#000000'), secondary_light=color('secondary_light')|default('#FFFFFF'), secondary_dark=color('secondary_dark')|default('#000000'), accent=color('accent')|default('#000000'), accent_light=color('accent_light')|default('#FFFFFF'), accent_dark=color('accent_dark')|default('#000000'), error=color('error')|default('#000000'), error_light=color('error_light')|default('#FFFFFF'), error_dark=color('error_dark')|default('#000000'), warning=color('warning')|default('#000000'), warning_light=color('warning_light')|default('#FFFFFF'), success=color('success')|default('#000000'), success_light=color('success_light')|default('#FFFFFF'), bg=color('bg')|default('#000000')) %}
{{
{
'bg_color': ns_colors.bg
} |to_json |string
}}
- obj: "p1b2" # CONTAINER
properties:
"jsonl": >
{% from 'openhasp.jinja' import color,grid,h,w,x,y %}
{% set ns = namespace(h=h(10), g=grid()) %}
{{
{
'w': (grid() | default('{}') | from_json)['display_width'] | default(0) | int,
'h': (h(10) if h(10) is not none else 0) | int,
'x': 0| default(0) | int,
'y': 0| default(0) | int
} |to_json |string
}}
-
Restart The Display and/or
reload_config_entry for your device to see your changes
-
From now on youâre on your own. Feel free to share yours
Here is my display: