LVGL:技巧与窍门
这里有一些在 ESPHome 中使用 LVGL 可以实现的各种有趣功能的配方。
NOTE
以下许多示例调用 Home Assistant 中的服务动作;然而,Home Assistant 默认不允许此类动作调用。对于每个将调用动作的 ESPHome 设备,您必须在 Home Assistant 中显式启用此设置。这可以在设备最初被采纳时完成,或通过使用 ESPHome 集成”设备”列表中的”配置”选项完成。
NOTE
以下示例假设您已正确设置 LVGL 及其显示屏和输入设备,并且您具备在 ESPHome 中设置各种组件的知识。某些示例使用绝对定位,适用于尺寸为 240x320px 的屏幕;如果您的显示屏尺寸不同,您需要调整它们以获得预期结果。
集成 LVGL switch 小部件与开关或灯的最简单方法是使用自动化:
light: - platform: ... id: local_light name: '本地灯' 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
如果您想从可选中(切换)的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: '远程灯' on_click: - homeassistant.action: action: light.toggle data: entity_id: light.remote_light
您可以使用 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: 30 height: 220 pad_all: 8 min_value: 0 max_value: 255 on_release: - homeassistant.action: action: light.turn_on data: entity_id: light.your_dimmer brightness: !lambda return int(x);请注意,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: mediaplayer_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 align: CENTER scales: - range_from: -10 range_to: 10 angle_range: 180 # 将总角度设置为 180 = 从左中开始到右中结束 ticks: count: 0 indicators: - line: id: val_needle width: 8 r_mod: 12 # 通过此值设置线长度与刻度默认半径的差异 value: -2 - arc: # 刻度背景的前半部分 color: 0xFF3000 r_mod: 10 # 与刻度默认半径的半径差异 width: 31 start_value: -10 end_value: 0 - arc: # 刻度背景的后半部分 color: 0x00FF00 r_mod: 10 width: 31 start_value: 0 end_value: 10 - obj: # 用于覆盖仪表指示线中间部分 height: 146 width: 146 radius: 73 align: CENTER border_width: 0 bg_color: 0xFFFFFF pad_all: 0 - label: # 仪表数字指示器 id: val_text text_font: montserrat_48 align: CENTER y: -5 text: "0" - 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"TIP
用于隐藏仪表指示线中间部分的 obj 的 radius 等于 width 和 height 的一半。这会产生一个圆形——实际上是一个带有超大圆角的正方形。
带有精确仪表的温度计也由 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: -100 # 指针值的刻度 range_to: 400 angle_range: 240 rotation: 150 indicators: - line: id: temperature_needle width: 2 color: 0xFF0000 r_mod: -4 - tick_style: start_value: -10 end_value: 40 color_start: 0x0000bd color_end: 0xbd0000 width: 1 - 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 widgets: - label: id: temperature_text text: "-.-°C" align: CENTER y: 45 - label: text: "室外" 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 scales: - range_from: -15 range_to: 35 angle_range: 180 ticks: count: 70 width: 1 length: 31 indicators: - tick_style: start_value: -15 end_value: 35 color_start: 0x3399ff color_end: 0xffcc66 - range_from: -150 range_to: 350 angle_range: 180 ticks: count: 0 indicators: - line: id: temperature_needle width: 8 r_mod: 2 value: -150 - obj: # 用于覆盖仪表指示线中间部分 height: 123 width: 123 radius: 73 align: CENTER border_width: 0 pad_all: 0 bg_color: 0xFFFFFF - label: id: temperature_text text: "--.-°C" align: CENTER y: -26 - label: text: "室外" align: CENTER y: -6TIP
您可以通过使用位图 image 指示器作为指针来省略用于隐藏仪表指示线中间部分的 obj,其中只有悬挂在刻度线上方的部分可见,其余部分是透明的。
spinbox 是控制恒温器的理想小部件:
首先我们从 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 ((0 == x.compare(std::string{"opening"})) or (0 == x.compare(std::string{"closing"}))); then: - lvgl.label.update: id: cov_stop_myroom text: "停止" 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: "我的房间" 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: 停止 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 使用继承来在各个小部件之间应用样式,可以在顶层应用它们,仅在必要时进行修改。
在这个示例中,我们在主题中准备一组渐变样式,并在样式定义中进行一些修改,可以批量应用到所需的小部件。主题会自动应用,可以手动用样式定义覆盖(继续阅读以了解如何操作)。
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: # 将所有按钮矩阵按钮设置为使用您自定义定义的样式和字体 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,即所有页面之上的始终置顶透明页面。您放在此页面上的所有内容都将位于所有其他页面之上。
对于导航栏,我们可以使用 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: - if: condition: lambda: 'return (0 == client_info.find("Home Assistant "));' then: - 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: 0xFFFFFF注意:
- 小部件在启动时以隐藏状态开始,仅在 API 连接触发时显示。
每个页面可以有自己的标题栏:
要在状态图标后面放置标题栏,我们需要将其添加到每个页面,同时包含具有唯一标题的标签:
lvgl: ... pages: - id: main_page widgets: - obj: align: TOP_MID styles: header_footer widgets: - label: text: "ESPHome LVGL 显示屏" align: CENTER text_align: CENTER text_color: 0xFFFFFF ... - id: second_page widgets: - obj: align: TOP_MID styles: header_footer widgets: - label: text: "第二页" 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: "东" - button: id: but_cov_up_east width: 70 # 选择按钮尺寸,以便 height: 68 # 它们在流动时很好地填充列 widgets: - label: id: cov_up_east align: CENTER text: "\U000F005D" # mdi:arrow-up - button: id: but_cov_stop_east width: 70 height: 68 widgets: - label: id: cov_stop_east align: CENTER text: "\U000F04DB" # mdi:stop - button: id: but_cov_down_east width: 70 height: 68 widgets: - label: id: cov_down_east align: CENTER text: "\U000F0045" # mdi:arrow-down
- label: text: "南" - button: id: but_cov_up_south width: 70 height: 68 widgets: - label: id: cov_up_south align: CENTER text: "\U000F005D" - button: id: but_cov_stop_south width: 70 height: 68 widgets: - label: id: cov_stop_south align: CENTER text: "\U000F04DB" - button: id: but_cov_down_south width: 70 height: 68 widgets: - label: id: cov_down_south align: CENTER text: "\U000F0045"
- label: text: "西" - button: id: but_cov_up_west width: 70 height: 68 widgets: - label: id: cov_up_west align: CENTER text: "\U000F005D" - button: id: but_cov_stop_west width: 70 height: 68 widgets: - label: id: cov_stop_west align: CENTER text: "\U000F04DB" - button: id: but_cov_down_west width: 70 height: 68 widgets: - label: id: cov_down_west align: CENTER text: "\U000F0045"这节省了大量的手动计算小部件位置的工作,否则需要使用 x 和 y 手动放置它们!您只需要确定小部件的通用宽度和高度,以便按您喜欢的方式在页面上分布。(下面的 MDI 图标文本 展示了如何使用自定义图标。)
Grid 布局定位
Section titled “Grid 布局定位”还有更多!使用 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: "东" grid_cell_column_pos: 0 # 将小部件放置在 grid_cell_row_pos: 0 # 相应的单元格中 grid_cell_x_align: STRETCH 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: "\U000F005D" - 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: "\U000F04DB" - 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: "\U000F0045"
- label: text: "南" grid_cell_column_pos: 1 grid_cell_row_pos: 0 grid_cell_x_align: STRETCH 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: "\U000F005D" - 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: "\U000F04DB" - 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: "\U000F0045"
- label: text: "西" grid_cell_column_pos: 2 grid_cell_row_pos: 0 grid_cell_x_align: STRETCH 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: "\U000F005D" - 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: "\U000F04DB" - 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: "\U000F0045"这里的巨大优势是,每当您需要添加例如新窗帘的额外按钮列时,您只需将其追加到 grid_columns 变量,并像上面一样添加相应的小部件。使用 STRETCH,它们的尺寸和位置将自动计算以填充单元格,而父级的 pad_all、pad_row 和 pad_column 可以帮助它们之间的间距。请参阅本页面下方的 天气预报面板 以获取另一个依赖 Grid 的示例。
ESPHome 启动画面
Section titled “ESPHome 启动画面”要显示一个带有旋转动画的启动图像,它会在几秒钟后或触摸屏幕后自动消失,您可以使用 顶层。技巧是将一个基础 obj 放置为全屏,子 image 小部件在中间,作为小部件列表的最后一项,这样它们绘制在所有其他小部件之上。要使其在启动后自动消失,您使用 ESPHome 的 on_boot 触发器:
esphome: ... on_boot: - delay: 5s - lvgl.widget.hide: boot_screen
image: - 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: -40 - spinner: align: CENTER y: 95 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_screenMDI 图标文本
Section titled “MDI 图标文本”ESPHome 的 字体渲染器 允许您为文本使用任何 OpenType/TrueType 字体文件。这非常灵活,因为您可以准备不同大小的各种字体集,每个都有不同数量的字形;这很重要,因为它可能有助于节省闪存空间。
一个例子是当您想在文本中内联使用一些 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 字体,请前往
- Pictogrammers GitHub 仓库
-从最近的版本文件夹中,下载 materialdesignicons-webfont.ttf 文件并将其放在您的 ESPHome 配置目录下名为 fonts 的文件夹中(以匹配上面的示例)。
- 要使用所需的图标,在复制的码点前加上
\U000。Unicode 字符转义序列必须以大写\U开头,并恰好有 8 个十六进制数字。 - 要将转义序列转换为真实的字形,请确保将字符串用双引号括起来。
恢复复选框标记
Section titled “恢复复选框标记”如果您将自定义字体配置为 LVGL 使用的 default_font,并且此字体不包含 FontAwesome 符号,您可能会观察到某些小部件无法正确显示;具体来说,checkbox 在选中时不会显示勾选标记。
要解决此问题,只需以所需大小导入仅勾选标记符号,并通过主题和样式定义将其应用到配置中的所有复选框:
font: - file: 'fonts/FontAwesome5-Solid+Brands+Regular.woff' id: fontawesome_checkmark size: 18 bpp: 4 glyphs: [ "\uF00C", # 勾选标记,用于复选框 ]
lvgl: ... theme: checkbox: indicator: checked: text_font: fontawesome_checkmark您当然可以简单地应用内置的 montserrat_ 包之一,但这对二进制大小没有好处——它会无用地包含整个字形集在闪存中。
切换状态图标按钮
Section titled “切换状态图标按钮”
图标的常见用例是状态显示。例如,可选中(切换)按钮将根据灯或开关的状态显示不同的图标。要在按钮上放置图标,您使用 label 小部件作为 button 的子级。由于主题和样式定义,着色已经可以不同,您可以为 checked 状态设置不同的颜色。此外,通过使用 text_sensor 从 Home Assistant 导入状态,我们不仅可以跟踪 on 状态,还可以跟踪 unavailable 或 unknown 状态来为这些情况应用禁用样式。
如果我们采用之前的远程灯按钮示例,我们可以像这样修改它:
font: - file: "custom/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 (0 == x.compare(std::string{"on"})); disabled: !lambda return ((0 == x.compare(std::string{"unavailable"})) or (0 == x.compare(std::string{"unknown"}))); - lvgl.label.update: id: lbl_lightbulb text: !lambda |- static char buf[10]; std::string icon; if (0 == x.compare(std::string{"on"})) { icon = "\U000F0335"; } else { icon = "\U000F0336"; } snprintf(buf, sizeof(buf), "%s", icon.c_str()); return buf;
lvgl: ... pages: - id: room_page widgets: - button: x: 110 y: 40 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 (满电) "\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 (满电) } 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 } 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: 40 x: -10 text_font: battery_icons_20 text: "\U000F0091" # 以 mdi-battery-unknown 开始电池充电动画
Section titled “电池充电动画”要制作一个说明电池充电的动画,您可以使用 animimg 和一组从 MDI 渲染的图像,显示电池级别:
image: - file: mdi:battery-10 id: batt_10 resize: 20x20 - file: mdi:battery-20 id: batt_20 resize: 20x20 - file: mdi:battery-30 id: batt_30 resize: 20x20 - file: mdi:battery-40 id: batt_40 resize: 20x20 - file: mdi:battery-50 id: batt_50 resize: 20x20 - file: mdi:battery-60 id: batt_60 resize: 20x20 - file: mdi:battery-70 id: batt_70 resize: 20x20 - file: mdi:battery-80 id: batt_80 resize: 20x20 - file: mdi:battery-90 id: batt_90 resize: 20x20 - file: mdi:battery id: batt_full resize: 20x20 - file: mdi:battery-outline id: batt_empty resize: 20x20
lvgl: ... pages: - id: battery_page widgets: - animimg: align: TOP_RIGHT y: 41 x: -10 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 小部件属性进行精确定位。
使用 meter 和 label 小部件,我们可以创建一个也显示日期的模拟时钟。
meter 有三个刻度:一个用于分钟刻度线和指针,范围在 0 和 60 之间;一个用于小时刻度线和标签作为主要刻度,范围在 1 和 12 之间;一个更高分辨率的刻度用于时针,范围在 0 和 720 之间,以便能够自然地将指针定位在小时之间。第二个刻度没有指示器,而第三个刻度没有刻度线或标签。
脚本在每分钟开始时运行,更新每个指针的线位置以及各自的文本。
lvgl: ... 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 r_mod: -4 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 ticks: count: 0 indicators: - line: id: hour_hand width: 5 color: 0xa6a6a6 r_mod: -30 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[] = {"一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"}; 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[] = {"周日", "周一", "周二", "周三", "周四", "周五", "周六"}; 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: "输入代码并 \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 (0 != x.compare(std::string{""})); then: - lvgl.label.update: id: lvgl_label text: !lambda 'return x.c_str();' else: - lvgl.label.update: id: lvgl_label text: "输入代码并 \uF00C" on_result: - if: condition: lambda: return (0 == x.compare(std::string{"1234"})); then: - lvgl.led.update: id: lvgl_led color: 0x00FF00 else: - lvgl.led.update: id: lvgl_led color: 0xFF0000注意:
- 一个基础对象
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: 状态传感器
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: "未知" 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: "体感温度:" 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: "最高:" 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: "最低:" 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: "当前:" 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: '天气预报状况' 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": "晴朗夜晚", "cloudy": "多云", "exceptional": "异常", "fog": "雾", "hail": "冰雹", "lightning": "闪电", "lightning-rainy": "雷雨", "partlycloudy": "局部多云", "pouring": "大雨", "rainy": "下雨", "snowy": "下雪", "snowy-rainy": "雨夹雪", "sunny": "晴天", "windy": "有风", "windy-variant": "多云有风", "unknown": "未知", "unavailable": "不可用", } %} {{ d.get( states('sensor.openweathermap_forecast_condition') ) }}
- id: weather_temp_feels_like_forecast alias: '天气体感温度' 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: '天气预报最高温度' 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: '天气预报最低温度' 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: '当前室外温度' 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 启动时,自动化将被触发以更新标签——这也是您需要 状态传感器 的原因。请注意,您需要根据您如何配置其名称来调整对应于您的 ESPHome 节点的实体 ID。
空闲时关闭屏幕
Section titled “空闲时关闭屏幕”LVGL 有屏幕不活动的概念——换句话说,自上次用户与屏幕交互以来的时间被跟踪。这可用于在不活动一段时间后调暗显示屏背光或将其关闭(如屏幕保护程序)。每次使用输入设备(触摸屏、旋转编码器)都算作活动并重置不活动计数器。请注意,使用 on_release 触发器来完成此任务很重要。使用模板数字,您可以让用户调整超时时间。
lvgl: ... on_idle: timeout: !lambda "return (id(display_timeout).state * 1000);" then: - logger.log: "LVGL 空闲" - light.turn_off: display_backlight - lvgl.pause:
touchscreen: - platform: ... on_release: - if: condition: lvgl.is_paused then: - logger.log: "LVGL 恢复" - lvgl.resume: - lvgl.widget.redraw: - light.turn_on: display_backlight
light: - platform: ... id: display_backlight
number: - platform: template name: LVGL 屏幕超时 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: 防烧屏 id: switch_antiburn icon: mdi:television-shimmer optimistic: true entity_category: "config" turn_on_action: - logger.log: "开始防烧屏" - if: condition: lvgl.is_paused then: - lvgl.resume: - lvgl.widget.redraw: - lvgl.pause: show_snow: true turn_off_action: - logger.log: "停止防烧屏" - 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:您可以将其与前面的示例结合使用以关闭背光,这样用户实际上不会注意到这一点。