Files
Home-Assistant/esphome/chore-tracker-esphome.yaml
T

1449 lines
53 KiB
YAML

# ################################################################################
# # chore-tracker-esphome.yaml (AUTO-GENERATED — edit chores_config.yaml)
# # Kids: Jordyn, Declan, Chloe
# #
# # HOME SCREEN BEHAVIOUR:
# # Red outline = chores incomplete
# # Solid green = all chores done
# # Resets to red automatically at midnight
# #
# # Hardware: Waveshare ESP32-S3-Touch-LCD-7 (800x480)
# ################################################################################
# substitutions:
# device_name: chore-tracker
# friendly_name: "Chore Tracker"
# esphome:
# name: ${device_name}
# friendly_name: ${friendly_name}
# on_boot:
# priority: -10
# then:
# - light.turn_on: backlight
# - lvgl.page.show: page_home
# esp32:
# board: esp32-s3-devkitc-1
# framework:
# type: esp-idf
# psram:
# mode: octal
# speed: 80MHz
# wifi:
# ssid: !secret wifi_iot_ssid
# password: !secret wifi_password
# manual_ip:
# static_ip: 192.168.69.115
# gateway: 192.168.69.1
# subnet: 255.255.255.0
# ap:
# ssid: "Chore Tracker Hotspot"
# password: "choretracker"
# # Prevents aggressive reconnection attempts
# power_save_mode: none # Use 'light' for battery devices
# # Slower but more reliable connection
# fast_connect: true
# # Handle connection failures gracefully
# on_connect:
# - logger.log: "Wi-Fi connected!"
# on_disconnect:
# - logger.log: "Wi-Fi disconnected!"
# mdns:
# disabled: true
# safe_mode:
# disabled: true
# #captive_portal:
# logger:
# level: DEBUG
# api:
# encryption:
# key: !secret api_encryption_key
# reboot_timeout: 0s
# ota:
# - platform: esphome
# password: !secret ota_password
# web_server:
# port: 8080
# # ── I2C ───────────────────────────────────────────────────────────────────────
# i2c:
# sda: 8
# scl: 9
# # ── CH422G IO Expander (controls LCD reset, touch reset, backlight) ───────────
# ch422g:
# - id: ch422g_hub
# # ── Display ───────────────────────────────────────────────────────────────────
# display:
# - platform: mipi_rgb
# model: ESP32-S3-TOUCH-LCD-7-800X480
# id: main_display
# update_interval: never
# auto_clear_enabled: false
# reset_pin:
# ch422g: ch422g_hub
# number: 3
# mode:
# output: true
# # ── Touchscreen ───────────────────────────────────────────────────────────────
# touchscreen:
# platform: gt911
# id: touch
# update_interval: 120ms
# reset_pin:
# ch422g: ch422g_hub
# number: 1
# mode:
# output: true
# # ── Backlight ─────────────────────────────────────────────────────────────────
# output:
# - platform: template
# id: lcd_backlight_out
# type: binary
# write_action:
# - if:
# condition:
# lambda: return state;
# then:
# - switch.turn_on: lcd_backlight_raw
# else:
# - switch.turn_off: lcd_backlight_raw
# light:
# - platform: binary
# name: "Display Backlight"
# output: lcd_backlight_out
# id: backlight
# # ── Midnight reset ────────────────────────────────────────────────────────────
# time:
# - platform: homeassistant
# id: ha_time
# on_time:
# - hours: 0
# minutes: 0
# seconds: 0
# then:
# - lambda: |-
# id(reset_jordyn_chores).execute();
# id(reset_declan_chores).execute();
# id(reset_chloe_chores).execute();
# - lvgl.page.show: page_home
# # ── Fonts (place files in /config/esphome/fonts/) ────────────────────────────
# font:
# - file: "fonts/Nunito-Black.ttf"
# id: font_title
# size: 40
# - file: "fonts/Nunito-ExtraBold.ttf"
# id: font_name
# size: 30
# - file: "fonts/Nunito-Bold.ttf"
# id: font_med
# size: 22
# - file: "fonts/Nunito-SemiBold.ttf"
# id: font_small
# size: 17
# - file: "fonts/Nunito-SemiBold.ttf"
# id: font_tiny
# size: 13
# # MDI icon font — used for chore icons and kid avatars
# - file: "fonts/materialdesignicons-webfont.ttf"
# id: font_mdi_large
# size: 48
# bpp: 4
# glyphs: 󰄛󰆫󰇷󰊩󰋣󰐂󰦩󱑷󱚣󱜜
# - file: "fonts/materialdesignicons-webfont.ttf"
# id: font_mdi_small
# size: 32
# bpp: 4
# glyphs: 󰄛󰆫󰇷󰊩󰋣󰐂󰦩󱑷󱚣󱜜
# # ── Switches — backlight raw + one per chore per kid ─────────────────────────
# switch:
# - platform: gpio
# id: lcd_backlight_raw
# name: "LCD Backlight Raw"
# restore_mode: ALWAYS_ON
# pin:
# ch422g: ch422g_hub
# number: 2
# mode:
# output: true
# # ── Jordyn's chores ──────────────────────────
# - platform: template
# name: "Jordyn - Put on underwear"
# id: jordyn_put_on_underwear
# icon: mdi:checkbox-marked-circle
# optimistic: true
# restore_mode: RESTORE_DEFAULT_OFF
# on_turn_on:
# then:
# - script.execute: update_jordyn_ui
# - homeassistant.service:
# service: switch.turn_on
# data:
# entity_id: switch.chore_tracker_jordyn_put_on_underwear
# on_turn_off:
# then:
# - script.execute: update_jordyn_ui
# - homeassistant.service:
# service: switch.turn_off
# data:
# entity_id: switch.chore_tracker_jordyn_put_on_underwear
# - platform: template
# name: "Jordyn - Brush Teeth"
# id: jordyn_brush_teeth
# icon: mdi:checkbox-marked-circle
# optimistic: true
# restore_mode: RESTORE_DEFAULT_OFF
# on_turn_on:
# then:
# - script.execute: update_jordyn_ui
# - homeassistant.service:
# service: switch.turn_on
# data:
# entity_id: switch.chore_tracker_jordyn_brush_teeth
# on_turn_off:
# then:
# - script.execute: update_jordyn_ui
# - homeassistant.service:
# service: switch.turn_off
# data:
# entity_id: switch.chore_tracker_jordyn_brush_teeth
# - platform: template
# name: "Jordyn - Fill water bowls"
# id: jordyn_fill_water_bowls
# icon: mdi:checkbox-marked-circle
# optimistic: true
# restore_mode: RESTORE_DEFAULT_OFF
# on_turn_on:
# then:
# - script.execute: update_jordyn_ui
# - homeassistant.service:
# service: switch.turn_on
# data:
# entity_id: switch.chore_tracker_jordyn_fill_water_bowls
# on_turn_off:
# then:
# - script.execute: update_jordyn_ui
# - homeassistant.service:
# service: switch.turn_off
# data:
# entity_id: switch.chore_tracker_jordyn_fill_water_bowls
# - platform: template
# name: "Jordyn - Restock Cat Food Cans"
# id: jordyn_restock_cat_food_cans
# icon: mdi:checkbox-marked-circle
# optimistic: true
# restore_mode: RESTORE_DEFAULT_OFF
# on_turn_on:
# then:
# - script.execute: update_jordyn_ui
# - homeassistant.service:
# service: switch.turn_on
# data:
# entity_id: switch.chore_tracker_jordyn_restock_cat_food_cans
# on_turn_off:
# then:
# - script.execute: update_jordyn_ui
# - homeassistant.service:
# service: switch.turn_off
# data:
# entity_id: switch.chore_tracker_jordyn_restock_cat_food_cans
# # ── Declan's chores ──────────────────────────
# - platform: template
# name: "Declan - Take morning pill"
# id: declan_take_morning_pill
# icon: mdi:checkbox-marked-circle
# optimistic: true
# restore_mode: RESTORE_DEFAULT_OFF
# on_turn_on:
# then:
# - script.execute: update_declan_ui
# - homeassistant.service:
# service: switch.turn_on
# data:
# entity_id: switch.chore_tracker_declan_take_morning_pill
# on_turn_off:
# then:
# - script.execute: update_declan_ui
# - homeassistant.service:
# service: switch.turn_off
# data:
# entity_id: switch.chore_tracker_declan_take_morning_pill
# - platform: template
# name: "Declan - Scoop Dog Poop"
# id: declan_scoop_dog_poop
# icon: mdi:checkbox-marked-circle
# optimistic: true
# restore_mode: RESTORE_DEFAULT_OFF
# on_turn_on:
# then:
# - script.execute: update_declan_ui
# - homeassistant.service:
# service: switch.turn_on
# data:
# entity_id: switch.chore_tracker_declan_scoop_dog_poop
# on_turn_off:
# then:
# - script.execute: update_declan_ui
# - homeassistant.service:
# service: switch.turn_off
# data:
# entity_id: switch.chore_tracker_declan_scoop_dog_poop
# # ── Chloe's chores ──────────────────────────
# - platform: template
# name: "Chloe - Fill kitty feeders"
# id: chloe_fill_kitty_feeders
# icon: mdi:checkbox-marked-circle
# optimistic: true
# restore_mode: RESTORE_DEFAULT_OFF
# on_turn_on:
# then:
# - script.execute: update_chloe_ui
# - homeassistant.service:
# service: switch.turn_on
# data:
# entity_id: switch.chore_tracker_chloe_fill_kitty_feeders
# on_turn_off:
# then:
# - script.execute: update_chloe_ui
# - homeassistant.service:
# service: switch.turn_off
# data:
# entity_id: switch.chore_tracker_chloe_fill_kitty_feeders
# - platform: template
# name: "Chloe - Scoop Kitty Litter"
# id: chloe_scoop_kitty_litter
# icon: mdi:checkbox-marked-circle
# optimistic: true
# restore_mode: RESTORE_DEFAULT_OFF
# on_turn_on:
# then:
# - script.execute: update_chloe_ui
# - homeassistant.service:
# service: switch.turn_on
# data:
# entity_id: switch.chore_tracker_chloe_scoop_kitty_litter
# on_turn_off:
# then:
# - script.execute: update_chloe_ui
# - homeassistant.service:
# service: switch.turn_off
# data:
# entity_id: switch.chore_tracker_chloe_scoop_kitty_litter
# - platform: template
# name: "Chloe - Replace Kitty Litter Bags"
# id: chloe_replace_kitty_litter_bags
# icon: mdi:checkbox-marked-circle
# optimistic: true
# restore_mode: RESTORE_DEFAULT_OFF
# on_turn_on:
# then:
# - script.execute: update_chloe_ui
# - homeassistant.service:
# service: switch.turn_on
# data:
# entity_id: switch.chore_tracker_chloe_replace_kitty_litter_bags
# on_turn_off:
# then:
# - script.execute: update_chloe_ui
# - homeassistant.service:
# service: switch.turn_off
# data:
# entity_id: switch.chore_tracker_chloe_replace_kitty_litter_bags
# # ── Sensors — reported to HA ─────────────────────────────────────────────────
# sensor:
# - platform: template
# name: "Jordyn Chores Done"
# id: jordyn_chores_done
# accuracy_decimals: 0
# update_interval: 2s
# lambda: |-
# int done = 0;
# if (id(jordyn_put_on_underwear).state) done++;
# if (id(jordyn_brush_teeth).state) done++;
# if (id(jordyn_fill_water_bowls).state) done++;
# if (id(jordyn_restock_cat_food_cans).state) done++;
# return done;
# - platform: template
# name: "Declan Chores Done"
# id: declan_chores_done
# accuracy_decimals: 0
# update_interval: 2s
# lambda: |-
# int done = 0;
# if (id(declan_take_morning_pill).state) done++;
# if (id(declan_scoop_dog_poop).state) done++;
# return done;
# - platform: template
# name: "Chloe Chores Done"
# id: chloe_chores_done
# accuracy_decimals: 0
# update_interval: 2s
# lambda: |-
# int done = 0;
# if (id(chloe_fill_kitty_feeders).state) done++;
# if (id(chloe_scoop_kitty_litter).state) done++;
# if (id(chloe_replace_kitty_litter_bags).state) done++;
# return done;
# # Diagnostic sensors
# - platform: wifi_signal
# name: "${device_name} WiFi Signal"
# update_interval: 60s
# - platform: uptime
# name: "${device_name} Uptime"
# update_interval: 60s
# text_sensor:
# - platform: wifi_info
# ip_address:
# name: "${device_name} IP Address"
# ssid:
# name: "${device_name} Connected SSID"
# bssid:
# name: "${device_name} BSSID"
# # Watchdog to auto-reboot if things go wrong
# # Note: interval fires on first tick (t≈0), so guard with millis() to ensure
# # the device has actually been running for 5+ minutes before triggering.
# interval:
# - interval: 5min
# then:
# - if:
# condition:
# and:
# - lambda: 'return millis() > 300000;' # >5 min uptime
# - not:
# wifi.connected:
# then:
# - logger.log: "Wi-Fi not connected for 5 min, rebooting..."
# - delay: 10s
# - lambda: 'App.safe_reboot();'
# # ── Scripts — reset + UI update ───────────────────────────────────────────────
# script:
# - id: reset_jordyn_chores
# mode: single
# then:
# - lambda: |-
# id(jordyn_put_on_underwear).turn_off();
# id(jordyn_brush_teeth).turn_off();
# id(jordyn_fill_water_bowls).turn_off();
# id(jordyn_restock_cat_food_cans).turn_off();
# - script.execute: update_jordyn_ui
# - id: update_jordyn_ui
# mode: single
# then:
# - lambda: |-
# int done = (id(jordyn_put_on_underwear).state ? 1 : 0) + (id(jordyn_brush_teeth).state ? 1 : 0) + (id(jordyn_fill_water_bowls).state ? 1 : 0) + (id(jordyn_restock_cat_food_cans).state ? 1 : 0);
# int total = 4;
# char buf[24];
# // Progress bar + label
# lv_bar_set_value(id(progress_bar_jordyn), done, LV_ANIM_ON);
# snprintf(buf, sizeof(buf), "%d / 4", done);
# lv_label_set_text(id(progress_label_jordyn), buf);
# // All-done message in sidebar
# lv_label_set_text(id(all_done_label_jordyn), done == total ? "All done!" : "");
# // Home button: RED outline = incomplete, SOLID GREEN = all done
# if (done == total) {
# lv_obj_set_style_bg_opa(id(home_btn_jordyn), LV_OPA_COVER, LV_PART_MAIN);
# lv_obj_set_style_bg_color(id(home_btn_jordyn), lv_color_hex(0x6BCB77), LV_PART_MAIN);
# lv_obj_set_style_border_width(id(home_btn_jordyn), 0, LV_PART_MAIN);
# lv_label_set_text(id(home_status_jordyn), "\u2713 all done!");
# lv_obj_set_style_text_color(id(home_status_jordyn), lv_color_hex(0xFFFFFF), LV_PART_MAIN);
# } else {
# lv_obj_set_style_bg_opa(id(home_btn_jordyn), LV_OPA_COVER, LV_PART_MAIN);
# lv_obj_set_style_bg_color(id(home_btn_jordyn), lv_color_hex(0xFF4757), LV_PART_MAIN);
# lv_obj_set_style_border_width(id(home_btn_jordyn), 0, LV_PART_MAIN);
# snprintf(buf, sizeof(buf), "%d left", total - done);
# lv_label_set_text(id(home_status_jordyn), buf);
# lv_obj_set_style_text_color(id(home_status_jordyn), lv_color_hex(0xFF4757), LV_PART_MAIN);
# }
# // Card colours
# if (id(jordyn_put_on_underwear).state) {
# lv_obj_set_style_bg_color(id(card_jordyn_put_on_underwear), lv_color_hex(0xF0FFF4), LV_PART_MAIN);
# lv_obj_set_style_border_color(id(card_jordyn_put_on_underwear), lv_color_hex(0x6BCB77), LV_PART_MAIN);
# lv_obj_set_style_border_width(id(card_jordyn_put_on_underwear), 3, LV_PART_MAIN);
# lv_label_set_text(id(check_jordyn_put_on_underwear), "\u2705");
# } else {
# lv_obj_set_style_bg_color(id(card_jordyn_put_on_underwear), lv_color_hex(0xFFFFFF), LV_PART_MAIN);
# lv_obj_set_style_border_color(id(card_jordyn_put_on_underwear), lv_color_hex(0xEEEEEE), LV_PART_MAIN);
# lv_obj_set_style_border_width(id(card_jordyn_put_on_underwear), 2, LV_PART_MAIN);
# lv_label_set_text(id(check_jordyn_put_on_underwear), "");
# }
# if (id(jordyn_brush_teeth).state) {
# lv_obj_set_style_bg_color(id(card_jordyn_brush_teeth), lv_color_hex(0xF0FFF4), LV_PART_MAIN);
# lv_obj_set_style_border_color(id(card_jordyn_brush_teeth), lv_color_hex(0x6BCB77), LV_PART_MAIN);
# lv_obj_set_style_border_width(id(card_jordyn_brush_teeth), 3, LV_PART_MAIN);
# lv_label_set_text(id(check_jordyn_brush_teeth), "\u2705");
# } else {
# lv_obj_set_style_bg_color(id(card_jordyn_brush_teeth), lv_color_hex(0xFFFFFF), LV_PART_MAIN);
# lv_obj_set_style_border_color(id(card_jordyn_brush_teeth), lv_color_hex(0xEEEEEE), LV_PART_MAIN);
# lv_obj_set_style_border_width(id(card_jordyn_brush_teeth), 2, LV_PART_MAIN);
# lv_label_set_text(id(check_jordyn_brush_teeth), "");
# }
# if (id(jordyn_fill_water_bowls).state) {
# lv_obj_set_style_bg_color(id(card_jordyn_fill_water_bowls), lv_color_hex(0xF0FFF4), LV_PART_MAIN);
# lv_obj_set_style_border_color(id(card_jordyn_fill_water_bowls), lv_color_hex(0x6BCB77), LV_PART_MAIN);
# lv_obj_set_style_border_width(id(card_jordyn_fill_water_bowls), 3, LV_PART_MAIN);
# lv_label_set_text(id(check_jordyn_fill_water_bowls), "\u2705");
# } else {
# lv_obj_set_style_bg_color(id(card_jordyn_fill_water_bowls), lv_color_hex(0xFFFFFF), LV_PART_MAIN);
# lv_obj_set_style_border_color(id(card_jordyn_fill_water_bowls), lv_color_hex(0xEEEEEE), LV_PART_MAIN);
# lv_obj_set_style_border_width(id(card_jordyn_fill_water_bowls), 2, LV_PART_MAIN);
# lv_label_set_text(id(check_jordyn_fill_water_bowls), "");
# }
# if (id(jordyn_restock_cat_food_cans).state) {
# lv_obj_set_style_bg_color(id(card_jordyn_restock_cat_food_cans), lv_color_hex(0xF0FFF4), LV_PART_MAIN);
# lv_obj_set_style_border_color(id(card_jordyn_restock_cat_food_cans), lv_color_hex(0x6BCB77), LV_PART_MAIN);
# lv_obj_set_style_border_width(id(card_jordyn_restock_cat_food_cans), 3, LV_PART_MAIN);
# lv_label_set_text(id(check_jordyn_restock_cat_food_cans), "\u2705");
# } else {
# lv_obj_set_style_bg_color(id(card_jordyn_restock_cat_food_cans), lv_color_hex(0xFFFFFF), LV_PART_MAIN);
# lv_obj_set_style_border_color(id(card_jordyn_restock_cat_food_cans), lv_color_hex(0xEEEEEE), LV_PART_MAIN);
# lv_obj_set_style_border_width(id(card_jordyn_restock_cat_food_cans), 2, LV_PART_MAIN);
# lv_label_set_text(id(check_jordyn_restock_cat_food_cans), "");
# }
# - id: reset_declan_chores
# mode: single
# then:
# - lambda: |-
# id(declan_take_morning_pill).turn_off();
# id(declan_scoop_dog_poop).turn_off();
# - script.execute: update_declan_ui
# - id: update_declan_ui
# mode: single
# then:
# - lambda: |-
# int done = (id(declan_take_morning_pill).state ? 1 : 0) + (id(declan_scoop_dog_poop).state ? 1 : 0);
# int total = 2;
# char buf[24];
# // Progress bar + label
# lv_bar_set_value(id(progress_bar_declan), done, LV_ANIM_ON);
# snprintf(buf, sizeof(buf), "%d / 2", done);
# lv_label_set_text(id(progress_label_declan), buf);
# // All-done message in sidebar
# lv_label_set_text(id(all_done_label_declan), done == total ? "All done!" : "");
# // Home button: RED outline = incomplete, SOLID GREEN = all done
# if (done == total) {
# lv_obj_set_style_bg_opa(id(home_btn_declan), LV_OPA_COVER, LV_PART_MAIN);
# lv_obj_set_style_bg_color(id(home_btn_declan), lv_color_hex(0x6BCB77), LV_PART_MAIN);
# lv_obj_set_style_border_width(id(home_btn_declan), 0, LV_PART_MAIN);
# lv_label_set_text(id(home_status_declan), "\u2713 all done!");
# lv_obj_set_style_text_color(id(home_status_declan), lv_color_hex(0xFFFFFF), LV_PART_MAIN);
# } else {
# lv_obj_set_style_bg_opa(id(home_btn_declan), LV_OPA_COVER, LV_PART_MAIN);
# lv_obj_set_style_bg_color(id(home_btn_declan), lv_color_hex(0xFF4757), LV_PART_MAIN);
# lv_obj_set_style_border_width(id(home_btn_declan), 0, LV_PART_MAIN);
# snprintf(buf, sizeof(buf), "%d left", total - done);
# lv_label_set_text(id(home_status_declan), buf);
# lv_obj_set_style_text_color(id(home_status_declan), lv_color_hex(0xFF4757), LV_PART_MAIN);
# }
# // Card colours
# if (id(declan_take_morning_pill).state) {
# lv_obj_set_style_bg_color(id(card_declan_take_morning_pill), lv_color_hex(0xF0FFF4), LV_PART_MAIN);
# lv_obj_set_style_border_color(id(card_declan_take_morning_pill), lv_color_hex(0x6BCB77), LV_PART_MAIN);
# lv_obj_set_style_border_width(id(card_declan_take_morning_pill), 3, LV_PART_MAIN);
# lv_label_set_text(id(check_declan_take_morning_pill), "\u2705");
# } else {
# lv_obj_set_style_bg_color(id(card_declan_take_morning_pill), lv_color_hex(0xFFFFFF), LV_PART_MAIN);
# lv_obj_set_style_border_color(id(card_declan_take_morning_pill), lv_color_hex(0xEEEEEE), LV_PART_MAIN);
# lv_obj_set_style_border_width(id(card_declan_take_morning_pill), 2, LV_PART_MAIN);
# lv_label_set_text(id(check_declan_take_morning_pill), "");
# }
# if (id(declan_scoop_dog_poop).state) {
# lv_obj_set_style_bg_color(id(card_declan_scoop_dog_poop), lv_color_hex(0xF0FFF4), LV_PART_MAIN);
# lv_obj_set_style_border_color(id(card_declan_scoop_dog_poop), lv_color_hex(0x6BCB77), LV_PART_MAIN);
# lv_obj_set_style_border_width(id(card_declan_scoop_dog_poop), 3, LV_PART_MAIN);
# lv_label_set_text(id(check_declan_scoop_dog_poop), "\u2705");
# } else {
# lv_obj_set_style_bg_color(id(card_declan_scoop_dog_poop), lv_color_hex(0xFFFFFF), LV_PART_MAIN);
# lv_obj_set_style_border_color(id(card_declan_scoop_dog_poop), lv_color_hex(0xEEEEEE), LV_PART_MAIN);
# lv_obj_set_style_border_width(id(card_declan_scoop_dog_poop), 2, LV_PART_MAIN);
# lv_label_set_text(id(check_declan_scoop_dog_poop), "");
# }
# - id: reset_chloe_chores
# mode: single
# then:
# - lambda: |-
# id(chloe_fill_kitty_feeders).turn_off();
# id(chloe_scoop_kitty_litter).turn_off();
# id(chloe_replace_kitty_litter_bags).turn_off();
# - script.execute: update_chloe_ui
# - id: update_chloe_ui
# mode: single
# then:
# - lambda: |-
# int done = (id(chloe_fill_kitty_feeders).state ? 1 : 0) + (id(chloe_scoop_kitty_litter).state ? 1 : 0) + (id(chloe_replace_kitty_litter_bags).state ? 1 : 0);
# int total = 3;
# char buf[24];
# // Progress bar + label
# lv_bar_set_value(id(progress_bar_chloe), done, LV_ANIM_ON);
# snprintf(buf, sizeof(buf), "%d / 3", done);
# lv_label_set_text(id(progress_label_chloe), buf);
# // All-done message in sidebar
# lv_label_set_text(id(all_done_label_chloe), done == total ? "All done!" : "");
# // Home button: RED outline = incomplete, SOLID GREEN = all done
# if (done == total) {
# lv_obj_set_style_bg_opa(id(home_btn_chloe), LV_OPA_COVER, LV_PART_MAIN);
# lv_obj_set_style_bg_color(id(home_btn_chloe), lv_color_hex(0x6BCB77), LV_PART_MAIN);
# lv_obj_set_style_border_width(id(home_btn_chloe), 0, LV_PART_MAIN);
# lv_label_set_text(id(home_status_chloe), "\u2713 all done!");
# lv_obj_set_style_text_color(id(home_status_chloe), lv_color_hex(0xFFFFFF), LV_PART_MAIN);
# } else {
# lv_obj_set_style_bg_opa(id(home_btn_chloe), LV_OPA_COVER, LV_PART_MAIN);
# lv_obj_set_style_bg_color(id(home_btn_chloe), lv_color_hex(0xFF4757), LV_PART_MAIN);
# lv_obj_set_style_border_width(id(home_btn_chloe), 0, LV_PART_MAIN);
# snprintf(buf, sizeof(buf), "%d left", total - done);
# lv_label_set_text(id(home_status_chloe), buf);
# lv_obj_set_style_text_color(id(home_status_chloe), lv_color_hex(0xFF4757), LV_PART_MAIN);
# }
# // Card colours
# if (id(chloe_fill_kitty_feeders).state) {
# lv_obj_set_style_bg_color(id(card_chloe_fill_kitty_feeders), lv_color_hex(0xF0FFF4), LV_PART_MAIN);
# lv_obj_set_style_border_color(id(card_chloe_fill_kitty_feeders), lv_color_hex(0x6BCB77), LV_PART_MAIN);
# lv_obj_set_style_border_width(id(card_chloe_fill_kitty_feeders), 3, LV_PART_MAIN);
# lv_label_set_text(id(check_chloe_fill_kitty_feeders), "\u2705");
# } else {
# lv_obj_set_style_bg_color(id(card_chloe_fill_kitty_feeders), lv_color_hex(0xFFFFFF), LV_PART_MAIN);
# lv_obj_set_style_border_color(id(card_chloe_fill_kitty_feeders), lv_color_hex(0xEEEEEE), LV_PART_MAIN);
# lv_obj_set_style_border_width(id(card_chloe_fill_kitty_feeders), 2, LV_PART_MAIN);
# lv_label_set_text(id(check_chloe_fill_kitty_feeders), "");
# }
# if (id(chloe_scoop_kitty_litter).state) {
# lv_obj_set_style_bg_color(id(card_chloe_scoop_kitty_litter), lv_color_hex(0xF0FFF4), LV_PART_MAIN);
# lv_obj_set_style_border_color(id(card_chloe_scoop_kitty_litter), lv_color_hex(0x6BCB77), LV_PART_MAIN);
# lv_obj_set_style_border_width(id(card_chloe_scoop_kitty_litter), 3, LV_PART_MAIN);
# lv_label_set_text(id(check_chloe_scoop_kitty_litter), "\u2705");
# } else {
# lv_obj_set_style_bg_color(id(card_chloe_scoop_kitty_litter), lv_color_hex(0xFFFFFF), LV_PART_MAIN);
# lv_obj_set_style_border_color(id(card_chloe_scoop_kitty_litter), lv_color_hex(0xEEEEEE), LV_PART_MAIN);
# lv_obj_set_style_border_width(id(card_chloe_scoop_kitty_litter), 2, LV_PART_MAIN);
# lv_label_set_text(id(check_chloe_scoop_kitty_litter), "");
# }
# if (id(chloe_replace_kitty_litter_bags).state) {
# lv_obj_set_style_bg_color(id(card_chloe_replace_kitty_litter_bags), lv_color_hex(0xF0FFF4), LV_PART_MAIN);
# lv_obj_set_style_border_color(id(card_chloe_replace_kitty_litter_bags), lv_color_hex(0x6BCB77), LV_PART_MAIN);
# lv_obj_set_style_border_width(id(card_chloe_replace_kitty_litter_bags), 3, LV_PART_MAIN);
# lv_label_set_text(id(check_chloe_replace_kitty_litter_bags), "\u2705");
# } else {
# lv_obj_set_style_bg_color(id(card_chloe_replace_kitty_litter_bags), lv_color_hex(0xFFFFFF), LV_PART_MAIN);
# lv_obj_set_style_border_color(id(card_chloe_replace_kitty_litter_bags), lv_color_hex(0xEEEEEE), LV_PART_MAIN);
# lv_obj_set_style_border_width(id(card_chloe_replace_kitty_litter_bags), 2, LV_PART_MAIN);
# lv_label_set_text(id(check_chloe_replace_kitty_litter_bags), "");
# }
# # ── LVGL UI ───────────────────────────────────────────────────────────────────
# lvgl:
# displays:
# - main_display
# touchscreens:
# - touch
# theme:
# button:
# radius: 20
# border_width: 0
# pages:
# - id: page_home
# bg_color: 0xFFF9F0
# widgets:
# - label:
# x: 0
# y: 36
# width: 776
# align: TOP_MID
# text: "Chore Tracker"
# text_font: font_title
# text_color: 0x2D3436
# - label:
# x: 0
# y: 90
# width: 776
# align: TOP_MID
# text: "Tap a name to check off chores"
# text_font: font_small
# text_color: 0xB2BEC3
# - button:
# x: 640
# y: 428
# width: 148
# height: 40
# bg_color: 0xFF4757
# radius: 12
# on_click:
# then:
# - script.execute: reset_jordyn_chores
# - script.execute: reset_declan_chores
# - script.execute: reset_chloe_chores
# widgets:
# - label:
# align: CENTER
# text: "Reset All"
# text_color: 0xFFFFFF
# text_font: font_tiny
# - button:
# id: home_btn_jordyn
# x: 80
# y: 130
# width: 200
# height: 210
# bg_color: 0xFF4757
# bg_opa: COVER
# border_width: 0
# radius: 24
# on_click:
# then:
# - lvgl.page.show: page_jordyn
# widgets:
# - label:
# align: CENTER
# y: -45
# text: "󰄛"
# text_font: font_mdi_large
# - label:
# align: CENTER
# y: 22
# text: "Jordyn"
# text_font: font_name
# text_color: 0xFFFFFF
# - label:
# id: home_status_jordyn
# align: CENTER
# y: 66
# text: "not done"
# text_font: font_tiny
# text_color: 0xFFFFFF
# - button:
# id: home_btn_declan
# x: 300
# y: 130
# width: 200
# height: 210
# bg_color: 0xFF4757
# bg_opa: COVER
# border_width: 0
# radius: 24
# on_click:
# then:
# - lvgl.page.show: page_declan
# widgets:
# - label:
# align: CENTER
# y: -45
# text: "󱑷"
# text_font: font_mdi_large
# - label:
# align: CENTER
# y: 22
# text: "Declan"
# text_font: font_name
# text_color: 0xFFFFFF
# - label:
# id: home_status_declan
# align: CENTER
# y: 66
# text: "not done"
# text_font: font_tiny
# text_color: 0xFFFFFF
# - button:
# id: home_btn_chloe
# x: 520
# y: 130
# width: 200
# height: 210
# bg_color: 0xFF4757
# bg_opa: COVER
# border_width: 0
# radius: 24
# on_click:
# then:
# - lvgl.page.show: page_chloe
# widgets:
# - label:
# align: CENTER
# y: -45
# text: "󱚣"
# text_font: font_mdi_large
# - label:
# align: CENTER
# y: 22
# text: "Chloe"
# text_font: font_name
# text_color: 0xFFFFFF
# - label:
# id: home_status_chloe
# align: CENTER
# y: 66
# text: "not done"
# text_font: font_tiny
# text_color: 0xFFFFFF
# - id: page_jordyn
# bg_color: 0xFFF9F0
# widgets:
# # ── Sidebar ──────────────────────────────────────────────────────────
# - obj:
# x: 0
# y: 0
# width: 172
# height: 480
# bg_color: 0x5F5980
# border_width: 0
# radius: 0
# pad_all: 0
# widgets:
# - label:
# x: 0
# y: 22
# width: 172
# align: TOP_MID
# text: "󰄛"
# text_font: font_mdi_large
# - label:
# x: 0
# y: 76
# width: 172
# align: TOP_MID
# text: "Jordyn"
# text_font: font_name
# text_color: 0xFFFFFF
# - bar:
# id: progress_bar_jordyn
# x: 14
# y: 132
# width: 144
# height: 16
# bg_color: 0x2F3061
# indicator:
# bg_color: 0xFFFFFF
# value: 0
# min_value: 0
# max_value: 4
# - label:
# id: progress_label_jordyn
# x: 0
# y: 156
# width: 172
# align: TOP_MID
# text: "0 / 4"
# text_font: font_small
# text_color: 0xDDEEFF
# - label:
# id: all_done_label_jordyn
# x: 0
# y: 192
# width: 172
# align: TOP_MID
# text: ""
# text_font: font_small
# text_color: 0xFFFFFF
# - button:
# x: 14
# y: 374
# width: 144
# height: 44
# bg_color: 0xFF4757
# radius: 14
# on_click:
# then:
# - script.execute: reset_jordyn_chores
# widgets:
# - label:
# align: CENTER
# text: "Reset"
# text_color: 0xFFFFFF
# text_font: font_small
# - button:
# x: 14
# y: 424
# width: 144
# height: 44
# bg_color: 0x2F3061
# radius: 14
# on_click:
# then:
# - lvgl.page.show: page_home
# widgets:
# - label:
# align: CENTER
# text: "Home"
# text_color: 0xFFFFFF
# text_font: font_small
# # ── Chore cards ───────────────────────────────────────────────────────
# - button:
# id: card_jordyn_put_on_underwear
# x: 182
# y: 12
# width: 195
# height: 138
# bg_color: 0xFFFFFF
# border_color: 0xEEEEEE
# border_width: 2
# radius: 20
# shadow_color: 0xCCCCCC
# shadow_width: 4
# shadow_ofs_y: 3
# on_click:
# then:
# - switch.toggle: jordyn_put_on_underwear
# widgets:
# - label:
# id: check_jordyn_put_on_underwear
# align: TOP_RIGHT
# x: -10
# y: 8
# text: ""
# text_font: font_med
# - label:
# align: CENTER
# y: -34
# text: "󰋣"
# text_font: font_mdi_small
# text_color: 0x5F5980
# - label:
# align: CENTER
# y: 17
# text: "Put on underwear"
# text_font: font_small
# text_color: 0x2D3436
# - button:
# id: card_jordyn_brush_teeth
# x: 387
# y: 12
# width: 195
# height: 138
# bg_color: 0xFFFFFF
# border_color: 0xEEEEEE
# border_width: 2
# radius: 20
# shadow_color: 0xCCCCCC
# shadow_width: 4
# shadow_ofs_y: 3
# on_click:
# then:
# - switch.toggle: jordyn_brush_teeth
# widgets:
# - label:
# id: check_jordyn_brush_teeth
# align: TOP_RIGHT
# x: -10
# y: 8
# text: ""
# text_font: font_med
# - label:
# align: CENTER
# y: -34
# text: "󰦩"
# text_font: font_mdi_small
# text_color: 0x5F5980
# - label:
# align: CENTER
# y: 17
# text: "Brush Teeth"
# text_font: font_small
# text_color: 0x2D3436
# - button:
# id: card_jordyn_fill_water_bowls
# x: 592
# y: 12
# width: 195
# height: 138
# bg_color: 0xFFFFFF
# border_color: 0xEEEEEE
# border_width: 2
# radius: 20
# shadow_color: 0xCCCCCC
# shadow_width: 4
# shadow_ofs_y: 3
# on_click:
# then:
# - switch.toggle: jordyn_fill_water_bowls
# widgets:
# - label:
# id: check_jordyn_fill_water_bowls
# align: TOP_RIGHT
# x: -10
# y: 8
# text: ""
# text_font: font_med
# - label:
# align: CENTER
# y: -34
# text: "󰆫"
# text_font: font_mdi_small
# text_color: 0x5F5980
# - label:
# align: CENTER
# y: 17
# text: "Fill water bowls"
# text_font: font_small
# text_color: 0x2D3436
# - button:
# id: card_jordyn_restock_cat_food_cans
# x: 182
# y: 160
# width: 195
# height: 138
# bg_color: 0xFFFFFF
# border_color: 0xEEEEEE
# border_width: 2
# radius: 20
# shadow_color: 0xCCCCCC
# shadow_width: 4
# shadow_ofs_y: 3
# on_click:
# then:
# - switch.toggle: jordyn_restock_cat_food_cans
# widgets:
# - label:
# id: check_jordyn_restock_cat_food_cans
# align: TOP_RIGHT
# x: -10
# y: 8
# text: ""
# text_font: font_med
# - label:
# align: CENTER
# y: -34
# text: "󱜜"
# text_font: font_mdi_small
# text_color: 0x5F5980
# - label:
# align: CENTER
# y: 17
# text: "Restock Cat Food Cans"
# text_font: font_small
# text_color: 0x2D3436
# - id: page_declan
# bg_color: 0xFFF9F0
# widgets:
# # ── Sidebar ──────────────────────────────────────────────────────────
# - obj:
# x: 0
# y: 0
# width: 172
# height: 480
# bg_color: 0x720026
# border_width: 0
# radius: 0
# pad_all: 0
# widgets:
# - label:
# x: 0
# y: 22
# width: 172
# align: TOP_MID
# text: "󱑷"
# text_font: font_mdi_large
# - label:
# x: 0
# y: 76
# width: 172
# align: TOP_MID
# text: "Declan"
# text_font: font_name
# text_color: 0xFFFFFF
# - bar:
# id: progress_bar_declan
# x: 14
# y: 132
# width: 144
# height: 16
# bg_color: 0x4F000B
# indicator:
# bg_color: 0xFFFFFF
# value: 0
# min_value: 0
# max_value: 2
# - label:
# id: progress_label_declan
# x: 0
# y: 156
# width: 172
# align: TOP_MID
# text: "0 / 2"
# text_font: font_small
# text_color: 0xDDEEFF
# - label:
# id: all_done_label_declan
# x: 0
# y: 192
# width: 172
# align: TOP_MID
# text: ""
# text_font: font_small
# text_color: 0xFFFFFF
# - button:
# x: 14
# y: 374
# width: 144
# height: 44
# bg_color: 0xFF4757
# radius: 14
# on_click:
# then:
# - script.execute: reset_declan_chores
# widgets:
# - label:
# align: CENTER
# text: "Reset"
# text_color: 0xFFFFFF
# text_font: font_small
# - button:
# x: 14
# y: 424
# width: 144
# height: 44
# bg_color: 0x4F000B
# radius: 14
# on_click:
# then:
# - lvgl.page.show: page_home
# widgets:
# - label:
# align: CENTER
# text: "Home"
# text_color: 0xFFFFFF
# text_font: font_small
# # ── Chore cards ───────────────────────────────────────────────────────
# - button:
# id: card_declan_take_morning_pill
# x: 182
# y: 12
# width: 195
# height: 138
# bg_color: 0xFFFFFF
# border_color: 0xEEEEEE
# border_width: 2
# radius: 20
# shadow_color: 0xCCCCCC
# shadow_width: 4
# shadow_ofs_y: 3
# on_click:
# then:
# - switch.toggle: declan_take_morning_pill
# widgets:
# - label:
# id: check_declan_take_morning_pill
# align: TOP_RIGHT
# x: -10
# y: 8
# text: ""
# text_font: font_med
# - label:
# align: CENTER
# y: -34
# text: "󰐂"
# text_font: font_mdi_small
# text_color: 0x720026
# - label:
# align: CENTER
# y: 17
# text: "Take morning pill"
# text_font: font_small
# text_color: 0x2D3436
# - button:
# id: card_declan_scoop_dog_poop
# x: 387
# y: 12
# width: 195
# height: 138
# bg_color: 0xFFFFFF
# border_color: 0xEEEEEE
# border_width: 2
# radius: 20
# shadow_color: 0xCCCCCC
# shadow_width: 4
# shadow_ofs_y: 3
# on_click:
# then:
# - switch.toggle: declan_scoop_dog_poop
# widgets:
# - label:
# id: check_declan_scoop_dog_poop
# align: TOP_RIGHT
# x: -10
# y: 8
# text: ""
# text_font: font_med
# - label:
# align: CENTER
# y: -34
# text: "󰇷"
# text_font: font_mdi_small
# text_color: 0x720026
# - label:
# align: CENTER
# y: 17
# text: "Scoop Dog Poop"
# text_font: font_small
# text_color: 0x2D3436
# - id: page_chloe
# bg_color: 0xFFF9F0
# widgets:
# # ── Sidebar ──────────────────────────────────────────────────────────
# - obj:
# x: 0
# y: 0
# width: 172
# height: 480
# bg_color: 0x73683B
# border_width: 0
# radius: 0
# pad_all: 0
# widgets:
# - label:
# x: 0
# y: 22
# width: 172
# align: TOP_MID
# text: "󱚣"
# text_font: font_mdi_large
# - label:
# x: 0
# y: 76
# width: 172
# align: TOP_MID
# text: "Chloe"
# text_font: font_name
# text_color: 0xFFFFFF
# - bar:
# id: progress_bar_chloe
# x: 14
# y: 132
# width: 144
# height: 16
# bg_color: 0x583E23
# indicator:
# bg_color: 0xFFFFFF
# value: 0
# min_value: 0
# max_value: 3
# - label:
# id: progress_label_chloe
# x: 0
# y: 156
# width: 172
# align: TOP_MID
# text: "0 / 3"
# text_font: font_small
# text_color: 0xDDEEFF
# - label:
# id: all_done_label_chloe
# x: 0
# y: 192
# width: 172
# align: TOP_MID
# text: ""
# text_font: font_small
# text_color: 0xFFFFFF
# - button:
# x: 14
# y: 374
# width: 144
# height: 44
# bg_color: 0xFF4757
# radius: 14
# on_click:
# then:
# - script.execute: reset_chloe_chores
# widgets:
# - label:
# align: CENTER
# text: "Reset"
# text_color: 0xFFFFFF
# text_font: font_small
# - button:
# x: 14
# y: 424
# width: 144
# height: 44
# bg_color: 0x583E23
# radius: 14
# on_click:
# then:
# - lvgl.page.show: page_home
# widgets:
# - label:
# align: CENTER
# text: "Home"
# text_color: 0xFFFFFF
# text_font: font_small
# # ── Chore cards ───────────────────────────────────────────────────────
# - button:
# id: card_chloe_fill_kitty_feeders
# x: 182
# y: 12
# width: 195
# height: 138
# bg_color: 0xFFFFFF
# border_color: 0xEEEEEE
# border_width: 2
# radius: 20
# shadow_color: 0xCCCCCC
# shadow_width: 4
# shadow_ofs_y: 3
# on_click:
# then:
# - switch.toggle: chloe_fill_kitty_feeders
# widgets:
# - label:
# id: check_chloe_fill_kitty_feeders
# align: TOP_RIGHT
# x: -10
# y: 8
# text: ""
# text_font: font_med
# - label:
# align: CENTER
# y: -34
# text: "󰊩"
# text_font: font_mdi_small
# text_color: 0x73683B
# - label:
# align: CENTER
# y: 17
# text: "Fill kitty feeders"
# text_font: font_small
# text_color: 0x2D3436
# - button:
# id: card_chloe_scoop_kitty_litter
# x: 387
# y: 12
# width: 195
# height: 138
# bg_color: 0xFFFFFF
# border_color: 0xEEEEEE
# border_width: 2
# radius: 20
# shadow_color: 0xCCCCCC
# shadow_width: 4
# shadow_ofs_y: 3
# on_click:
# then:
# - switch.toggle: chloe_scoop_kitty_litter
# widgets:
# - label:
# id: check_chloe_scoop_kitty_litter
# align: TOP_RIGHT
# x: -10
# y: 8
# text: ""
# text_font: font_med
# - label:
# align: CENTER
# y: -34
# text: "󰇷"
# text_font: font_mdi_small
# text_color: 0x73683B
# - label:
# align: CENTER
# y: 17
# text: "Scoop Kitty Litter"
# text_font: font_small
# text_color: 0x2D3436
# - button:
# id: card_chloe_replace_kitty_litter_bags
# x: 592
# y: 12
# width: 195
# height: 138
# bg_color: 0xFFFFFF
# border_color: 0xEEEEEE
# border_width: 2
# radius: 20
# shadow_color: 0xCCCCCC
# shadow_width: 4
# shadow_ofs_y: 3
# on_click:
# then:
# - switch.toggle: chloe_replace_kitty_litter_bags
# widgets:
# - label:
# id: check_chloe_replace_kitty_litter_bags
# align: TOP_RIGHT
# x: -10
# y: 8
# text: ""
# text_font: font_med
# - label:
# align: CENTER
# y: -34
# text: "󰇷"
# text_font: font_mdi_small
# text_color: 0x73683B
# - label:
# align: CENTER
# y: 17
# text: "Replace Kitty Litter Bags"
# text_font: font_small
# text_color: 0x2D3436