# ============================================================================ # DEMO / EXAMPLE SCREEN (base id 6, carousel name "demo") # ---------------------------------------------------------------------------- # A minimal, heavily-commented screen that shows how a screen package is built: # * it registers itself in the carousel (g_present), # * it shows its own LVGL page, # * it reacts to a tap (toggles the background black <-> white, text inverts), # * it declares its OWN settings toggles (Settings -> Widgets -> Demo): # g_demo_border -> draw a border # g_demo_led -> drive the LED ring (green on white bg, red on black bg) # The globals live in THIS file and are registered into the core's generic settings # registry (g_wgrp_* / g_wopt_*) in on_boot - see how below as the template. # * it drives the LED ring directly (a simple demo - it bypasses the ring # reaction-priority system in the core; that is fine for an example). # # Everything a screen needs lives in THIS file (globals/scripts/interval/lvgl page + # its settings registration). Remove the screen by dropping this file from the package # "files:" list - nothing else to edit, anywhere. # ============================================================================ esphome: on_boot: # Register this screen so the carousel knows it exists. The core builds the # carousel order from `screen_order`, including only screens that set g_present. - priority: 655 then: - lambda: 'id(g_present)[6] = true;' # Register this screen's settings (Settings -> Widgets -> Demo) in the generic registry. - priority: 650 then: - lambda: |- int g = id(g_wgrp_n)++; id(g_wgrp_labels) += (g ? "\n" : ""); id(g_wgrp_labels) += "Demo"; int o; o = id(g_wopt_n)++; id(g_wopt_group)[o] = g; id(g_wopt_label)[o] = "Border"; id(g_wopt_kind)[o] = 0; id(g_wopt_ptr)[o] = &id(g_demo_border); o = id(g_wopt_n)++; id(g_wopt_group)[o] = g; id(g_wopt_label)[o] = "Demo LED"; id(g_wopt_kind)[o] = 0; id(g_wopt_ptr)[o] = &id(g_demo_led); globals: - id: g_demo_bg # background: false = black, true = white (runtime state) type: bool restore_value: no initial_value: 'false' # Demo settings (declared here, registered above; the core menu reaches them by pointer). - id: g_demo_border # draw a border (Settings -> Widgets -> Demo) type: bool restore_value: yes initial_value: 'false' - id: g_demo_led # drive the LED ring (Settings -> Widgets -> Demo) type: bool restore_value: yes initial_value: 'false' # Tap de-duplication: the core bumps g_tap_seq on every normal-mode tap; we # remember the last value we handled so one tap = one toggle. - id: g_demo_lasttap type: int restore_value: no initial_value: '0' # Tracks whether WE currently own the LED ring, so we can release it on leave. # Persistent so that after a reboot (the ring component restores its last colour) the # 80ms loop below sees "we owned it", and since we boot onto the home screen it turns # the ring back off instead of leaving the demo colour stuck on. - id: g_demo_ring_on type: bool restore_value: yes initial_value: 'false' script: # Redraws the screen from the three state flags (g_demo_bg / border / led). - id: demo_render then: # background + text colour (text is the inverse of the background) - if: condition: { lambda: 'return id(g_demo_bg);' } # white background then: - lvgl.widget.update: { id: demo_bg, bg_color: 0xFFFFFF, border_color: 0x000000 } - lvgl.label.update: { id: demo_lbl, text_color: 0x000000 } else: # black background - lvgl.widget.update: { id: demo_bg, bg_color: 0x000000, border_color: 0xFFFFFF } - lvgl.label.update: { id: demo_lbl, text_color: 0xFFFFFF } # border on/off (width is an int, so a lambda is fine here) - lvgl.widget.update: { id: demo_bg, border_width: !lambda 'return id(g_demo_border) ? 6 : 0;' } # LED ring: green when the background is white, red when it is black - if: condition: { lambda: 'return id(g_demo_led);' } then: - if: condition: { lambda: 'return id(g_demo_bg);' } then: [ light.turn_on: { id: ring_light, red: 0%, green: 100%, blue: 0%, effect: "None" } ] else: [ light.turn_on: { id: ring_light, red: 100%, green: 0%, blue: 0%, effect: "None" } ] - lambda: 'id(g_demo_ring_on) = true;' else: # LED toggle is off: release the ring if we were driving it - if: condition: { lambda: 'return id(g_demo_ring_on);' } then: - light.turn_off: ring_light - lambda: 'id(g_demo_ring_on) = false;' interval: # Carousel navigation: when this screen becomes selected, show its page. - interval: 50ms then: - if: condition: { lambda: 'return id(g_nav_req) && id(g_base)==6;' } then: - lambda: 'id(g_nav_req) = false; id(g_demo_lasttap) = id(g_tap_seq);' # sync tap counter so arriving never counts as a tap - if: { condition: { lambda: 'return id(g_nav_anim)==0;' }, then: [lvgl.page.show: { id: page_demo, animation: move_left, time: 250ms }] } - if: { condition: { lambda: 'return id(g_nav_anim)==1;' }, then: [lvgl.page.show: { id: page_demo, animation: move_right, time: 250ms }] } - if: { condition: { lambda: 'return id(g_nav_anim)==2;' }, then: [lvgl.page.show: { id: page_demo, animation: move_top, time: 250ms }] } - if: { condition: { lambda: 'return id(g_nav_anim)==3;' }, then: [lvgl.page.show: { id: page_demo, animation: move_bottom, time: 250ms }] } - script.execute: demo_render # React to a tap while this screen is on top; release the LED when we leave. - interval: 80ms then: - if: condition: { lambda: 'return id(g_base)==6 && id(g_tap_seq) != id(g_demo_lasttap);' } then: - lambda: 'id(g_demo_lasttap) = id(g_tap_seq); id(g_demo_bg) = !id(g_demo_bg);' # one tap = flip background - script.execute: demo_render - if: condition: { lambda: 'return id(g_base) != 6 && id(g_demo_ring_on);' } then: - lambda: 'id(g_demo_ring_on) = false;' lvgl: pages: - id: page_demo bg_color: 0x000000 widgets: # A full circle that fills the round screen - this is the background we recolour. # radius 180 on a 360x360 box makes it a circle, so the optional border is a clean ring. - obj: id: demo_bg align: CENTER width: 360 height: 360 radius: 180 bg_color: 0x000000 bg_opa: COVER border_width: 0 border_color: 0xFFFFFF - label: id: demo_lbl align: CENTER text: "TAP ME!" text_font: montserrat_34 text_color: 0xFFFFFF