LVGL:技巧与窍门
以下是几个有趣的食谱,展示你可以用 ESPHome 中的 Lvgl 实现的各种效果。
NOTE
以下许多示例都会在 Home Assistant 中调用服务动作;然而,Home Assistant 默认不允许此类动作
调用。对于每个会调用动作的 ESPHome 设备,你必须在 Home
Assistant 中显式启用此设置。可以在设备初次被采用时设置,也可以通过 ESPHome 集成中“devices”
列表里的 Configure 选项来设置。
NOTE
以下示例假定你已经使用显示器及其输入设备正确配置了 LVGL,并且你有
在 ESPHome 中设置各种组件的知识。部分示例使用绝对定位,适用于 240x320px 尺寸的屏幕
;如果你的显示屏尺寸不同,则需要调整以获得预期效果。
本地灯光开关
Section titled “本地灯光开关”将 LVGL switch 控件与一个开关或灯光集成的最简单方式是使用
自动化:
light: - platform: ... id: local_light name: 'Local light' on_state: - lvgl.widget.update: id: light_switch state: checked: !lambda return id(local_light).current_values.is_on();
lvgl: ... pages: - id: main_page widgets: - switch: align: CENTER id: light_switch on_click: light.toggle: local_light远程灯光按钮
Section titled “远程灯光按钮”
如果你想从一个可切换(复选)的
button 控制一个在 Home Assistant 中作为实体出现的远程灯光,
首先需要将灯光状态导入 ESPHome,然后通过动作调用来控制它:
binary_sensor: - platform: homeassistant id: remote_light entity_id: light.remote_light publish_initial_state: true on_state: then: lvgl.widget.update: id: light_btn state: checked: !lambda return x;
lvgl: ... pages: - id: room_page widgets: - button: id: light_btn align: CENTER width: 100 height: 70 checkable: true widgets: - label: align: CENTER text: 'Remote light' on_click: - homeassistant.action: action: light.toggle data: entity_id: light.remote_light灯光亮度滑块
Section titled “灯光亮度滑块”
你可以使用 slider 或 arc 来控制可调光灯光的亮度。
我们可以使用一个传感器来读取灯光的当前亮度,它在 Home Assistant 中作为实体的属性,以整数形式存储,取值范围在 0(最小值)和 255(最大值)之间。相应地设置滑块的 min_value
和 max_value 会很方便。
sensor: - platform: homeassistant id: light_brightness entity_id: light.your_dimmer attribute: brightness on_value: - lvgl.slider.update: id: dimmer_slider value: !lambda return x;
lvgl: ... pages: - id: room_page widgets: - slider: id: dimmer_slider x: 20 y: 50 width: 20 height: 220 pad_all: 8 min_value: 0 max_value: 255 ext_click_area: 20 on_release: - homeassistant.action: action: light.turn_on data: entity_id: light.your_dimmer brightness: !lambda return int(x);ext_click_area 选项用于将控件周围的触摸区域放大 20 像素。
NOTE
Home Assistant 在 light.turn_on 动作调用的 brightness 参数处期望一个整数,而 ESPHome 使用浮点数,因此 x 需要进行转换。
这也适用于 fan.set_percentage 或 valve.set_valve_position 之类的动作调用;唯一区别是 max_value 必须为 100。
媒体播放器音量滑块
Section titled “媒体播放器音量滑块”
类似地,你可以使用 slider 或 arc 来控制 媒体播放器的音量级别,后者使用的是浮点数值。
通过一个传感器,我们读取媒体播放器的当前音量级别,它在 Home Assistant 中作为实体的属性,是一个介于 0(最小值)和 1(最大值)之间的浮点值。由于 LVGL 只处理整数,
因此将滑块的可选值设为 0 到 100 会更方便。因此需要在两者之间进行转换,即从 Home Assistant 读取值时要乘以 100,通过动作调用设置音量时要除以 100 :
sensor: - platform: homeassistant id: media_player_volume entity_id: media_player.your_room attribute: volume_level on_value: - lvgl.slider.update: id: slider_media_player value: !lambda return (x * 100);
lvgl: ... pages: - id: mediapplayer_page widgets: - slider: id: slider_media_player x: 60 y: 50 width: 30 height: 220 pad_all: 8 min_value: 0 max_value: 100 adv_hittest: true on_value: - homeassistant.action: action: media_player.volume_set data: entity_id: media_player.your_room volume_level: !lambda return (x / 100);adv_hittest 选项确保对屏幕的意外触碰不会导致音量突然变化(更多细节
参见 slider 文档)。
NOTE
请注意,on_value 会在滑块拖动过程中被持续触发。这通常会对性能
产生负面影响。例如,你不应该用这个触发器通过 Modbus 设置热泵的目标温度,
或设置电动帘盖的位置,因为很可能导致故障。为缓解此问题,
可以考虑使用像 on_release 这样的通用控件触发器,在交互完成后一次性获取 x 变量。
一个与 Home Assistant 在能量仪表盘中显示的类似的仪表,可以通过 meter
和 label 控件实现:
这里的技巧是有一个父 obj,其中包含其他控件作为子控件。我们在中间
放置一个 meter,它由一个 line 指示器和两个 arc 控件组成。我们使用
另一个更小的 obj 覆盖在上方,以隐藏指示器的中心部分,并放置一些
label 控件来显示数字信息:
sensor: - platform: ... id: values_between_-10_and_10 on_value: - lvgl.indicator.update: id: val_needle value: !lambda return x; - lvgl.label.update: id: val_text text: format: "%.0f" args: [ 'x' ]lvgl: ... pages: - id: gauge_page widgets: - obj: height: 240 width: 240 align: CENTER bg_color: 0xFFFFFF border_width: 0 pad_all: 4 widgets: - meter: height: 100% width: 100% border_width: 0 bg_opa: TRANSP indicator: bg_opa: TRANSP align: CENTER scales: - range_from: -10 range_to: 10 angle_range: 180 # 将总角度设置为 180 = 从中左开始到中右结束 indicators: - arc: # 刻度背景的前半部分 color: 0xFF3000 width: 31 start_value: -10 end_value: 0 - arc: # 刻度背景的后半部分 color: 0x00FF00 width: 31 start_value: 0 end_value: 10 - line: # 指针 id: val_needle width: 8 value: -3 rounded: false length: 33 radial_offset: 73 - label: # 仪表数字指示器 id: val_text text_font: montserrat_48 align: CENTER y: -5 text: "-3" - label: # 下范围指示器 text_font: montserrat_18 align: CENTER y: 8 x: -90 text: "-10" - label: # 上范围指示器 text_font: montserrat_18 align: CENTER y: 8 x: 90 text: "+10"一个带有精密仪表的温度计,同样由 meter 控件和一个使用
label 的数字显示组成:
每当传感器发来新值时,我们都会更新指针指示器以及
label 中的文本。由于 LVGL 只在 meter 的刻度上处理整数值,
但传感器的值是一个 float,我们采用与上述示例相同的方法;将传感器的
值乘以 10 并送入 meter。这本质上是上下叠放的两个刻度:一个根据
乘积后的值设置指针,另一个在 label 中显示传感器的原始值。
sensor: - platform: ... id: outdoor_temperature on_value: - lvgl.indicator.update: id: temperature_needle value: !lambda return x * 10; - lvgl.label.update: id: temperature_text text: format: "%.1f°C" args: [ 'x' ]lvgl: ... pages: - id: meter_page widgets: - meter: align: CENTER height: 180 width: 180 scales: - range_from: -10 # 值标签的刻度 range_to: 40 angle_range: 240 rotation: 150 ticks: width: 1 count: 51 length: 10 color: 0x000000 major: stride: 5 width: 2 length: 10 color: 0x404040 label_gap: 10 indicators: - tick_style: start_value: -10 end_value: 40 color_start: 0x0000bd color_end: 0xbd0000 width: 1 - range_from: -100 # 指针值的刻度 range_to: 400 angle_range: 240 rotation: 150 indicators: - line: id: temperature_needle width: 2 color: 0xFF0000 length: 96% widgets: - label: id: temperature_text text: "-.-°C" align: CENTER y: 45 - label: text: "Outdoor" align: CENTER y: 65以下是同样的传感器配置,但改为带有渐变背景的半圆仪表,通过大量刻度绘制:
如果你改变控件的大小,为了获得均匀的渐变,请相应地增加或减少刻度数量。
lvgl: ... pages: - id: meter_page widgets: - obj: height: 240 width: 240 align: CENTER y: -18 bg_color: 0xFFFFFF border_width: 0 pad_all: 14 widgets: - meter: height: 100% width: 100% border_width: 0 align: CENTER bg_opa: TRANSP indicator: bg_opa: TRANSP scales: - range_from: -15 range_to: 35 angle_range: 180 ticks: count: 100 width: 10 length: 30 radial_offset: 2 indicators: - tick_style: start_value: -15 end_value: 35 color_start: 0x3399ff color_end: 0xffcc66 - range_from: -150 range_to: 350 angle_range: 180 indicators: - line: id: temperature_needle2 width: 8 value: -150 rounded: false length: 32 radial_offset: 61 - label: id: temperature_text text: "--.-°C" align: CENTER y: -26 - label: text: "Outdoor" align: CENTER y: -6spinbox 是控制恒温器的理想控件:
首先我们从 Home Assistant 导入气候组件的当前目标温度,并在它每次变化时用它的值更新 spinbox。我们使用两个带有减号和加号的按钮来控制 spinbox,每当改变其值时,我们只需 简单地调用一个 Home Assistant 动作来设置气候组件的新目标温度。
sensor: - platform: homeassistant id: room_thermostat entity_id: climate.room_thermostat attribute: temperature on_value: - lvgl.spinbox.update: id: spinbox_id value: !lambda return x;
lvgl: ... pages: - id: thermostat_control widgets: - obj: align: BOTTOM_MID y: -50 layout: type: FLEX flex_flow: ROW flex_align_cross: CENTER width: SIZE_CONTENT height: SIZE_CONTENT widgets: - button: id: spin_down on_click: - lvgl.spinbox.decrement: spinbox_id widgets: - label: text: "-" - spinbox: id: spinbox_id align: CENTER text_align: CENTER width: 50 range_from: 15 range_to: 35 selected_digit: 0 rollover: false digits: 3 decimal_places: 1 on_value: then: - homeassistant.action: action: climate.set_temperature data: temperature: !lambda return x; entity_id: climate.room_thermostat - button: id: spin_up on_click: - lvgl.spinbox.increment: spinbox_id widgets: - label: text: "+"帘盖状态与控制
Section titled “帘盖状态与控制”要为控制 Home Assistant 帘盖制作一个漂亮的用户界面,你可以使用 3 个按钮,它们同时还会 显示状态。
与前面的示例一样,我们首先需要获取帘盖的状态。我们将使用一个数值传感器来读取 帘盖的当前位置,并使用一个文本传感器来读取其当前运动状态。我们特别关注 运动状态(opening 和 closing),因为在运动期间我们希望将中间的标签更改为 显示 STOP。否则,该按钮标签将显示开启的百分比。此外,根据帘盖是完全打开还是完全关闭, 我们将更改 UP 和 DOWN 按钮上标签的不透明度。
sensor: - platform: homeassistant id: cover_myroom_pos entity_id: cover.myroom attribute: current_position on_value: - if: condition: lambda: |- return x == 100; then: - lvgl.widget.update: id: cov_up_myroom text_opa: 60% else: - lvgl.widget.update: id: cov_up_myroom text_opa: 100% - if: condition: lambda: |- return x == 0; then: - lvgl.widget.update: id: cov_down_myroom text_opa: 60% else: - lvgl.widget.update: id: cov_down_myroom text_opa: 100%
text_sensor: - platform: homeassistant id: cover_myroom_state entity_id: cover.myroom on_value: - if: condition: lambda: |- return (x == "opening" or x == "closing"); then: - lvgl.label.update: id: cov_stop_myroom text: "STOP" else: - lvgl.label.update: id: cov_stop_myroom text: format: "%.0f%%" args: [ 'id(cover_myroom_pos).get_state()' ]
lvgl: ... pages: - id: room_page widgets: - label: x: 10 y: 6 width: 70 text: "My room" text_align: CENTER - button: x: 10 y: 30 width: 70 height: 68 widgets: - label: id: cov_up_myroom align: CENTER text: "\uF077" on_press: then: - homeassistant.action: action: cover.open data: entity_id: cover.myroom - button: x: 10 y: 103 width: 70 height: 68 widgets: - label: id: cov_stop_myroom align: CENTER text: STOP on_press: then: - homeassistant.action: action: cover.stop data: entity_id: cover.myroom - button: x: 10 y: 178 width: 70 height: 68 widgets: - label: id: cov_down_myroom align: CENTER text: "\uF078" on_press: then: - homeassistant.action: action: cover.close data: entity_id: cover.myroom主题与样式定义
Section titled “主题与样式定义”由于 LVGL 使用继承来在控件之间应用样式,因此可以在顶层应用它们,并且只在需要时 进行修改。
在此示例中,我们在 theme 中准备了一组渐变样式,并在 style_definition 中 做一些修改,可以批量应用到所需控件上。主题会自动应用,并且可以通过样式定义手动 覆盖(继续往下阅读以了解方法)。
lvgl: ... theme: label: text_font: my_font # 将所有标签设置为使用你自定义的字体 button: bg_color: 0x2F8CD8 bg_grad_color: 0x005782 bg_grad_dir: VER bg_opa: COVER border_color: 0x0077b3 border_width: 1 text_color: 0xFFFFFF pressed: # 设置一些按钮在按下状态下的颜色不同 bg_color: 0x006699 bg_grad_color: 0x00334d checked: # 设置一些按钮在选中状态下的颜色不同 bg_color: 0x1d5f96 bg_grad_color: 0x03324A text_color: 0xfff300 buttonmatrix: bg_opa: TRANSP border_color: 0x0077b3 border_width: 0 text_color: 0xFFFFFF pad_all: 0 items: # 将所有 buttonmatrix 按钮设置为使用你自定义的样式和字体 bg_color: 0x2F8CD8 bg_grad_color: 0x005782 bg_grad_dir: VER bg_opa: COVER border_color: 0x0077b3 border_width: 1 text_color: 0xFFFFFF text_font: my_font pressed: bg_color: 0x006699 bg_grad_color: 0x00334d checked: bg_color: 0x1d5f96 bg_grad_color: 0x03324A text_color: 0x005580 switch: bg_color: 0xC0C0C0 bg_grad_color: 0xb0b0b0 bg_grad_dir: VER bg_opa: COVER checked: bg_color: 0x1d5f96 bg_grad_color: 0x03324A bg_grad_dir: VER bg_opa: COVER knob: bg_color: 0xFFFFFF bg_grad_color: 0xC0C0C0 bg_grad_dir: VER bg_opa: COVER slider: border_width: 1 border_opa: 15% bg_color: 0xcccaca bg_opa: 15% indicator: bg_color: 0x1d5f96 bg_grad_color: 0x03324A bg_grad_dir: VER bg_opa: COVER knob: bg_color: 0x2F8CD8 bg_grad_color: 0x005782 bg_grad_dir: VER bg_opa: COVER border_color: 0x0077b3 border_width: 1 text_color: 0xFFFFFF style_definitions: - id: header_footer bg_color: 0x2F8CD8 bg_grad_color: 0x005782 bg_grad_dir: VER bg_opa: COVER border_opa: TRANSP radius: 0 pad_all: 0 pad_row: 0 pad_column: 0 border_color: 0x0077b3 text_color: 0xFFFFFF width: 100% height: 30请注意,样式定义也可以包含通用属性,例如定位和尺寸。
页面导航页脚
Section titled “页面导航页脚”如果使用多个页面,那么在屏幕底部有一个导航栏会很有用:
为了避免在每个页面上重复相同的控件,有 top_layer,它是位于所有页面上方的 Always on Top 透明页面。你将所有内容放在这个页面上,它就会位于所有其他页面的上方。
对于导航栏,我们可以使用 buttonmatrix。注意 header_footer 样式
定义是如何应用到控件及其子对象上的,以及在主控件上是如何手动配置一些更多样式的:
lvgl: ... top_layer: widgets: - buttonmatrix: align: bottom_mid styles: header_footer pad_all: 0 outline_width: 0 id: top_layer items: styles: header_footer rows: - buttons: - id: page_prev text: "\uF053" on_press: then: lvgl.page.previous: - id: page_home text: "\uF015" on_press: then: lvgl.page.show: main_page - id: page_next text: "\uF054" on_press: then: lvgl.page.next:为了让此示例正确显示,请使用 上文 的主题和样式选项,以及 LVGL 自身的 库 字体。
API 连接状态图标
Section titled “API 连接状态图标”顶层显示在所有页面上可见的状态图标很有用:
在下面的示例中,我们仅在建立与 Home Assistant 的连接时显示图标:
api: on_client_connected: - wait_until: condition: api.connected: state_subscription_only: true - lvgl.widget.show: lbl_hastatus on_client_disconnected: - if: condition: lambda: 'return (0 == client_info.find("Home Assistant "));' then: - lvgl.widget.hide: lbl_hastatus
lvgl: ... top_layer: widgets: - label: text: "\uF1EB" id: lbl_hastatus hidden: true align: top_right x: -2 y: 7 text_align: right text_color: 0xFFFFFFNOTE
- 控件在启动时是隐藏的,只有在由与 API 的连接触发时才会显示。
- 控件的对齐方式:由于提供了 align 选项,x 和 y 选项用于相对于计算出的位置来定位控件。
每个页面的标题栏
Section titled “每个页面的标题栏”每个页面都可以有自己的标题栏:
为了在状态图标后面放置标题栏,我们需要将它添加到每个页面中,同时包含一个带有唯一 标题的标签:
lvgl: ... pages: - id: main_page widgets: - obj: align: TOP_MID styles: header_footer widgets: - label: text: "ESPHome LVGL Display" align: CENTER text_align: CENTER text_color: 0xFFFFFF ... - id: second_page widgets: - obj: align: TOP_MID styles: header_footer widgets: - label: text: "A second page" align: CENTER text_align: CENTER text_color: 0xFFFFFF ...为了让此示例工作,请使用 上文 的主题和样式选项。
Flex 布局定位
Section titled “Flex 布局定位”布局 旨在自动定位控件,无需指定坐标来定位 每个控件。这是简化包含许多控件的配置的好方法,因为你可以省略 对齐选项。
此示例展示了一个三个帘盖的控制面板,由标签和独立按钮组成。虽然按钮矩阵 也可能适合,但你可能仍倾向于使用功能齐全的独立按钮,因为它们提供了更广泛的 自定义选项,如 帘盖状态与控制 示例中所示。这里我们 使用 Flex 布局:
lvgl: ... pages: - id: room_page widgets: - obj: # 用于所有控件的正确放置的容器对象 align: CENTER width: 240 height: 256 x: 4 y: 4 pad_all: 3 bg_opa: TRANSP border_opa: TRANSP layout: # 为子控件启用 FLEX 布局 type: FLEX flex_flow: COLUMN_WRAP # 控件顺序从左上角开始 flex_align_cross: CENTER # 它们应该居中 pad_row: 6 pad_column: 8 widgets: - label: text: "East" - button: id: but_cov_up_east width: 70 # 选择按钮尺寸,使它们 height: 68 # 在流动时很好地填充列 widgets: - label: id: cov_up_east align: CENTER text: "\uF077" text_font: montserrat_48 - button: id: but_cov_stop_east width: 70 height: 68 widgets: - label: id: cov_stop_east align: CENTER text: "\uF04D" text_font: montserrat_30 - button: id: but_cov_down_east width: 70 height: 68 widgets: - label: id: cov_down_east align: CENTER text: "\uF078" text_font: montserrat_48
- label: text: "South" - button: id: but_cov_up_south width: 70 height: 68 widgets: - label: id: cov_up_south align: CENTER text: "\uF077" text_font: montserrat_48 - button: id: but_cov_stop_south width: 70 height: 68 widgets: - label: id: cov_stop_south align: CENTER text: "\uF04D" text_font: montserrat_30 - button: id: but_cov_down_south width: 70 height: 68 widgets: - label: id: cov_down_south align: CENTER text: "\uF078" text_font: montserrat_48
- label: text: "West" - button: id: but_cov_up_west width: 70 height: 68 widgets: - label: id: cov_up_west align: CENTER text: "\uF077" text_font: montserrat_48 - button: id: but_cov_stop_west width: 70 height: 68 widgets: - label: id: cov_stop_west align: CENTER text: "\uF04D" text_font: montserrat_30 - button: id: but_cov_down_west width: 70 height: 68 widgets: - label: id: cov_down_west align: CENTER text: "\uF078" text_font: montserrat_48这让你省去大量控件定位的手动计算,否则需要依靠 x 和 y 手动放置它们!你只需要为控件确定一个公共的宽度和高度,
然后按你喜欢的分布它们。(文本中的 MDI 图标 展示了如何使用自定义
图标。)
网格布局定位
Section titled “网格布局定位”但还有更多!使用 Grid 布局,你不需要为控件指定宽度和高度。你 只需要将空间划分为行和列;控件可以自动调整大小以适应由这些行和列定义的单元格。 同样的任务,如果使用完全自动化的网格,看起来是这样的:
lvgl: ... pages: - id: room_page widgets: - obj: # 用于所有控件的正确放置的容器对象 align: CENTER width: 240 height: 256 pad_all: 6 bg_opa: TRANSP border_opa: TRANSP layout: # 为子控件启用 GRID 布局 type: GRID # 按比例划分行和列 grid_columns: [FR(1), FR(1), FR(1)] # 等分 grid_rows: [FR(10), FR(30), FR(30), FR(30)] # 类似百分比 pad_row: 6 pad_column: 8 widgets: - label: text: "East" grid_cell_column_pos: 0 # 将控件放置在 grid_cell_row_pos: 0 # 相应的单元格中 grid_cell_x_align: CENTER grid_cell_y_align: STRETCH - button: id: but_cov_up_east grid_cell_column_pos: 0 grid_cell_row_pos: 1 grid_cell_x_align: STRETCH grid_cell_y_align: STRETCH widgets: - label: id: cov_up_east align: CENTER text: "\uF077" text_font: montserrat_48 - button: id: but_cov_stop_east grid_cell_column_pos: 0 grid_cell_row_pos: 2 grid_cell_x_align: STRETCH grid_cell_y_align: STRETCH widgets: - label: id: cov_stop_east align: CENTER text: "\uF04D" text_font: montserrat_30 - button: id: but_cov_down_east grid_cell_column_pos: 0 grid_cell_row_pos: 3 grid_cell_x_align: STRETCH grid_cell_y_align: STRETCH widgets: - label: id: cov_down_east align: CENTER text: "\uF078" text_font: montserrat_48
- label: text: "South" grid_cell_column_pos: 1 grid_cell_row_pos: 0 grid_cell_x_align: CENTER grid_cell_y_align: STRETCH - button: id: but_cov_up_south grid_cell_column_pos: 1 grid_cell_row_pos: 1 grid_cell_x_align: STRETCH grid_cell_y_align: STRETCH widgets: - label: id: cov_up_south align: CENTER text: "\uF077" text_font: montserrat_48 - button: id: but_cov_stop_south grid_cell_column_pos: 1 grid_cell_row_pos: 2 grid_cell_x_align: STRETCH grid_cell_y_align: STRETCH widgets: - label: id: cov_stop_south align: CENTER text: "\uF04D" text_font: montserrat_30 - button: id: but_cov_down_south grid_cell_column_pos: 1 grid_cell_row_pos: 3 grid_cell_x_align: STRETCH grid_cell_y_align: STRETCH widgets: - label: id: cov_down_south align: CENTER text: "\uF078" text_font: montserrat_48
- label: text: "West" grid_cell_column_pos: 2 grid_cell_row_pos: 0 grid_cell_x_align: CENTER grid_cell_y_align: STRETCH - button: id: but_cov_up_west grid_cell_column_pos: 2 grid_cell_row_pos: 1 grid_cell_x_align: STRETCH grid_cell_y_align: STRETCH widgets: - label: id: cov_up_west align: CENTER text: "\uF077" text_font: montserrat_48 - button: id: but_cov_stop_west grid_cell_column_pos: 2 grid_cell_row_pos: 2 grid_cell_x_align: STRETCH grid_cell_y_align: STRETCH widgets: - label: id: cov_stop_west align: CENTER text: "\uF04D" text_font: montserrat_30 - button: id: but_cov_down_west grid_cell_column_pos: 2 grid_cell_row_pos: 3 grid_cell_x_align: STRETCH grid_cell_y_align: STRETCH widgets: - label: id: cov_down_west align: CENTER text: "\uF078" text_font: montserrat_48这里的主要优势是,每当你需要添加例如用于新帘盖的一列按钮时,你
只需简单地将其追加到 grid_columns 变量,并像上面一样添加相应的控件。借助 STRETCH,它们的
大小和位置会自动计算以填充单元格,而父对象的 pad_all、pad_row
和 pad_column 可以帮助它们在之间调整间距。请参见下方的 天气预报面板
以获取另一个依赖于 Grid 的示例。
ESPHome 启动画面
Section titled “ESPHome 启动画面”要显示一个带有旋转动画的启动图像,并在几秒后自动消失或触摸
屏幕时消失,你可以使用 top layer。技巧是放置一个全屏的基础 obj 并将其中间的
image 控件作为控件列表的最后一项,这样它们就会绘制在所有其他控件之上。要让它在启动后自动消失,你可以使用 ESPHome 的 on_boot 触发器:
esphome: ... on_boot: - delay: 5s - lvgl.widget.hide: boot_screen
image: - platform: file file: https://media.esphome.io/logo/logo.png id: boot_logo resize: 200x200 type: RGB565 transparency: alpha_channel
lvgl: ... top_layer: widgets: ... # 确保它是此列表中的最后一项: - obj: id: boot_screen x: 0 y: 0 width: 100% height: 100% bg_color: 0xffffff bg_opa: COVER radius: 0 pad_all: 0 border_width: 0 widgets: - image: align: CENTER src: boot_logo y: -50 - spinner: align: CENTER y: 50 height: 50 width: 50 spin_time: 1s arc_length: 60deg arc_width: 8 indicator: arc_color: 0x18bcf2 arc_width: 8 on_press: - lvgl.widget.hide: boot_screen文本中的 MDI 图标
Section titled “文本中的 MDI 图标”ESPHome 的 字体渲染器 允许你为文本使用任何 OpenType/TrueType 字体文件。这非常 灵活,因为你可以准备不同大小、包含不同数量字形(glyphs)的多套字体;这很重要,因为它有助于 节省闪存空间。
一种情况是你希望一些 MDI 图标与文本内联使用(类似于 LVGL 内部字体 和符号共存的方式)。你可以使用任何你选择的字体;从 MDI 中选择想要的符号/图标,并将它们混合在 一个相同大小的集合中。
在下面的示例中,我们使用 RobotoCondensed-Regular 的默认字形集,并从 MDI 附加一些额外 符号。然后通过转义它们的代码点,将它们在文本中内联显示:
font: - file: "fonts/RobotoCondensed-Regular.ttf" id: roboto_icons_42 size: 42 bpp: 4 extras: - file: "fonts/materialdesignicons-webfont.ttf" glyphs: [ "\U000F02D1", # mdi-heart "\U000F05D4", # mdi-airplane-landing ]
lvgl: ... pages: - id: main_page widgets: - label: text: "Just\U000f05d4here. Already\U000F02D1this." align: CENTER text_align: CENTER text_font: roboto_icons_42TIP
按照这些步骤选择你的 MDI 图标:
-
要查找你的图标,请使用 Pictogrammers 站点。点击想要的
-
图标,并注意它的代码点(它是下载选项旁边的十六进制数字)。
-
要获取包含所有图标的 TrueType 字体,请前往
-
并从最近的版本文件夹中,下载
materialdesignicons-webfont.ttf文件,并将其放在你的 ESPHome 配置目录中名为fonts的文件夹下(以匹配上面的示例)。 -
要使用所需图标,请在复制的代码点前面加上
\U000。Unicode 字符转义序列必须 -
以大写的
\U开头,并恰好有 8 个十六进制数字。 -
要将转义序列转换为真正的字形,请确保将字符串用双引号括起来。
修复缺失的复选框和箭头
Section titled “修复缺失的复选框和箭头”如果你将自定义字体配置为 LVGL 使用的 default_font,它很可能不包含
FontAwesome 符号。你可能会观察到一些控件显示不正确;
具体来说 checkbox 在选中时不会显示对勾,而
dropdown 不会显示向下指向以打开列表的箭头。
要解决此问题,只需导入所需尺寸的对勾和箭头符号,并通过 主题与样式定义 将它们应用到配置中所有的复选框和下拉菜单:
font: - file: 'fonts/FontAwesome5-Solid+Brands+Regular.woff' id: fontawesome_checkmark size: 16 bpp: 4 glyphs: [ "\uF00C", # 对勾,用于复选框 "\uF078", # 向下箭头,用于下拉菜单 ]
lvgl: ... theme: checkbox: indicator: checked: text_font: fontawesome_checkmark dropdown: indicator: text_font: fontawesome_checkmark当然你也可以直接应用内置的 montserrat_ 包之一,但这在
二进制大小上不会有益——它会在闪存中无意义地包含整个字形集。
切换状态图标按钮
Section titled “切换状态图标按钮”
使用图标的常见场景是状态显示。例如,一个可切换(复选)按钮会根据
灯光或开关的状态显示不同的图标。要在按钮上放置图标,你可以将一个 label
控件作为 button 的子控件。借助
主题与样式定义 中设置的 checked 状态的不同颜色,
着色已经可以不同。此外,通过使用 text_sensor 从 Home Assistant 导入状态,我们不仅可以追踪 on 状态,
还可以追踪 unavailable 或 unknown 状态,以便为这些情况应用禁用样式。
如果我们以之前的 远程灯光按钮 示例为基础,可以像这样修改:
font: - file: "fonts/materialdesignicons-webfont.ttf" id: mdi_42 size: 42 bpp: 4 glyphs: [ "\U000F0335", # mdi-lightbulb "\U000F0336", # mdi-lightbulb-outline ]
text_sensor: - platform: homeassistant id: ts_remote_light entity_id: light.remote_light on_value: then: - lvgl.widget.update: id: btn_lightbulb state: checked: !lambda return (x == "on"); disabled: !lambda return ((x == "unavailable") or (x == "unknown")); - if: condition: lambda: return (x == "on"); then: - lvgl.label.update: id: lbl_lightbulb text: "\U000F0335" else: - lvgl.label.update: id: lbl_lightbulb text: "\U000F0336"
lvgl: ... pages: - id: room_page widgets: - button: align: CENTER width: 90 height: 50 checkable: true id: btn_lightbulb widgets: - label: id: lbl_lightbulb align: CENTER text_font: mdi_42 text: "\U000F0336" # mdi-lightbulb-outline on_short_click: - homeassistant.action: action: light.toggle data: entity_id: light.remote_light电池状态图标
Section titled “电池状态图标”使用 MDI 图标的另一个示例是以 10 个等级显示电池百分比。我们需要一个包含 对应不同电池百分比等级字形的字体,并且需要一个传感器从 Home Assistant 导入电池状态 为一个数值。我们使用一个 lambda 根据传感器值返回相应 字形的代码点:
font: - file: "fonts/materialdesignicons-webfont.ttf" id: battery_icons_20 size: 20 bpp: 4 glyphs: [ "\U000F007A", # mdi-battery-10 "\U000F007B", # mdi-battery-20 "\U000F007C", # mdi-battery-30 "\U000F007D", # mdi-battery-40 "\U000F007E", # mdi-battery-50 "\U000F007F", # mdi-battery-60 "\U000F0080", # mdi-battery-70 "\U000F0081", # mdi-battery-80 "\U000F0082", # mdi-battery-90 "\U000F0079", # mdi-battery (full) "\U000F008E", # mdi-battery-outline "\U000F0091", # mdi-battery-unknown ]
sensor: - platform: homeassistant id: sns_battery_percentage entity_id: sensor.device_battery on_value: - lvgl.label.update: id: lbl_battery_status text: !lambda |- static char buf[10]; std::string icon; if (x == 100.0) { icon = "\U000F0079"; // mdi-battery (full) } else if (x > 90) { icon = "\U000F0082"; // mdi-battery-90 } else if (x > 80) { icon = "\U000F0081"; // mdi-battery-80 } else if (x > 70) { icon = "\U000F0080"; // mdi-battery-70 } else if (x > 60) { icon = "\U000F007F"; // mdi-battery-60 } else if (x > 50) { icon = "\U000F007E"; // mdi-battery-50 } else if (x > 40) { icon = "\U000F007D"; // mdi-battery-40 } else if (x > 30) { icon = "\U000F007C"; // mdi-battery-30 } else if (x > 20) { icon = "\U000F007B"; // mdi-battery-20 } else if (x > 10) { icon = "\U000F007A"; // mdi-battery-10 } else if (x > 0) { icon = "\U000F008E"; // mdi-battery-outline } else { icon = "\U000F0091"; // mdi-battery-unknown (questionmark) } snprintf(buf, sizeof(buf), "%s", icon.c_str()); return buf;
lvgl: ... pages: - id: battery_page widgets: - label: id: lbl_battery_status align: TOP_RIGHT y: 5 x: -15 text_font: battery_icons_20 text: "\U000F0091" # 从 mdi-battery-unknown 开始电池充电动画
Section titled “电池充电动画”要显示一个表示电池充电的动画,你可以使用 animimg 与一组
由 MDI 渲染的图像 显示电池等级:
image: - platform: file file: mdi:battery-10 type: BINARY id: batt_10 resize: 20x20 - platform: file file: mdi:battery-20 type: BINARY id: batt_20 resize: 20x20 - platform: file file: mdi:battery-30 type: BINARY id: batt_30 resize: 20x20 - platform: file file: mdi:battery-40 type: BINARY id: batt_40 resize: 20x20 - platform: file file: mdi:battery-50 type: BINARY id: batt_50 resize: 20x20 - platform: file file: mdi:battery-60 type: BINARY id: batt_60 resize: 20x20 - platform: file file: mdi:battery-70 type: BINARY id: batt_70 resize: 20x20 - platform: file file: mdi:battery-80 type: BINARY id: batt_80 resize: 20x20 - platform: file file: mdi:battery-90 type: BINARY id: batt_90 resize: 20x20 - platform: file file: mdi:battery type: BINARY id: batt_full resize: 20x20 - platform: file file: mdi:battery-outline type: BINARY id: batt_empty resize: 20x20
lvgl: ... pages: - id: battery_page widgets: - animimg: align: TOP_RIGHT y: 5 x: -15 id: ani_battery_charging src: [ batt_empty, batt_10, batt_20, batt_30, batt_40, batt_50, batt_60, batt_70, batt_80, batt_90, batt_full ] duration: 2200msTIP
你可以将上述两个电池示例叠加放置,并根据充电器是否连接来切换它们的 hidden 标志:
binary_sensor: - platform: ... id: charger_connected on_press: then: - lvgl.widget.show: ani_battery_charging - lvgl.widget.hide: lbl_battery_status on_release: then: - lvgl.widget.show: lbl_battery_status - lvgl.widget.hide: ani_battery_charging使用 x、y、align 控件属性进行精确定位。
一个模拟时钟
Section titled “一个模拟时钟”使用 meter 和 label 控件,我们可以创建一个模拟时钟,它
还会显示日期。
meter 有三个刻度:一个用于分钟刻度和指针,范围在 0 到 60;一个
用于小时刻度和作为主刻度的标签,范围在 1 到 12;还有一个更高精度的小时
刻度,范围在 0 到 720,以便将指针自然地定位在小时之间。第二个刻度
没有指示器,而第三个刻度没有刻度也没有标签。
该脚本在每分钟开始时运行,以更新每个指针的线条位置以及相应的 文本。
lvgl: ... style_definitions: - id: date_style text_font: unscii_8 align: center text_color: 0x000000 bg_opa: 60% radius: 4 pad_all: 2 pages: - id: clock_page widgets: - obj: # 时钟容器 height: SIZE_CONTENT width: 240 align: CENTER pad_all: 0 border_width: 0 bg_color: 0xFFFFFF widgets: - meter: # 表盘 height: 220 width: 220 align: CENTER bg_opa: TRANSP border_width: 0 text_color: 0x000000 scales: - range_from: 0 # 分钟刻度 range_to: 60 angle_range: 360 rotation: 270 ticks: width: 1 count: 61 length: 10 color: 0x000000 indicators: - line: id: minute_hand width: 3 color: 0xa6a6a6 length: 96% value: 0 - range_from: 1 # 小时标签刻度 range_to: 12 angle_range: 330 rotation: 300 ticks: width: 1 count: 12 length: 1 major: stride: 1 width: 4 length: 10 color: 0xC0C0C0 label_gap: 12 - range_from: 0 # 高精度小时刻度用于指针 range_to: 720 angle_range: 360 rotation: 270 indicators: - line: id: hour_hand width: 5 color: 0xa6a6a6 length: 70% value: 0 - label: styles: date_style id: day_label y: -30 - label: id: date_label styles: date_style y: 30
time: - platform: homeassistant id: time_comp on_time_sync: - script.execute: time_update on_time: - minutes: '*' seconds: 0 then: - script.execute: time_update
script: - id: time_update then: - lvgl.indicator.update: id: minute_hand value: !lambda |- return id(time_comp).now().minute; - lvgl.indicator.update: id: hour_hand value: !lambda |- auto now = id(time_comp).now(); return std::fmod(now.hour, 12) * 60 + now.minute; - lvgl.label.update: id: date_label text: !lambda |- static const char * const mon_names[] = {"JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"}; static char date_buf[8]; auto now = id(time_comp).now(); snprintf(date_buf, sizeof(date_buf), "%s %2d", mon_names[now.month-1], now.day_of_month); return date_buf; - lvgl.label.update: id: day_label text: !lambda |- static const char * const day_names[] = {"SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"}; return day_names[id(time_comp).now().day_of_week - 1];一个数字输入键盘
Section titled “一个数字输入键盘”buttonmatrix 控件可以与
Key collector 组件 一起工作,以收集按键按下作为按键序列。它会将按钮的 text
(或在配置时使用的 key_code)发送给按键收集器。
如果你输入正确的序列,led 控件会相应地改变颜色:
lvgl: ... pages: - id: keypad_page widgets: - led: id: lvgl_led x: 30 y: 47 color: 0xFF0000 brightness: 70% - obj: width: 140 height: 25 align_to: id: lvgl_led align: OUT_RIGHT_MID x: 17 border_width: 1 border_color: 0 border_opa: 50% pad_all: 0 bg_opa: 80% bg_color: 0xFFFFFF shadow_color: 0 shadow_opa: 50% shadow_width: 10 shadow_spread: 3 radius: 5 widgets: - label: id: lvgl_label align: CENTER text: "Enter code and \uF00C" text_align: CENTER - buttonmatrix: id: lvgl_keypad x: 20 y: 85 width: 200 height: 190 items: pressed: bg_color: 0xFFFF00 rows: - buttons: - text: 1 control: no_repeat: true - text: 2 control: no_repeat: true - text: 3 control: no_repeat: true - buttons: - text: 4 control: no_repeat: true - text: 5 control: no_repeat: true - text: 6 control: no_repeat: true - buttons: - text: 7 control: no_repeat: true - text: 8 control: no_repeat: true - text: 9 control: no_repeat: true - buttons: - text: "\uF55A" key_code: "*" control: no_repeat: true - text: 0 control: no_repeat: true - text: "\uF00C" key_code: "#" control: no_repeat: true
key_collector: - source_id: lvgl_keypad min_length: 4 max_length: 4 end_keys: "#" end_key_required: true back_keys: "*" allowed_keys: "0123456789*#" timeout: 5s on_progress: - if: condition: lambda: return (x != ""); then: - lvgl.label.update: id: lvgl_label text: !lambda 'return x.c_str();' else: - lvgl.label.update: id: lvgl_label text: "Enter code and \uF00C" on_result: - if: condition: lambda: return (x == "1234"); then: - lvgl.led.update: id: lvgl_led color: 0x00FF00 else: - lvgl.led.update: id: lvgl_led color: 0xFF0000NOTE
- 基础对象
obj用作标签的父级;这允许正确地居中标签,并通过阴影独立于标签尺寸地强调它。 align_to用于在垂直方向上将标签与led对齐。- 在
pressed状态下更改按钮的背景颜色。 - 使用
key_code配置将不同的字符发送给key_collector,而不是显示的符号。
天气预报面板
Section titled “天气预报面板”另一个依赖 Grid 布局的示例可以是显示通过 Home Assistant 的 OpenWeatherMap 集成 天气预报的面板。
这里显示的所有信息都可以通过本地 platform: homeassistant 传感器获取,正如本书中几个
示例所描述的那样;不过,这次我们采用不同的方法。不是由 ESPHome 拉取数据,
而是从 Home Assistant 推送到原生的 Lvgl 组件。
我们使用的气象状况图标来自 MDI。我们只导入 Home Assistant 的 Weather 集成 所支持的气象状况对应的图标。对于其他所有标签,你可以使用任何 字体 的选择。
binary_sensor: - platform: status name: Status sensor
font: - file: "fonts/materialdesignicons-webfont.ttf" id: icons_100 size: 100 bpp: 4 glyphs: [ "\U000F0594", # clear-night "\U000F0590", # cloudy "\U000F0F2F", # exceptional "\U000F0591", # fog "\U000F0592", # hail "\U000F0593", # lightning "\U000F067E", # lightning-rainy "\U000F0595", # partlycloudy "\U000F0596", # pouring "\U000F0597", # rainy "\U000F0598", # snowy "\U000F067F", # snowy-rainy "\U000F0599", # sunny "\U000F059D", # windy "\U000F059E", # windy-variant "\U000F14E4", # sunny-off ]
lvgl: ... pages: - id: weather_forecast widgets: - obj: align: CENTER width: 228 height: 250 pad_all: 10 layout: pad_column: 0 type: GRID grid_rows: [FR(48), FR(13), FR(13), FR(13), FR(13)] grid_columns: [FR(10), FR(40), FR(40), FR(10)] widgets: - label: text: "\U000F14E4" id: lbl_weather_forecast_condition_icon text_font: icons_100 text_align: CENTER grid_cell_row_pos: 0 grid_cell_column_pos: 0 grid_cell_column_span: 2 grid_cell_x_align: CENTER grid_cell_y_align: START
- label: text: "Unknown" id: lbl_weather_forecast_condition_name text_align: CENTER grid_cell_row_pos: 0 grid_cell_column_pos: 2 grid_cell_column_span: 2 grid_cell_x_align: STRETCH grid_cell_y_align: CENTER
- label: text: "Feels like:" grid_cell_row_pos: 1 grid_cell_column_pos: 1
- label: text: "--.- °C" id: lbl_weather_forecast_tempap text_align: RIGHT grid_cell_row_pos: 1 grid_cell_column_pos: 2 grid_cell_x_align: STRETCH
- label: text: "Maximum:" grid_cell_row_pos: 2 grid_cell_column_pos: 1
- label: text: "--.- °C" id: lbl_weather_forecast_temphi text_align: RIGHT grid_cell_row_pos: 2 grid_cell_column_pos: 2 grid_cell_x_align: STRETCH
- label: text: "Minimum:" grid_cell_row_pos: 3 grid_cell_column_pos: 1
- label: text: "--.- °C" id: lbl_weather_forecast_templo text_align: RIGHT grid_cell_row_pos: 3 grid_cell_column_pos: 2 grid_cell_x_align: STRETCH
- label: text: "Now:" grid_cell_row_pos: 4 grid_cell_column_pos: 1
- label: text: "--.- °C" id: lbl_weather_outdnoor_now text_align: RIGHT grid_cell_row_pos: 4 grid_cell_column_pos: 2 grid_cell_x_align: STRETCH
text: - platform: lvgl name: fr_cond_icon widget: lbl_weather_forecast_condition_icon mode: text - platform: lvgl name: fr_cond_name widget: lbl_weather_forecast_condition_name mode: text - platform: lvgl name: fr_tempap widget: lbl_weather_forecast_tempap mode: text - platform: lvgl name: fr_temphi widget: lbl_weather_forecast_temphi mode: text - platform: lvgl name: fr_templo widget: lbl_weather_forecast_templo mode: text - platform: lvgl name: wd_out_now widget: lbl_weather_outdnoor_now mode: text如果你仔细查看 grid_columns 变量,你会注意到左右两侧有两条较窄的列
(FR(10) )。原因是为了给标签与边缘之间留出一些空间。这就是为什么我们在
第一行的控件中使用了 grid_cell_column_span,以占用多列的空间。
这些标签将在 Home Assistant 中显示为
可编辑文本组件,这使得使用
text.set_value 动作更新它们非常容易。为此,我们向 Home Assistant 添加以下
自动化:
- id: weather_cond_forecast alias: 'Weather Forecast Condition' trigger: - platform: state entity_id: sensor.openweathermap_forecast_condition - platform: state entity_id: binary_sensor.your_esphome_node_status_sensor to: 'on' action: - action: text.set_value target: entity_id: - text.your_esphome_node_fr_cond_icon data: value: > {% set d = { "clear-night": "\U000F0594", "cloudy": "\U000F0590", "exceptional": "\U000F0F2F", "fog": "\U000F0591", "hail": "\U000F0592", "lightning": "\U000F0593", "lightning-rainy": "\U000F067E", "partlycloudy": "\U000F0595", "pouring": "\U000F0596", "rainy": "\U000F0597", "snowy": "\U000F0598", "snowy-rainy": "\U000F067F", "sunny": "\U000F0599", "windy": "\U000F059D", "windy-variant": "\U000F059E", "unknown": "\U000F14E4", "unavailable": "\U000F14E4", } %} {{ d.get( states('sensor.openweathermap_forecast_condition') ) }}
- action: text.set_value target: entity_id: - text.your_esphome_node_fr_cond_name data: value: > {% set d = { "clear-night": "Clear Night", "cloudy": "Cloudy", "exceptional": "Exceptional", "fog": "Fog", "hail": "Hail", "lightning": "Lightning", "lightning-rainy": "Lightning rainy", "partlycloudy": "Partly cloudy", "pouring": "Pouring", "rainy": "Rainy", "snowy": "Snowy", "snowy-rainy": "Snowy rainy", "sunny": "Sunny", "windy": "Windy", "windy-variant": "Windy cloudy", "unknown": "Unknown", "unavailable": "Unavailable", } %} {{ d.get( states('sensor.openweathermap_forecast_condition') ) }}
- id: weather_temp_feels_like_forecast alias: 'Weather Temperature Feels Like' trigger: - platform: state entity_id: sensor.openweathermap_feels_like_temperature - platform: state entity_id: binary_sensor.your_esphome_node_status_sensor to: 'on' action: - action: text.set_value target: entity_id: - text.your_esphome_node_fr_tempap data: value: "{{states('sensor.openweathermap_feels_like_temperature') | round(1)}} °C"
- id: weather_temp_forecast_temphi alias: 'Weather Temperature Forecast Hi' trigger: - platform: state entity_id: sensor.openweathermap_forecast_temperature - platform: state entity_id: binary_sensor.your_esphome_node_status_sensor to: 'on' action: - action: text.set_value target: entity_id: - text.your_esphome_node_fr_temphi data: value: "{{states('sensor.openweathermap_forecast_temperature') | round(1)}} °C"
- id: weather_temp_forecast_templo alias: 'Weather Temperature Forecast Lo' trigger: - platform: state entity_id: sensor.openweathermap_forecast_temperature_low - platform: state entity_id: binary_sensor.your_esphome_node_status_sensor to: 'on' action: - action: text.set_value target: entity_id: - text.your_esphome_node_fr_templo data: value: "{{states('sensor.openweathermap_forecast_temperature_low') | round(1)}} °C"
- id: weather_temp_outdoor_now alias: 'Weather Temperature Now' trigger: - platform: state entity_id: sensor.outdoor_temperature - platform: state entity_id: binary_sensor.your_esphome_node_status_sensor to: 'on' action: - action: text.set_value target: entity_id: - text.your_esphome_node_wd_out_now data: value: "{{states('sensor.outdoor_temperature') | round(1)}} °C"每当相应的实体发生变化时,以及 ESPHome 恢复运行时,自动化将被触发以更新标签——这也是你还需要 Status 的原因。注意,你需要根据你 配置其名称使用方式 调整对应的 ESPHome 节点的实体 ID。
空闲时关闭屏幕
Section titled “空闲时关闭屏幕”LVGL 有屏幕非活动的概念——换句话说,它会追踪自上次用户与屏幕交互以来的时间。
这可用于在一段时间没有活动后调暗显示背光或关闭它
(就像屏幕保护程序一样)。每次使用输入设备(触摸屏、旋转编码器)都算作一次活动并重置
非活动计数器。注意使用 on_release 触发器来完成此任务很重要。通过模板数字,
你可以让用户调整超时时间。
lvgl: ... on_idle: timeout: !lambda "return (id(display_timeout).state * 1000);" then: - logger.log: "LVGL is idle" - light.turn_off: display_backlight - lvgl.pause:
touchscreen: - platform: ... on_release: - if: condition: lvgl.is_paused then: - logger.log: "LVGL resuming" - lvgl.resume: - lvgl.widget.redraw: - light.turn_on: display_backlight
light: - platform: ... id: display_backlight
number: - platform: template name: LVGL Screen timeout optimistic: true id: display_timeout unit_of_measurement: "s" initial_value: 45 restore_value: true min_value: 10 max_value: 180 step: 5 mode: box防止 LCD 烧屏
Section titled “防止 LCD 烧屏”你可以利用这一点来保护并延长 LCD 屏幕的使用寿命,从而更加环保并减少 有害废物的产生。
壁挂式 LCD 屏幕的一个常见问题是,它们 99.999% 的时间都显示相同的图像。即使 有人在夜间或黑暗时段关闭了背光,LCD 屏幕仍然显示相同的图像, 只是没人看见。这种情况在使用几年后很可能会发生烧屏。
缓解这一问题的方法之一是定期锻炼像素,通过显示不同的内容。在 LVGL 暂停状态下使用
show_snow 选项就是为此而设计的;它会在整个屏幕上显示随机颜色的像素,
以通过锻炼每个单独的像素来最小化屏幕烧屏。
在下面的示例中,像素训练每晚进行四次,每次半小时;可以通过 触摸屏幕来停止。
time: - platform: ... on_time: - hours: 2,3,4,5 minutes: 5 seconds: 0 then: - switch.turn_on: switch_antiburn - hours: 2,3,4,5 minutes: 35 seconds: 0 then: - switch.turn_off: switch_antiburn
switch: - platform: template name: Antiburn id: switch_antiburn icon: mdi:television-shimmer optimistic: true entity_category: "config" turn_on_action: - logger.log: "Starting Antiburn" - if: condition: lvgl.is_paused then: - lvgl.resume: - lvgl.widget.redraw: - lvgl.pause: show_snow: true turn_off_action: - logger.log: "Stopping Antiburn" - if: condition: lvgl.is_paused then: - lvgl.resume: - lvgl.widget.redraw:
touchscreen: - platform: ... on_release: then: - if: condition: lvgl.is_paused then: - lvgl.resume: - lvgl.widget.redraw:你可以将它与前面的示例结合使用来关闭背光,这样用户就不会实际注意到。