substitutions: name: mbr-ha-remote friendly_name: "MBR HA Remote" # Home Assistant entity IDs - UPDATE THESE TO MATCH YOUR SETUP light_1_entity: switch.pollys_light light_2_entity: switch.joshuas_light fan_entity: switch.parents_ceiling_fan esphome: name: ${name} friendly_name: ${friendly_name} on_boot: priority: -100 then: - lambda: |- id(last_interaction_ms) = millis(); id(backlight_is_on) = true; - component.update: my_display esp32: board: esp32dev framework: type: arduino logger: api: encryption: key: !secret api_encryption_key ota: platform: esphome password: !secret ota_password wifi: ssid: !secret wifi_ssid password: !secret wifi_password ap: ssid: "${name} Fallback" password: !secret wifi_password captive_portal: # SPI for display and touchscreen (CYD uses two separate SPI buses) spi: - id: tft_spi clk_pin: GPIO14 mosi_pin: GPIO13 miso_pin: GPIO12 - id: touch_spi clk_pin: GPIO25 mosi_pin: GPIO32 miso_pin: GPIO39 # ILI9341 Display (2.8" 320x240) display: - platform: ili9xxx model: ili9341 spi_id: tft_spi cs_pin: GPIO15 dc_pin: GPIO2 dimensions: width: 320 height: 240 # Use palette mode to lower display buffer memory usage. color_palette: 8BIT data_rate: 20MHz update_interval: never rotation: 0 invert_colors: false id: my_display lambda: |- it.fill(Color(0x1A1A2E)); it.print(160, 20, id(title_font), Color(0xFFFFFF), TextAlign::TOP_CENTER, "Room Remote"); // Button 1: All Toggle (top-left) if (id(all_state)) { it.filled_rectangle(20, 50, 130, 80, Color(0x4CAF50)); } else { it.filled_rectangle(20, 50, 130, 80, Color(0x424242)); } it.print(85, 90, id(button_font), Color(0xFFFFFF), TextAlign::CENTER, "Toggle All"); // Button 2: Light 1 (top-right) if (id(light1_state)) { it.filled_rectangle(170, 50, 130, 80, Color(0x4CAF50)); } else { it.filled_rectangle(170, 50, 130, 80, Color(0x424242)); } it.print(235, 90, id(button_font), Color(0xFFFFFF), TextAlign::CENTER, "Polly's Light"); // Button 3: Light 2 (bottom-left) if (id(light2_state)) { it.filled_rectangle(20, 150, 130, 80, Color(0x4CAF50)); } else { it.filled_rectangle(20, 150, 130, 80, Color(0x424242)); } it.print(85, 190, id(button_font), Color(0xFFFFFF), TextAlign::CENTER, "Joshua's Light"); // Button 4: Fan (bottom-right) if (id(fan_state)) { it.filled_rectangle(170, 150, 130, 80, Color(0x4CAF50)); } else { it.filled_rectangle(170, 150, 130, 80, Color(0x424242)); } it.print(235, 190, id(button_font), Color(0xFFFFFF), TextAlign::CENTER, "Ceiling Fan"); # XPT2046 Touchscreen touchscreen: - platform: xpt2046 spi_id: touch_spi cs_pin: GPIO33 interrupt_pin: GPIO36 calibration: x_min: 280 x_max: 3860 y_min: 340 y_max: 3860 on_touch: - lambda: |- id(last_interaction_ms) = millis(); if (!id(backlight_is_on)) { auto call = id(backlight).turn_on(); call.perform(); id(backlight_is_on) = true; } int tx = touch.x; int ty = touch.y; ESP_LOGD("touch", "Touch at x=%d, y=%d", tx, ty); if (tx >= 20 && tx <= 150 && ty >= 50 && ty <= 130) { id(touch_all) = true; } else if (tx >= 170 && tx <= 300 && ty >= 50 && ty <= 130) { id(touch_light1) = true; } else if (tx >= 20 && tx <= 150 && ty >= 150 && ty <= 230) { id(touch_light2) = true; } else if (tx >= 170 && tx <= 300 && ty >= 150 && ty <= 230) { id(touch_fan) = true; } # Backlight control output: - platform: ledc pin: GPIO21 id: backlight_output light: - platform: monochromatic output: backlight_output name: "Display Backlight" id: backlight restore_mode: ALWAYS_ON # Fonts font: - file: "gfonts://Nunito" id: title_font size: 24 - file: "gfonts://Nunito" id: button_font size: 20 # Global state tracking globals: - id: all_state type: bool initial_value: "false" - id: light1_state type: bool initial_value: "false" - id: light2_state type: bool initial_value: "false" - id: fan_state type: bool initial_value: "false" - id: touch_all type: bool initial_value: "false" - id: touch_light1 type: bool initial_value: "false" - id: touch_light2 type: bool initial_value: "false" - id: touch_fan type: bool initial_value: "false" - id: last_interaction_ms type: uint32_t initial_value: "0" - id: backlight_is_on type: bool initial_value: "true" # Process touch events interval: - interval: 100ms then: - if: condition: lambda: 'return id(backlight_is_on) && ((uint32_t)(millis() - id(last_interaction_ms)) > 30000);' then: - light.turn_off: backlight - lambda: 'id(backlight_is_on) = false;' - if: condition: lambda: 'return id(touch_all);' then: - lambda: 'id(touch_all) = false;' - homeassistant.service: service: switch.toggle data: entity_id: ${light_1_entity} - homeassistant.service: service: switch.toggle data: entity_id: ${light_2_entity} - homeassistant.service: service: switch.toggle data: entity_id: ${fan_entity} - component.update: my_display - if: condition: lambda: 'return id(touch_light1);' then: - lambda: 'id(touch_light1) = false;' - homeassistant.service: service: switch.toggle data: entity_id: ${light_1_entity} - component.update: my_display - if: condition: lambda: 'return id(touch_light2);' then: - lambda: 'id(touch_light2) = false;' - homeassistant.service: service: switch.toggle data: entity_id: ${light_2_entity} - component.update: my_display - if: condition: lambda: 'return id(touch_fan);' then: - lambda: 'id(touch_fan) = false;' - homeassistant.service: service: switch.toggle data: entity_id: ${fan_entity} - component.update: my_display # Import states from Home Assistant text_sensor: - platform: homeassistant entity_id: ${light_1_entity} id: ha_light1_state on_value: then: - lambda: 'id(light1_state) = (x == "on");' - lambda: 'id(all_state) = id(light1_state) && id(light2_state) && id(fan_state);' - component.update: my_display - platform: homeassistant entity_id: ${light_2_entity} id: ha_light2_state on_value: then: - lambda: 'id(light2_state) = (x == "on");' - lambda: 'id(all_state) = id(light1_state) && id(light2_state) && id(fan_state);' - component.update: my_display - platform: homeassistant entity_id: ${fan_entity} id: ha_fan_state on_value: then: - lambda: 'id(fan_state) = (x == "on");' - lambda: 'id(all_state) = id(light1_state) && id(light2_state) && id(fan_state);' - component.update: my_display