substitutions: device_name: "habbit_desk" comment: "esp32" location: "kantoor" #api_password: !secret deskcontroller_api #ota_password: !secret ota_password wifi_ssid: !secret wifi_ssid wifi_password: !secret wifi_password gateway: !secret ip_gateway subnet: !secret ip_subnet # Device customization # Personalización del dispositivo name: habbit_desk friendly_name: Habbit Desk logo: https://aguacatec.es/wp-content/uploads/2025/11/habbit_aguacatec.png background_color: 0x000000 text_color: 0xFFFFFF text_color_secondary: 0x626262 iddle_time: 60s greeting: "Habbit Desk v2" # Entities weather_temperature: weather.forecast_home home_temperature: sensor.temperatuur_slaapkamer_temperatuur home_humidity: sensor.temperatuur_slaapkamer_luchtvochtigheid climate: climate.smart_radiator_thermostat_x cover: cover.shelly_projector_scherm # dehumidifier: humidifier.deshumidificador # fan_ceiling: fan.ventilador_estudio light_ceiling: light.lamp_slaapkamer_2 light_desk: light.lamp_bed_links_dim # printer_3d: switch.impresora_3d # printer_3d_octoprint_status: switch.orange_pi # printer_3d_octoprint_operation: sensor.octoprint_current_state # printer_3d_octoprint_progress: sensor.octoprint_job_percentage # printer_3d_pause_button: button.octoprint_pause_job # printer_3d_stop_button: button.octoprint_stop_job # printer_3d_spotlight: switch.foco_impresora_3d vacuum: vacuum.roborock_qrevo_s # Other settings # Otros ajustes allowed_characters: " ¿?¡!#%'()+,-./:µ³°0123456789ABCDEFGHIJKLMNOPQRSTUVWYZabcdefghijklmnopqrstuvwxyzáéíóú" ################################################################################################################ esphome: name: ${name} friendly_name: ${friendly_name} psram: mode: octal esp32: board: esp32-s3-devkitc-1 flash_size: 16MB framework: type: esp-idf # Enable logging logger: # Enable Home Assistant API api: encryption: key: !secret deskcontroller_api ota: - platform: esphome password: !secret ota_password wifi: ssid: !secret wifi_ssid password: !secret wifi_password # Enable fallback hotspot (captive portal) in case wifi connection fails ap: ssid: "Habbit-Desk-V2 Fallback Hotspot" password: "2ddsfd0123" captive_portal: external_components: - source: github://buglloc/esphome-components components: [sy6970] #disable Blinking led sy6970: i2c_id: touchscreen_bus state_led_enable: false font: - file: "gfonts://Kanit" id: font_clock size: 40 glyphs: ${allowed_characters} - file: "gfonts://Kanit" id: font_date size: 20 glyphs: ${allowed_characters} - file: "gfonts://Kanit" id: font_greeting size: 20 glyphs: ${allowed_characters} - file: "gfonts://Material+Symbols+Outlined" id: font_home_icons size: 40 glyphs: [ "\U0000e430", # sun "\U0000e1ff", # thermometer "\U0000f87e", # water-percent "\U0000e58c", # house-dots "\U0000e879", # exit "\U0000e548", # more "\U0000ec12", # roller-shade "\U0000ec11", # roller-shade-close "\U0000e145", # plus "\U0000f88a", # minus " ", ] - file: "gfonts://Kanit" id: font_home_info size: 28 glyphs: ${allowed_characters} - file: "gfonts://Material+Symbols+Outlined" id: font_device_icons size: 100 glyphs: [ "\U0000f02a", # ceiling-light "\U0000f168", # fan "\U0000ec12", # roller-shade "\U0000ec11", # roller-shade-close "\U0000ed38", # 3d "\U0000ef55", # fire "\U0000efc5", # vacuum "\U0000f7ed", # led-strip "\U0000e97e", # dehumidifier "\U0000e1c4", # play "\U0000e1a2", # pause "\U0000eaaa", # base "\U0000eee1", # locate "\U0000f418", # power "\U0000ef71", # stop "\U0000ebfe", # spotlight " ", ] - file: "gfonts://Kanit" id: font_slider_value size: 100 glyphs: ${allowed_characters} - file: "gfonts://Kanit" id: font_device_status size: 20 glyphs: ${allowed_characters} i2c: sda: 15 scl: 10 id: touchscreen_bus image: - file: ${logo} id: logo resize: 180x180 type: RGB565 transparency: alpha_channel - file: mdi:robot-vacuum id: icon_vacuum resize: 100x100 type: BINARY transparency: chroma_key - file: mdi:printer-3d-nozzle id: icon_3dprinter resize: 100x100 type: BINARY transparency: chroma_key - file: mdi:led-strip-variant id: icon_ledstrip resize: 100x100 type: BINARY transparency: chroma_key interval: - interval: 60s then: - lvgl.label.update: id: lbl_date text: !lambda 'return id(esptime).now().strftime("%d/%m/%y");' - lvgl.label.update: id: lbl_clock text: !lambda 'return id(esptime).now().strftime("%H:%M");' light: - platform: monochromatic output: backlight_pwm id: backlight name: "Display" icon: mdi:tablet restore_mode: ALWAYS_ON output: - platform: ledc pin: GPIO1 id: backlight_pwm sensor: # Home Assistant sensors # Sensores de Home Assistant - platform: homeassistant id: home_icon_weather_temperature entity_id: ${weather_temperature} internal: true on_value: then: - lvgl.label.update: id: lbl_weather_temperature text: format: "%.0f °C" args: - id(home_icon_weather_temperature).state - platform: homeassistant id: home_icon_home_temperature entity_id: ${home_temperature} internal: true on_value: then: - lvgl.label.update: id: lbl_home_temperature text: format: "%.0f °C" args: - id(home_icon_home_temperature).state - platform: homeassistant id: home_icon_home_humidity entity_id: ${home_humidity} internal: true on_value: then: - lvgl.label.update: id: lbl_home_humidity text: format: "%.0f%%" args: - id(home_icon_home_humidity).state - platform: homeassistant id: cover_position_value entity_id: ${cover} attribute: current_position internal: true filters: - lambda: |- if (isnan(x)) { return 0; } else { return x; } on_value: then: - lvgl.slider.update: id: slider_cover value: !lambda 'return (int)x;' - lambda: |- int position = (int) id(cover_position_value).state; char buf[8]; snprintf(buf, sizeof(buf), "%d %%", position); // Actualizar texto del label LVGL lv_label_set_text(id(slider_cover_value), buf); # - platform: homeassistant # id: printer_3d_octoprint_progress_value # entity_id: ${printer_3d_octoprint_progress} # internal: true # filters: # - lambda: |- # if (isnan(x)) { return 0; } # else { return x; } # on_value: # then: # - if: # condition: # lambda: 'return id(device_printer_3d_octoprint_operation).state == "Printing";' # then: # - lvgl.label.update: # id: lbl_device_printer_3d_octoprint_progress # text: # format: "Impresión al %.0f %%" # args: # - id(printer_3d_octoprint_progress_value).state spi: - id: display_qspi type: quad clk_pin: 17 data_pins: - 13 - 18 - 21 - 14 text_sensor: - platform: homeassistant id: device_light_ceiling entity_id: ${light_ceiling} internal: true on_value: then: - lvgl.label.update: id: lbl_device_light_ceiling text_color: !lambda |- if (x == "on") { return lv_color_hex(0xe6d754); } else { return lv_color_hex(0x626262); } # - platform: homeassistant # id: device_fan_ceiling # entity_id: ${fan_ceiling} # internal: true # on_value: # then: # - lvgl.label.update: # id: lbl_device_fan_ceiling # text_color: !lambda |- # if (x == "on") { # return lv_color_hex(0x54c9e6); # } else { # return lv_color_hex(0x626262); # } - platform: homeassistant id: device_cover entity_id: ${cover} internal: true on_value: then: - lvgl.label.update: id: lbl_device_cover text: !lambda |- if (x == "open") { return "\U0000ec12"; } else { return "\U0000ec11"; } text_color: !lambda |- if (x == "open") { return lv_color_hex(0xffffff); } else { return lv_color_hex(0x626262); } # - platform: homeassistant # id: device_printer_3d # entity_id: ${printer_3d} # internal: true # on_value: # then: # - lvgl.image.update: # id: lbl_device_printer_3d # image_recolor: !lambda |- # if (x == "on") { # return lv_color_hex(0xe65476); # } else { # return lv_color_hex(0x626262); # } # - lvgl.label.update: # id: lbl_device_printer_3d_state # text_color: !lambda |- # if (x == "on") { # return lv_color_hex(0xe65476); # } else { # return lv_color_hex(0x626262); # } # # - lvgl.label.update: # # id: lbl_device_printer_3d_octoprint_progress # # text: !lambda |- # # if (x == "off") { # # return " "; # # } # - lvgl.widget.redraw: # - platform: homeassistant # id: device_printer_3d_spotlight # entity_id: ${printer_3d_spotlight} # internal: true # on_value: # then: # - lvgl.label.update: # id: lbl_device_printer_3d_spotlight # text_color: !lambda |- # if (x == "on") { # return lv_color_hex(0xe6d754); # } else { # return lv_color_hex(0x626262); # } # - platform: homeassistant # id: device_printer_3d_octoprint # entity_id: ${printer_3d_octoprint_status} # internal: true # on_value: # then: # - lvgl.label.update: # id: lbl_device_printer_3d_octoprint_status # text: !lambda |- # if (x == "on") { # return "Octoprint (Online)"; # } else { # return "Octoprint (Offline)"; # } # text_color: !lambda |- # if (x == "on") { # return lv_color_hex(0x9def0d); # } else { # return lv_color_hex(0x626262); # } # - lvgl.label.update: # id: lbl_device_printer_3d_octoprint_progress # text: !lambda |- # if (x == "off") { # return " "; # } # - platform: homeassistant # id: device_printer_3d_octoprint_operation # entity_id: ${printer_3d_octoprint_operation} # internal: true # - platform: homeassistant # id: device_climate # entity_id: ${climate} # internal: true # on_value: # then: # - lvgl.label.update: # id: lbl_device_climate # text_color: !lambda |- # if (x == "heat") { # return lv_color_hex(0xffae00); # } else { # return lv_color_hex(0x626262); # } - platform: homeassistant id: device_vacuum entity_id: ${vacuum} internal: true on_value: then: - lvgl.image.update: id: lbl_device_vacuum image_recolor: !lambda |- if (x == "docked") { return lv_color_hex(0x626262); } else { return lv_color_hex(0xffffff); } - platform: homeassistant id: device_light_desk entity_id: ${light_desk} internal: true on_value: then: - lvgl.image.update: id: lbl_device_light_desk image_recolor: !lambda |- if (x == "on") { return lv_color_hex(0xe6d754); } else { return lv_color_hex(0x626262); } # - platform: homeassistant # id: device_dehumidifier # entity_id: ${dehumidifier} # internal: true # on_value: # then: # - lvgl.label.update: # id: lbl_device_dehumidifier # text_color: !lambda |- # if (x == "on") { # return lv_color_hex(0xe6d754); # } else { # return lv_color_hex(0x626262); # } time: - platform: homeassistant id: esptime touchscreen: - platform: axs15231 id: main_touch display: main_display i2c_id: touchscreen_bus transform: mirror_x: true mirror_y: true swap_xy: false calibration: x_min: 0 x_max: 640 y_min: 0 y_max: 180 on_touch: - logger.log: format: Touch at (%d, %d) args: [touch.x, touch.y] on_release: then: - if: condition: lvgl.is_paused then: - lvgl.page.show: page_home - light.turn_on: backlight - lvgl.resume: - lvgl.widget.redraw: display: - platform: mipi_spi id: main_display spi_id: display_qspi model: AXS15231 dimensions: height: 640 width: 180 cs_pin: 12 reset_pin: 16 rotation: 90 auto_clear_enabled: false lvgl: displays: - main_display touchscreens: - main_touch buffer_size: 100% on_idle: - timeout: ${iddle_time} then: - lvgl.pause: - light.turn_off: id: backlight style_definitions: - id: button_menu width: 60 height: 60 pad_all: 0 bg_color: 0x000000 border_color: 0x000000 align: BOTTOM_RIGHT - id: button_more width: 60 height: 60 pad_all: 0 bg_color: 0x000000 border_color: 0x000000 align: TOP_RIGHT - id: button_device_1 width: 130 height: 130 pad_all: 0 translate_x: 30 bg_color: 0x000000 border_color: 0x000000 align: LEFT_MID - id: button_device_2 width: 130 height: 130 pad_all: 0 translate_x: 160 bg_color: 0x000000 border_color: 0x000000 align: LEFT_MID - id: button_device_3 width: 130 height: 130 pad_all: 0 translate_x: 290 bg_color: 0x000000 border_color: 0x000000 align: LEFT_MID - id: button_device_4 width: 130 height: 130 pad_all: 0 translate_x: 420 bg_color: 0x000000 border_color: 0x000000 align: LEFT_MID - id: slider_front_cover width: 530 height: 160 translate_x: -25 bg_color: 0x9c9c79 radius: 10 align: CENTER - id: slider_back_cover width: 530 height: 160 translate_x: -25 bg_color: 0xb4b4a9 radius: 10 align: CENTER - id: slider_label_values_cover align: CENTER translate_x: -10 text_font: font_slider_value text_color: 0xffffff - id: device_status_top align: TOP_LEFT pad_left: 20 text_font: font_device_status text_color: ${text_color_secondary} - id: device_status_bottom align: BOTTOM_LEFT pad_left: 20 text_font: font_device_status text_color: 0xffffff pages: - id: page_home bg_color: ${background_color} widgets: - image: align: LEFT_MID src: logo - label: align: TOP_LEFT id: lbl_greeting text: "${greeting}" text_color: ${text_color_secondary} text_font: font_greeting pad_top: 20 pad_left: 150 - label: align: TOP_RIGHT id: lbl_clock text: "00:00" text_color: ${text_color} text_font: font_clock pad_right: 20 - label: align: TOP_RIGHT id: lbl_date text: "00/00/00" text_color: ${text_color_secondary} text_font: font_date pad_top: 45 pad_right: 20 - label: align: CENTER text_font: font_home_icons text_color: 0xe6d754 text: "\U0000e430" pad_top: 40 pad_right: 220 - label: id: lbl_weather_temperature align: CENTER text_font: font_home_info text_color: ${text_color} text: "20 °C" pad_top: 40 pad_right: 110 - label: align: CENTER text_font: font_home_icons text_color: 0xe65476 text: "\U0000e1ff" pad_top: 40 - label: id: lbl_home_temperature align: CENTER text_font: font_home_info text_color: ${text_color} text: "20 °C" pad_top: 40 pad_left: 100 - label: align: CENTER text_font: font_home_icons text_color: 0x54c9e6 text: "\U0000f87e" pad_top: 40 pad_left: 220 - label: id: lbl_home_humidity align: CENTER text_font: font_home_info text_color: ${text_color} text: "50%" pad_top: 40 pad_left: 320 - obj: styles: button_menu on_press: then: lvgl.page.show: page_devices widgets: - label: text_font: font_home_icons text_color: ${text_color_secondary} text: "\U0000e58c" - id: page_devices bg_color: ${background_color} widgets: - obj: styles: button_device_1 widgets: - label: id: lbl_device_light_ceiling text_font: font_device_icons text_color: ${text_color_secondary} text: "\U0000f02a" on_press: then: - homeassistant.action: service: light.toggle data: entity_id: ${light_ceiling} - obj: styles: button_device_2 widgets: - label: id: lbl_device_cover text_font: font_device_icons text_color: ${text_color_secondary} text: "\U0000ec12" on_press: then: lvgl.page.show: page_cover - obj: styles: button_device_3 widgets: - label: id: lbl_device_climate text_font: font_device_icons text_color: ${text_color_secondary} text: "\U0000ef55" on_press: then: - homeassistant.action: service: climate.toggle data: entity_id: ${climate} - obj: styles: button_device_4 widgets: - image: id: lbl_device_vacuum src: icon_vacuum image_recolor: ${text_color_secondary} image_recolor_opa: COVER pad_top: 12 on_press: then: lvgl.page.show: page_vacuum - obj: styles: button_more on_press: then: lvgl.page.show: page_devices_2 widgets: - label: text_font: font_home_icons text_color: ${text_color_secondary} text: "\U0000e548" - obj: styles: button_menu on_press: then: lvgl.page.show: page_home widgets: - label: text_font: font_home_icons text_color: ${text_color_secondary} text: "\U0000e879" - id: page_devices_2 bg_color: ${background_color} widgets: # - obj: # styles: button_device_1 # widgets: # - image: # id: lbl_device_printer_3d # src: icon_3dprinter # image_recolor: ${text_color_secondary} # image_recolor_opa: COVER # pad_top: 12 # on_press: # then: # lvgl.page.show: page_3dprinter # - obj: # styles: button_device_2 # widgets: # - label: # id: lbl_device_fan_ceiling # text_font: font_device_icons # text_color: ${text_color_secondary} # text: "\U0000f168" # on_press: # then: # - homeassistant.action: # service: fan.toggle # data: # entity_id: ${fan_ceiling} - obj: styles: button_device_3 widgets: - image: id: lbl_device_light_desk src: icon_ledstrip image_recolor: ${text_color_secondary} image_recolor_opa: COVER pad_top: 12 on_press: then: - homeassistant.action: service: light.toggle data: entity_id: ${light_desk} # - obj: # styles: button_device_4 # widgets: # - label: # id: lbl_device_dehumidifier # text_font: font_device_icons # text_color: ${text_color_secondary} # text: "\U0000e97e" # on_press: # then: # - homeassistant.action: # service: humidifier.toggle # data: # entity_id: ${dehumidifier} - obj: styles: button_more on_press: then: lvgl.page.show: page_devices widgets: - label: text_font: font_home_icons text_color: ${text_color_secondary} text: "\U0000e548" - obj: styles: button_menu on_press: then: lvgl.page.show: page_home widgets: - label: text_font: font_home_icons text_color: ${text_color_secondary} text: "\U0000e879" - id: page_cover bg_color: ${background_color} widgets: - slider: styles: slider_back_cover id: slider_cover indicator: styles: slider_front_cover knob: opa: 0 min_value: 0 max_value: 100 on_release: - homeassistant.action: action: cover.set_cover_position data: entity_id: ${cover} position: !lambda return int(x); - label: styles: slider_label_values_cover id: slider_cover_value text: "0 %" - label: text_font: font_home_icons text_color: 0xFFFFFF text: "\U0000ec11" align: LEFT_MID pad_left: 50 - label: text_font: font_home_icons text_color: 0xFFFFFF text: "\U0000ec12" align: RIGHT_MID pad_right: 110 - obj: styles: button_more on_press: then: lvgl.page.show: page_devices widgets: - label: text_font: font_home_icons text_color: ${text_color_secondary} text: "\U0000e548" - obj: styles: button_menu on_press: then: lvgl.page.show: page_home widgets: - label: text_font: font_home_icons text_color: ${text_color_secondary} text: "\U0000e879" - id: page_vacuum bg_color: ${background_color} widgets: - obj: styles: button_device_1 widgets: - label: text_font: font_device_icons text_color: ${text_color_secondary} text: "\U0000e1c4" # play on_press: then: - homeassistant.action: service: vacuum.start data: entity_id: ${vacuum} - obj: styles: button_device_2 widgets: - label: text_font: font_device_icons text_color: ${text_color_secondary} text: "\U0000e1a2" # pause on_press: then: - homeassistant.action: service: vacuum.pause data: entity_id: ${vacuum} - obj: styles: button_device_3 widgets: - label: text_font: font_device_icons text_color: ${text_color_secondary} text: "\U0000eaaa" # base on_press: then: - homeassistant.action: service: vacuum.return_to_base data: entity_id: ${vacuum} - obj: styles: button_device_4 widgets: - label: text_font: font_device_icons text_color: ${text_color_secondary} text: "\U0000eee1" # locate on_press: then: - homeassistant.action: service: vacuum.locate data: entity_id: ${vacuum} - obj: styles: button_more on_press: then: lvgl.page.show: page_devices widgets: - label: text_font: font_home_icons text_color: ${text_color_secondary} text: "\U0000e548" - obj: styles: button_menu on_press: then: lvgl.page.show: page_home widgets: - label: text_font: font_home_icons text_color: ${text_color_secondary} text: "\U0000e879" # - id: page_3dprinter # bg_color: ${background_color} # widgets: # - obj: # styles: button_device_1 # widgets: # - label: # id: lbl_device_printer_3d_state # text_font: font_device_icons # text_color: ${text_color_secondary} # text: "\U0000f418" # power # on_press: # then: # - homeassistant.action: # service: switch.toggle # data: # entity_id: ${printer_3d} # - obj: # styles: button_device_2 # widgets: # - label: # text_font: font_device_icons # text_color: ${text_color_secondary} # text: "\U0000e1a2" # pause # on_press: # then: # - homeassistant.action: # action: button.press # data: # entity_id: ${printer_3d_pause_button} # - obj: # styles: button_device_3 # widgets: # - label: # text_font: font_device_icons # text_color: ${text_color_secondary} # text: "\U0000ef71" # stop # on_press: # then: # - homeassistant.action: # action: button.press # data: # entity_id: ${printer_3d_stop_button} # - obj: # styles: button_device_4 # widgets: # - label: # id: lbl_device_printer_3d_spotlight # text_font: font_device_icons # text_color: ${text_color_secondary} # text: "\U0000ebfe" # spotlight # on_press: # then: # - homeassistant.action: # service: switch.toggle # data: # entity_id: ${printer_3d_spotlight} # - label: # styles: device_status_top # id: lbl_device_printer_3d_octoprint_status # text: "Octoprint (Offline)" # - label: # styles: device_status_bottom # id: lbl_device_printer_3d_octoprint_progress # text: " " - obj: styles: button_more on_press: then: lvgl.page.show: page_devices widgets: - label: text_font: font_home_icons text_color: ${text_color_secondary} text: "\U0000e548" - obj: styles: button_menu on_press: then: lvgl.page.show: page_home widgets: - label: text_font: font_home_icons text_color: ${text_color_secondary} text: "\U0000e879"