firecrest/firmware/components/display-classic.yaml
pyr0ball a0734891ad fix: display BME280 ambient temperature in Fahrenheit, not Celsius
sensors.yaml formatted the raw BME280 reading directly with no unit
conversion, inconsistent with the dial setpoints and weather widget, which
are already Fahrenheit. Converts at the only point this sensor's value
reaches the UI (shared by every display variant); climate.yaml's internal
Celsius values are untouched since ESPHome's climate component always
operates in Celsius regardless of display unit.
2026-07-12 14:26:27 -07:00

528 lines
20 KiB
YAML

lvgl:
id: lvgl_comp
displays:
- lcd_display
touchscreens:
- touch_gt911
pages:
- id: page_home
widgets:
- obj:
width: 100%
height: 100%
pad_all: 0
border_width: 0
outline_width: 0
scrollable: false
widgets:
- label:
align: top_left
x: 70
y: 20
text: "-- --" # static placeholder clock — live time is a fast-follow, no time: component this pass
text_font: montserrat_16
- obj:
id: mode_toggle
align: top_mid
y: 12
width: 400px
height: 44px
radius: 22
bg_color: 0x333333
border_width: 0
outline_width: 0
pad_all: 4
scrollable: false
layout:
type: flex
flex_flow: row
flex_align_cross: center
flex_align_track: center
flex_align_main: space_evenly
widgets:
- button:
id: toggle_temp_btn
width: 120px
height: 36px
radius: 18
bg_color: 0xFFFFFF
on_click:
- lvgl.widget.show: temp_view
- lvgl.widget.hide: status_view
- lvgl.widget.update:
id: toggle_temp_btn
bg_color: 0xFFFFFF
- lvgl.widget.update:
id: toggle_status_btn
bg_opa: transp
widgets:
- label:
text: "Temp"
text_color: 0x000000
text_font: montserrat_16
- button:
id: toggle_status_btn
width: 120px
height: 36px
radius: 18
bg_opa: transp
on_click:
- lvgl.widget.show: status_view
- lvgl.widget.hide: temp_view
- lvgl.widget.update:
id: toggle_status_btn
bg_color: 0xFFFFFF
- lvgl.widget.update:
id: toggle_temp_btn
bg_opa: transp
widgets:
- label:
text: "Status"
text_font: montserrat_16
- button:
width: 120px
height: 36px
radius: 18
bg_opa: transp
widgets:
- label:
text: "My Home"
text_font: montserrat_16
- label:
align: top_right
x: -16
y: 20
text: "Firecrest"
text_font: montserrat_16
- obj:
id: temp_view
width: 100%
height: 100%
pad_all: 0
border_width: 0
outline_width: 0
scrollable: false
bg_opa: transp
# LVGL's base obj is clickable by default (obj->flags = LV_OBJ_FLAG_CLICKABLE in
# lv_obj_create) and lv_obj_hit_test() requires that flag before checking geometry.
# Being a full-screen, topmost sibling of mode_toggle, this container was silently
# swallowing every tap over the toggle bar (no on_click, so nothing visibly happened)
# before the touch search could reach mode_toggle underneath — same z-order class of
# bug as the bg_opa fix above, but for touch capture instead of rendering.
clickable: false
widgets:
- arc:
id: dial_bg_arc
align: center
y: -20
width: 400px
height: 400px
start_angle: 315
end_angle: 225
min_value: 0
max_value: 1
value: 0
adjustable: false
# arc_color/arc_width are flattened to top-level (MAIN part) rather than nested
# under a "main:" key — an explicit "main:" sub-block silently suppresses the
# implicit top-level MAIN-part properties (align/width/height/x/y), verified via
# isolated test compile. indicator:/knob: below are unaffected since those are
# genuinely separate, non-MAIN parts that require nesting.
arc_color: 0x444444
arc_width: 10
indicator:
arc_opa: transp
knob:
arc_opa: transp
- arc:
id: heat_arc
align: center
y: -20
width: 400px
height: 400px
start_angle: 315
end_angle: 225
min_value: 60
max_value: 90
value: 70
adjustable: true
adv_hittest: true # LVGL's default touch hit-test just uses each widget's full
# bounding square (any topmost widget under the touch point
# wins), which is why the radius difference alone didn't fix
# touch disambiguation. adv_hittest switches to LVGL's precise
# ring-based hit test (see LV_EVENT_HIT_TEST in lv_arc.c),
# which actually respects each arc's own radius.
arc_opa: transp # flattened, see dial_bg_arc comment above
indicator:
arc_color: 0xFF3B30
arc_width: 10
knob:
bg_color: 0xFF3B30
on_value:
then:
- climate.control:
id: main_thermostat
target_temperature_low: !lambda "return (x - 32.0f) * 5.0f / 9.0f;"
- lvgl.label.update:
id: heat_to_value_label
text:
format: "%.0f"
args: ["x"]
- arc:
id: cool_arc
align: center
y: -20
width: 360px # smaller radius than heat_arc (400px) so the two rings are visually
height: 360px # distinct; touch disambiguation itself comes from adv_hittest below,
# not from this size difference alone (a same-radius pair would also
# work correctly with adv_hittest on both, but differing radii keeps
# the two rings visually separable too)
start_angle: 315
end_angle: 225
mode: REVERSE
min_value: 60
max_value: 90
value: 75
adjustable: true
adv_hittest: true # see heat_arc comment above — precise ring-based hit test
arc_opa: transp # flattened, see dial_bg_arc comment above
indicator:
arc_color: 0x0A84FF
arc_width: 10
knob:
bg_color: 0x0A84FF
on_value:
then:
- climate.control:
id: main_thermostat
target_temperature_high: !lambda "return (x - 32.0f) * 5.0f / 9.0f;"
- lvgl.label.update:
id: cool_to_value_label
text:
format: "%.0f"
args: ["x"]
- obj:
align: center
y: -40
width: 200px
height: size_content
border_width: 0
outline_width: 0
scrollable: false
layout:
type: flex
flex_flow: column
flex_align_cross: center
flex_align_track: center
flex_align_main: center
widgets:
- label:
id: home_mode_label
text: "Mode: --"
text_font: montserrat_20
- label:
id: home_temp_label
text: "-- °F"
text_font: montserrat_48
- button:
align: bottom_left
x: 24
y: -24
width: 90px
height: 90px
radius: 45
on_click:
climate.control:
id: main_thermostat
preset: away
widgets:
- label:
align: center
y: -10
text: "Away"
text_font: montserrat_16
- button:
align: bottom_mid
x: -110
y: -24
width: 90px
height: 60px
radius: 30
widgets:
- label:
id: heat_to_value_label
align: center
text: "70"
text_font: montserrat_20
- button:
align: bottom_mid
x: 110
y: -24
width: 90px
height: 60px
radius: 30
widgets:
- label:
id: cool_to_value_label
align: center
text: "75"
text_font: montserrat_20
- label:
id: outside_temp_label
align: bottom_right
x: -24
y: -24
text: "-- °"
text_font: montserrat_20
- obj:
id: status_view
hidden: true
width: 100%
height: 100%
pad_all: 16
border_width: 0
outline_width: 0
scrollable: false
bg_opa: transp
clickable: false # see temp_view comment above — same touch-capture fix
layout:
type: flex
flex_flow: column
flex_align_cross: center
flex_align_track: center
flex_align_main: space_evenly
widgets:
- label:
id: status_condition_label
text: "--"
text_font: montserrat_32
- label:
id: status_temp_label
text: "Outside: -- °F"
text_font: montserrat_24
- label:
id: status_humidity_label
text: "Humidity: -- %"
text_font: montserrat_20
- label:
id: status_high_label
text: "High: -- °F"
text_font: montserrat_20
- label:
id: status_low_label
text: "Low: -- °F"
text_font: montserrat_20
- label:
id: status_precip_label
text: "Precipitation: -- %"
text_font: montserrat_20
- id: page_sensors
widgets:
- obj:
width: 100%
height: 510px
pad_all: 16
border_width: 0
outline_width: 0
scrollable: false
layout:
type: flex
flex_flow: column
flex_align_cross: center
flex_align_track: center
flex_align_main: space_evenly
widgets:
- label:
id: sensors_temp_label
text: "Temperature: -- °F"
text_font: montserrat_28
- label:
id: sensors_humidity_label
text: "Humidity: -- %"
text_font: montserrat_28
- label:
id: sensors_pressure_label
text: "Pressure: -- hPa"
text_font: montserrat_28
- id: page_settings
widgets:
- obj:
width: 100%
height: 510px
pad_all: 16
border_width: 0
outline_width: 0
scrollable: false
layout:
type: flex
flex_flow: column
flex_align_cross: center
flex_align_track: start
flex_align_main: start
widgets:
- label:
id: settings_ip_label
text: "IP: --"
text_font: montserrat_20
- label:
id: settings_uptime_label
text: "Uptime: -- min"
text_font: montserrat_20
- label:
text: "Manual relay override (bypasses thermostat):"
text_font: montserrat_16
- obj:
width: 100%
height: size_content
border_width: 0
outline_width: 0
scrollable: false
layout:
type: flex
flex_flow: row_wrap
flex_align_cross: start
flex_align_track: start
flex_align_main: start
widgets:
- checkbox:
text: "Fan"
on_click:
switch.toggle: relay_fan
- checkbox:
text: "Cool 1"
on_click:
switch.toggle: relay_cool1
- checkbox:
text: "Cool 2"
on_click:
switch.toggle: relay_cool2
- checkbox:
text: "Heat 1"
on_click:
switch.toggle: relay_heat1
- checkbox:
text: "Heat 2"
on_click:
switch.toggle: relay_heat2
- id: page_schedule
widgets:
- obj:
width: 100%
height: 510px
pad_all: 16
border_width: 0
outline_width: 0
scrollable: false
layout:
type: flex
flex_flow: column
flex_align_cross: center
flex_align_track: center
flex_align_main: space_evenly
widgets:
- label:
text: "Preset"
text_font: montserrat_28
- obj:
width: 100%
height: size_content
border_width: 0
outline_width: 0
scrollable: false
layout:
type: flex
flex_flow: row
flex_align_cross: center
flex_align_track: center
flex_align_main: space_evenly
widgets:
- button:
width: 300px
height: 100px
on_click:
climate.control:
id: main_thermostat
preset: home
widgets:
- label:
text: "Home"
text_font: montserrat_28
- button:
width: 300px
height: 100px
on_click:
climate.control:
id: main_thermostat
preset: away
widgets:
- label:
text: "Away"
text_font: montserrat_28
- label:
text: "Time-based scheduling runs in Home Assistant, not on this device."
text_font: montserrat_16
top_layer:
widgets:
- button:
id: hamburger_btn
align: top_left
x: 16
y: 16
width: 60px
height: 60px
radius: 8
on_click:
lvgl.widget.show: nav_menu_overlay
widgets:
- label:
text: "="
text_font: montserrat_28
- obj:
id: nav_menu_overlay
hidden: true
align: top_left
x: 16
y: 90px
width: 280px
height: size_content
pad_all: 8
radius: 8
layout:
type: flex
flex_flow: column
flex_align_cross: stretch
flex_align_track: start
flex_align_main: start
widgets:
- button:
on_click:
- lvgl.page.show: page_home
- lvgl.widget.hide: nav_menu_overlay
widgets:
- label:
text: "Home"
text_font: montserrat_20
- button:
on_click:
- lvgl.page.show: page_sensors
- lvgl.widget.hide: nav_menu_overlay
widgets:
- label:
text: "Sensors"
text_font: montserrat_20
- button:
on_click:
- lvgl.page.show: page_settings
- lvgl.widget.hide: nav_menu_overlay
widgets:
- label:
text: "Settings"
text_font: montserrat_20
- button:
on_click:
- lvgl.page.show: page_schedule
- lvgl.widget.hide: nav_menu_overlay
widgets:
- label:
text: "Schedule"
text_font: montserrat_20