2018-08-22 22:05:28 +02:00
Display Component
=================
2018-11-14 22:12:27 +01:00
.. seo ::
:description: Instructions for setting up the display integration.
2018-11-19 18:32:16 +01:00
:image: folder-open.png
2018-11-14 22:12:27 +01:00
2019-02-16 23:25:23 +01:00
The `` display `` component houses ESPHome's powerful rendering and display
2018-08-24 22:44:01 +02:00
engine. Fundamentally, there are these types of displays:
2018-08-22 22:05:28 +02:00
2018-08-24 22:44:01 +02:00
- Text based displays like :doc: `7-Segment displays <max7219>` or
2019-02-27 10:10:09 +01:00
:doc: `some LCD displays <lcd_display>` .
2018-08-24 22:44:01 +02:00
- Displays like the :doc: `nextion` that have their own processors for rendering.
2018-08-22 22:05:28 +02:00
- Binary displays which can toggle ON/OFF any pixel, like :doc: `E-Paper displays <waveshare_epaper>` or
2019-02-27 10:10:09 +01:00
:doc: `OLED displays <ssd1306>` .
2018-08-22 22:05:28 +02:00
2019-02-16 23:25:23 +01:00
For the last type, ESPHome has a powerful rendering engine that can do
2018-08-22 22:05:28 +02:00
many things like draw some basic shapes, print text with any font you want, or even show images.
2019-02-16 23:25:23 +01:00
To achieve all this flexibility displays tie in directly into ESPHome's :ref: `lambda system <config-lambda>` .
2018-08-22 22:05:28 +02:00
So when you want to write some text or sensor values to the screen you will be writing in C++ code
using an API that is designed to
- be simple and to be used without programming experience
- but also be flexible enough to work with more complex tasks like displaying an analog clock.
.. _display-engine:
Display Rendering Engine
------------------------
2019-02-16 23:25:23 +01:00
In this section we will be discussing how to use ESPHome's display rendering engine from ESPHome
2018-08-22 22:05:28 +02:00
and some basic commands. Please note that this only applies to displays that can control each pixel
individually.
2019-02-16 23:25:23 +01:00
So, first a few basics: When setting up a display platform in ESPHome there will be a configuration
option called `` lambda: `` which will be called every time ESPHome wants to re-render the display.
In there, you can write code like in any :ref: `lambda <config-lambda>` in ESPHome. Display
2018-08-22 22:05:28 +02:00
lambdas are additionally passed a variable called `` it `` which represents the rendering engine object.
2018-11-19 18:32:16 +01:00
.. code-block :: yaml
2018-08-22 22:05:28 +02:00
display:
- platform: ...
# ...
lambda: |-
// Write your display rendering code here
// For example, draw a line from [x=0,y=0] to [x=50,y=50]
it.line(0, 0, 50, 50);
.. note ::
Lambdas are essentially just a lightly modified version of C++. So don't forget to end each line
with a semicolon (`` ; `` ). Otherwise you will be greeted by a long error message at the compilation stage.
If you compile and upload the configuration above, you should see a black (or white, depending on the display)
line which starts at the top left and goes a few pixels down at a 45° angle. (If it's in another corner, use the
`` rotation: `` option to rotate the display to your liking)
This already highlights one of the things you must learn before diving into writing your own custom display code:
The **top left** is always the origin of the pixel coordinate system. Also, all points in this coordinate system
are a pair of integers like `` 50, 50 `` which represent the shift to the right and shift downwards. So, in other words,
x always represents the horizontal axis (width) and y the vertical axis (height). And the convention in
the rendering engine is always first specify the `` x `` coordinate and then the `` y `` coordinate.
Basic Shapes
2018-08-24 22:44:01 +02:00
***** ***** **
2018-08-22 22:05:28 +02:00
2019-02-16 23:25:23 +01:00
Now that you know a bit more about ESPHome's coordinate system, let's draw some basic shapes like lines, rectangles
2018-08-22 22:05:28 +02:00
and circles:
2018-11-19 18:32:16 +01:00
.. code-block :: yaml
2018-08-22 22:05:28 +02:00
display:
- platform: ...
# ...
lambda: |-
// Draw a line from [0,0] to [100,50]
it.line(0, 0, 100, 50);
// Draw the outline of a rectangle with the top left at [50,60], a width of 30 and a height of 42
it.rectangle(50, 60, 30, 42);
// Draw the same rectangle, but this time filled.
it.filled_rectangle(50, 60, 30, 42);
// Circles! Let's draw one with the center at [25,25] and a radius of 10
it.circle(25, 25, 10);
// ... and the same thing filled again
it.filled_circle(25, 25, 10);
All the above methods can optionally also be called with an argument at the end which specifies in which
color to draw. Currently, only `` COLOR_ON `` (the default if color is not given) and `` COLOR_OFF `` are supported because
2019-02-16 23:25:23 +01:00
ESPHome only has implemented binary displays.
2018-08-22 22:05:28 +02:00
2018-11-19 18:32:16 +01:00
.. code-block :: yaml
2018-08-22 22:05:28 +02:00
display:
- platform: ...
# ...
lambda: |-
// Turn the whole display on.
it.fill(COLOR_ON);
// Turn the whole display off.
it.fill(COLOR_OFF);
// Turn a single pixel off at [50,60]
it.draw_pixel_at(50, 60, COLOR_OFF);
// Turn off a whole display portion.
it.rectangle(50, 50, 30, 42, COLOR_OFF);
Additionally, you have access to two helper methods which will fetch the width and height of the display:
2018-11-19 18:32:16 +01:00
.. code-block :: yaml
2018-08-22 22:05:28 +02:00
display:
- platform: ...
# ...
lambda: |-
// Draw a circle in the middle of the display
2020-12-21 01:55:44 +01:00
it.filled_circle(it.get_width() / 2, it.get_height() / 2, 20);
2018-08-22 22:05:28 +02:00
2019-02-07 13:54:45 +01:00
You can view the full API documentation for the rendering engine in the "API Reference" in the See Also section.
2018-08-22 22:05:28 +02:00
.. _display-static_text:
Drawing Static Text
2018-08-24 22:44:01 +02:00
***** ***** ***** *** *
2018-08-22 22:05:28 +02:00
2019-02-16 23:25:23 +01:00
The rendering engine also has a powerful font drawer which integrates seamlessly into ESPHome.
2020-05-10 21:27:59 +02:00
Whereas in most Arduino display projects you have to use one of a few pre-defined fonts in very
specific sizes, with ESPHome you have the option to use **any** TrueType (`` .ttf `` ) font file
2018-08-22 22:05:28 +02:00
at **any** size! Granted the reason for it is actually not having to worry about the licensing of font files :)
2019-02-16 23:25:23 +01:00
To use fonts you first have to define a font object in your ESPHome configuration file. Just grab
2018-08-22 22:05:28 +02:00
a `` .ttf `` file from somewhere on the Internet and create a `` font: `` section in your configuration:
2018-11-19 18:32:16 +01:00
.. code-block :: yaml
2018-08-22 22:05:28 +02:00
font:
- file: "Comic Sans MS.ttf"
id: my_font
size: 20
display:
# ...
Configuration variables:
2020-05-10 21:27:59 +02:00
- **file** (**Required** , string): The path (relative to where the .yaml file is) of the TrueType font
2018-08-22 22:05:28 +02:00
file.
- **id** (**Required** , :ref: `config-id` ): The ID with which you will be able to reference the font later
in your display code.
- **size** (*Optional* , int): The size of the font in pt (not pixel!).
If you want to use the same font in different sizes, create two font objects. Defaults to `` 20 `` .
- **glyphs** (*Optional* , list): A list of characters you plan to use. Only the characters you specify
here will be compiled into the binary. Adjust this if you need some special characters or want to
reduce the size of the binary if you don't plan to use some glyphs. The items in the list can also
be more than one character long if you for example want to use font ligatures. Defaults to
`` !"%()+,-_.:°0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz `` .
.. note ::
2019-02-16 23:25:23 +01:00
To use fonts you will need to have the python `` pillow `` package installed, as ESPHome uses that package
2020-05-10 21:27:59 +02:00
to translate the TrueType files into an internal format. If you're running this as a Hass.io add-on or with
2019-02-16 23:25:23 +01:00
the official ESPHome docker image, it should already be installed. Otherwise you need to install it using
2019-10-19 22:16:09 +02:00
`` pip install pillow `` .
2018-08-22 22:05:28 +02:00
Then, in your display code just reference the font like so:
2018-11-19 18:32:16 +01:00
.. code-block :: yaml
2018-08-22 22:05:28 +02:00
display:
- platform: ...
# ...
lambda: |-
// Print the string "Hello World!" at [0,10]
it.print(0, 10, id(my_font), "Hello World!");
2019-02-16 23:25:23 +01:00
By default, ESPHome will *align* the text at the top left. That means if you enter the coordinates
2018-08-22 22:05:28 +02:00
`` [0,10] `` for your text, the top left of the text will be at `` [0,10] `` . If you want to draw some
text at the right side of the display, it is however sometimes useful to choose a different **text alignment** .
2019-02-16 23:25:23 +01:00
When you enter `` [0,10] `` you're really telling ESPHome that it should position the **anchor point** of the text
2018-08-22 22:05:28 +02:00
at `` [0,10] `` . When using a different alignment, like `` TOP_RIGHT `` , the text will be positioned left of the anchor
pointed, so that, as the name implies, the anchor point is a the *top right* corner of the text.
2018-11-19 18:32:16 +01:00
.. code-block :: yaml
2018-08-22 22:05:28 +02:00
display:
- platform: ...
# ...
lambda: |-
// Aligned on left by default
it.print(0, 0, id(my_font), "Left aligned");
// Aligned on right edge
2018-11-23 11:17:40 +01:00
it.print(it.get_width(), 0, id(my_font), TextAlign::TOP_RIGHT, "Right aligned");
2018-08-22 22:05:28 +02:00
As with basic shapes, you can also specify a color for the text:
2018-11-19 18:32:16 +01:00
.. code-block :: yaml
2018-08-22 22:05:28 +02:00
display:
- platform: ...
# ...
lambda: |-
// Syntax is always: it.print(<x>, <y>, <font>, [color=COLOR_ON], [align=TextAlign::TOP_LEFT], <text>);
it.print(0, 0, id(my_font), COLOR_ON, "Left aligned");
.. _display-printf:
Formatted Text
2018-08-24 22:44:01 +02:00
***** ***** *** *
2018-08-22 22:05:28 +02:00
Static text by itself is not too impressive. What we really want is to display *dynamic* content like sensor values
2019-02-16 23:25:23 +01:00
on the display!. That's where `` printf `` comes in. `` printf `` is a formatting engine from the C era and ESPHome
2018-08-22 22:05:28 +02:00
chose to use because ... well, I'm too lazy to create a fully-fledged format engine where the existing stuff
is way better documented :)
`` printf `` can do way more stuff than you will probably ever need, but it's also quite simple for the basic stuff.
For example, a printf call can look like this:
2018-11-19 18:32:16 +01:00
.. code-block :: yaml
2018-08-22 22:05:28 +02:00
sensor:
- platform: ...
# ...
id: my_sensor
display:
- platform: ...
# ...
lambda: |-
2018-10-20 14:53:27 +02:00
it.printf(0, 0, id(my_font), "The sensor value is: %.1f", id(my_sensor).state);
2018-08-22 22:05:28 +02:00
// If the sensor has the value 30.02, the result will be: "The sensor value is: 30.0"
As you can see, when you call `` printf `` most of the string is printed as-is, but when this weird percent sign with some
2018-10-20 14:53:27 +02:00
stuff after it is encountered, it is magically replaced by the argument after the format (here `` id(my_sensor).state `` ).
2018-08-22 22:05:28 +02:00
Every time you type a percent sign `` % `` in a printf format string, it will treat the following letters as a format tag
2019-02-27 18:32:47 +01:00
until a so-called "specifier" is encountered (in this case `` f `` ). You can read more about it
`here <https://www.tutorialspoint.com/c_standard_library/c_function_printf.htm> `__ ,
2019-02-16 23:25:23 +01:00
but for ESPHome there are really just a few things you need to know.
2018-08-22 22:05:28 +02:00
Let's break `` %.1f `` down:
- `` % `` - initiate the format string
- `` .1 `` - round the decimal number to `` 1 `` digits after the decimal point.
- `` f `` - the specifier which tells printf the data type of the argument. Here it is a f(loat).
For example, if you would like to print a sensor value with two digits of accuracy, you would write `` %.2f `` and with
zero digits of accuracy (without a decimal) `` %.0f `` .
Another interesting format string is `` %7.2f `` , which would become the right-justified string
`` " 20.51" `` for a value of 20.506.
- `` % `` - initiate the format
- `` 7 `` - means that the number will be right-justified and be padded on the left by spaces if
the result would be shorter than 7 characters long.
2018-09-29 14:50:55 +02:00
- `` .2 `` - round the decimal number to `` 2 `` digits after the decimal point.
2018-08-22 22:05:28 +02:00
- `` f `` - specifier: f(loat).
You can even have as many format strings as you want in a single printf call. Just make sure the put the
arguments after the format string in the right order.
2018-11-19 18:32:16 +01:00
.. code-block :: yaml
2018-08-22 22:05:28 +02:00
display:
- platform: ...
# ...
lambda: |-
// %% - literal % sign
2018-10-20 14:53:27 +02:00
it.printf(0, 0, id(my_font), "Temperature: %.1f°C, Humidity: %.1f%%", id(temperature).state, id(humidity).state);
2018-08-22 22:05:28 +02:00
2021-01-21 23:28:15 +01:00
To display a text string from a `` text_sensor `` , append `` .c_str() `` to the end of your variable.
2018-08-22 22:05:28 +02:00
2021-01-21 23:28:15 +01:00
.. code-block :: yaml
display:
- platform: ...
# ...
lambda: |-
it.printf(0, 0, id(my_font), "Text to follow: %s", id(template_text).state.c_str());
2018-08-22 22:05:28 +02:00
The last printf tip for use in displays I will discuss here is how to display binary sensor values. You
*could* of course just check the state with an `` if `` statement as the first few lines in the example below, but if
you want to be efficient you can use an *inline if* too. With the `` %s `` print specifier you can tell it to
use any string you pass it, like `` "ON" `` or `` "OFF" `` .
2018-11-19 18:32:16 +01:00
.. code-block :: yaml
2018-08-22 22:05:28 +02:00
binary_sensor:
- platform: ...
# ...
id: my_binary_sensor
display:
- platform: ...
# ...
lambda: |-
2018-10-20 14:53:27 +02:00
if (id(my_binary_sensor).state) {
2018-08-22 22:05:28 +02:00
it.print(0, 0, id(my_font), "state: ON");
} else {
it.print(0, 0, id(my_font), "state: OFF");
}
// Shorthand:
2018-10-20 14:53:27 +02:00
it.printf(0, 0, id(my_font), "State: %s", id(my_binary_sensor).state ? "ON" : "OFF");
2018-08-22 22:05:28 +02:00
2018-10-26 22:27:01 +02:00
.. note ::
For displaying external data on the display, for example data from your Home Assistant instance,
2019-02-07 13:54:45 +01:00
you can use the :doc: `/components/text_sensor/mqtt_subscribe` (see the example there for more information).
2018-10-26 22:27:01 +02:00
2018-08-22 22:05:28 +02:00
.. _display-strftime:
Displaying Time
2018-08-24 22:44:01 +02:00
***** ***** *****
2018-08-22 22:05:28 +02:00
2021-02-20 22:02:46 +01:00
You can display current time using a time component. Please see the example :ref: `here <strftime>` .
.. _config-color:
Color
*****
When using RGB-capable displays in ESPHome you may wish to use custom colors.
A `` color `` component exists for just this purpose:
.. code-block :: yaml
color:
- id: my_light_red
red: 100%
green: 20%
blue: 25%
white: 0%
Configuration variables:
- **red** (*Optional* , percentage): The percentage of the red component. Defaults to `` 100% `` .
- **green** (*Optional* , percentage): The percentage of the green component. Defaults to `` 100% `` .
- **blue** (*Optional* , percentage): The percentage of the blue component. Defaults to `` 100% `` .
- **white** (*Optional* , percentage): The percentage of the white component. Defaults to `` 100% `` .
RGB displays use red, green, and blue, while grayscale displays may use white.
2018-08-22 22:05:28 +02:00
Images
2019-08-27 20:14:56 +02:00
***** *
2018-08-22 22:05:28 +02:00
2021-02-20 22:02:46 +01:00
Use this component to store graphical images on the device, you can then draw the images on compatible displays.
2018-11-19 18:32:16 +01:00
.. code-block :: yaml
2018-08-22 22:05:28 +02:00
image:
- file: "image.png"
id: my_image
resize: 100x100
Configuration variables:
- **file** (**Required** , string): The path (relative to where the .yaml file is) of the image file.
2018-09-29 14:50:55 +02:00
- **id** (**Required** , :ref: `config-id` ): The ID with which you will be able to reference the image later
2018-08-22 22:05:28 +02:00
in your display code.
2021-02-20 22:02:46 +01:00
- **resize** (*Optional* , string): If set, this will resize the image to fit inside the given dimensions `` WIDTHxHEIGHT ``
2018-08-22 22:05:28 +02:00
and preserve the aspect ratio.
2020-08-11 15:28:43 +02:00
- **type** (*Optional* ): Specifies how to encode image internally. Defaults to `` BINARY `` .
- `` BINARY `` : Two colors, suitable for 1 color displays or 2 color image in color displays. Uses 1 bit
per pixel, 8 pixels per byte.
2021-05-03 13:10:47 +02:00
- `` GRAYSCALE `` : Full scale grey. Uses 8 bits per pixel, 1 pixel per byte.
2020-08-11 15:28:43 +02:00
- `` RGB24 `` : Full RGB color stored. Uses 3 bytes per pixel.
2018-08-22 22:05:28 +02:00
2021-05-03 13:10:47 +02:00
- **dither** (*Optional* ): Specifies which dither method used to process the image, only used in GRAYSCALE and BINARY type image. Defaults to `` NONE `` . You can read more about it `here <https://pillow.readthedocs.io/en/stable/reference/Image.html?highlight=Dither#PIL.Image.Image.convert> `__ and `here <https://en.wikipedia.org/wiki/Dither> `__ .
2020-11-01 19:54:25 +01:00
- `` NONE `` : Every pixel convert to its nearest color.
- `` FLOYDSTEINBERG `` : Uses Floyd-Steinberg dither to approximate the original image luminosity levels.
2018-08-22 22:05:28 +02:00
.. note ::
To use images you will need to have the python `` pillow `` package installed.
2019-02-16 23:25:23 +01:00
If you're running this as a Hass.io add-on or with the official ESPHome docker image, it should already be
2019-10-19 22:16:09 +02:00
installed. Otherwise you need to install it using `` pip install pillow `` .
2018-08-22 22:05:28 +02:00
And then later in code:
2018-11-19 18:32:16 +01:00
.. code-block :: yaml
2018-08-22 22:05:28 +02:00
display:
- platform: ...
# ...
lambda: |-
// Draw the image my_image at position [x=0,y=0]
it.image(0, 0, id(my_image));
2020-08-11 15:28:43 +02:00
For binary images the `` image `` method accepts two additional color parameters which can
be supplied to modify the color used to represent the on and off bits respectively. e.g.
.. code-block :: yaml
display:
- platform: ...
# ...
lambda: |-
// Draw the image my_image at position [x=0,y=0]
// with front color red and back color blue
it.image(0, 0, id(my_image), id(red), id(blue));
You can also use this to invert images in two colors display, use `` COLOR_OFF `` then `` COLOR_ON ``
as the additional parameters.
2020-12-14 17:16:51 +01:00
Animation
***** *** *
2021-02-20 22:02:46 +01:00
Allows to use animated images on displays. Animation inherits all options from the image component.
It adds an additional lambda method: `` next_frame() `` to change the shown picture of a gif.
2020-12-14 17:16:51 +01:00
.. code-block :: yaml
animation:
- file: "animation.gif"
id: my_animation
resize: 100x100
The animation can be rendered just like the image component with the `` image() `` function of the display component.
To show the next frame of the animation call `` id(my_animation).next_frame() ``
This can be combined with all Lambdas:
.. code-block :: yaml
display:
- platform: ...
# ...
lambda: |-
//Ingress shown animation Frame.
id(my_animation).next_frame();
// Draw the animation my_animation at position [x=0,y=0]
it.image(0, 0, id(my_animation), COLOR_ON, COLOR_OFF);
.. note ::
To draw the next animation independent of Display draw cycle use an interval:
.. code-block :: yaml
interval:
- interval: 5s
then:
lambda: |-
id(my_animation).next_frame();
2021-02-20 22:02:46 +01:00
Configuration variables:
^^^^^^^^^^^^^^^^^^^^^^^^
- **file** (**Required** , string): The path (relative to where the .yaml file is) of the gif file.
- **id** (**Required** , :ref: `config-id` ): The ID with which you will be able to reference the animation later
in your display code.
- **resize** (*Optional* , string): If set, this will resize all the frames to fit inside the given dimensions `` WIDTHxHEIGHT ``
and preserve the aspect ratio.
- **type** (*Optional* ): Specifies how to encode each frame internally. Defaults to `` BINARY `` .
- `` BINARY `` : Two colors, suitable for 1 color displays or 2 color image in color displays. Uses 1 bit
per pixel, 8 pixels per byte.
- `` GREYSCALE `` : Full scale grey. Uses 8 bits per pixel, 1 pixel per byte.
- `` RGB24 `` : Full RGB color stored. Uses 3 bytes per pixel.
- **dither** (*Optional* ): Specifies which dither method used to process each frame, only used in GREYSCALE and BINARY type image.
Defaults to `` NONE `` . You can read more about it `here <https://pillow.readthedocs.io/en/stable/reference/Image.html?highlight=Dither#PIL.Image.Image.convert> `__
and `here <https://en.wikipedia.org/wiki/Dither> `__ .
- `` NONE `` : Every pixel convert to its nearest color.
- `` FLOYDSTEINBERG `` : Uses Floyd-Steinberg dither to approximate the original image luminosity levels.
2019-02-22 21:17:31 +01:00
.. _display-pages:
Display Pages
-------------
Certain display types also allow you to show "pages". With pages you can create drawing lambdas
that you can switch between. For example with pages you can set up 3 screens, each with
different content, and switch between them on a timer.
.. code-block :: yaml
display:
- platform: ...
# ...
id: my_display
pages:
- id: page1
lambda: |-
it.print(0, 10, id(my_font), "This is page 1!");
- id: page2
lambda: |-
it.print(0, 10, id(my_font), "This is page 2!");
You can then switch between these with three different actions:
2019-05-01 17:15:51 +02:00
**show_next** / **show_previous** : Shows the next or previous page, wraps around at the end.
2019-02-22 21:17:31 +01:00
.. code-block :: yaml
on_...:
- display.page.show_next: my_display
2019-05-01 17:15:51 +02:00
- display.page.show_previous: my_display
2019-02-22 21:17:31 +01:00
# For example cycle through pages on a timer
interval:
- interval: 5s
then:
- display.page.show_next: my_display
- component.update: my_display
**display.page.show** : Show a specific page
.. code-block :: yaml
on_...:
- display.page.show: page1
# Templated
- display.page.show: !lambda |-
if (id(my_binary_sensor).state) {
return id(page1);
} else {
return id(page2);
}
.. note ::
To trigger a redraw right after the page show use a :ref: `component.update <component-update_action>`
action:
.. code-block :: yaml
# For example cycle through pages on a timer
interval:
- interval: 5s
then:
- display.page.show_next: my_display
- component.update: my_display
2021-02-20 22:02:46 +01:00
2018-08-22 22:05:28 +02:00
See Also
--------
2019-05-12 22:44:59 +02:00
- :apiref: `display/display_buffer.h`
2019-02-07 13:54:45 +01:00
- :ghedit: `Edit`
2018-08-22 22:05:28 +02:00
.. toctree ::
:maxdepth: 1
2018-10-20 14:53:27 +02:00
:glob:
2018-08-22 22:05:28 +02:00
2018-10-20 14:53:27 +02:00
*