2026-02-28 15:47:57 -05:00
|
|
|
substitutions:
|
2026-02-28 20:47:58 -05:00
|
|
|
name: mbr-ha-remote
|
|
|
|
|
friendly_name: "MBR HA Remote"
|
2026-02-28 15:47:57 -05:00
|
|
|
|
|
|
|
|
# Home Assistant entity IDs - UPDATE THESE TO MATCH YOUR SETUP
|
2026-02-28 20:47:58 -05:00
|
|
|
light_1_entity: switch.pollys_light
|
|
|
|
|
light_2_entity: switch.joshuas_light
|
|
|
|
|
fan_entity: switch.parents_ceiling_fan
|
2026-02-28 15:47:57 -05:00
|
|
|
|
|
|
|
|
esphome:
|
|
|
|
|
name: ${name}
|
|
|
|
|
friendly_name: ${friendly_name}
|
2026-02-28 20:57:44 -05:00
|
|
|
on_boot:
|
|
|
|
|
priority: 600
|
|
|
|
|
then:
|
|
|
|
|
- output.turn_on: backlight_output
|
|
|
|
|
- output.set_level:
|
|
|
|
|
id: backlight_output
|
|
|
|
|
level: 100%
|
2026-02-28 15:47:57 -05:00
|
|
|
|
|
|
|
|
esp32:
|
|
|
|
|
board: esp32dev
|
|
|
|
|
framework:
|
2026-02-28 20:57:44 -05:00
|
|
|
type: esp-idf
|
2026-02-28 15:47:57 -05:00
|
|
|
|
|
|
|
|
logger:
|
|
|
|
|
|
|
|
|
|
api:
|
|
|
|
|
encryption:
|
|
|
|
|
key: !secret api_encryption_key
|
|
|
|
|
|
|
|
|
|
ota:
|
2026-02-28 19:48:28 -05:00
|
|
|
platform: esphome
|
|
|
|
|
password: !secret ota_password
|
2026-02-28 15:47:57 -05:00
|
|
|
|
|
|
|
|
wifi:
|
2026-02-28 19:52:21 -05:00
|
|
|
ssid: !secret wifi_ssid
|
2026-02-28 15:47:57 -05:00
|
|
|
password: !secret wifi_password
|
|
|
|
|
ap:
|
|
|
|
|
ssid: "${name} Fallback"
|
2026-02-28 20:34:08 -05:00
|
|
|
password: !secret wifi_password
|
2026-02-28 20:47:58 -05:00
|
|
|
|
2026-02-28 15:47:57 -05:00
|
|
|
captive_portal:
|
|
|
|
|
|
|
|
|
|
# SPI for display and touchscreen
|
|
|
|
|
spi:
|
2026-02-28 19:48:28 -05:00
|
|
|
clk_pin: GPIO14
|
|
|
|
|
mosi_pin: GPIO13
|
|
|
|
|
miso_pin: GPIO12
|
2026-02-28 15:47:57 -05:00
|
|
|
|
|
|
|
|
# ILI9341 Display (2.8" 320x240)
|
|
|
|
|
display:
|
|
|
|
|
- platform: ili9xxx
|
|
|
|
|
model: ili9341
|
|
|
|
|
cs_pin: GPIO15
|
|
|
|
|
dc_pin: GPIO2
|
2026-02-28 20:57:44 -05:00
|
|
|
data_rate: 40MHz
|
2026-02-28 15:47:57 -05:00
|
|
|
rotation: 0
|
2026-02-28 19:52:21 -05:00
|
|
|
invert_colors: false
|
2026-02-28 15:47:57 -05:00
|
|
|
id: my_display
|
|
|
|
|
lambda: |-
|
2026-02-28 19:48:28 -05:00
|
|
|
it.fill(Color(0x1A1A2E));
|
2026-02-28 15:47:57 -05:00
|
|
|
|
2026-02-28 19:48:28 -05:00
|
|
|
it.print(160, 20, id(title_font), Color(0xFFFFFF), TextAlign::TOP_CENTER, "Room Remote");
|
2026-02-28 15:47:57 -05:00
|
|
|
|
|
|
|
|
// Button 1: All Toggle (top-left)
|
2026-02-28 19:48:28 -05:00
|
|
|
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, "ALL");
|
2026-02-28 15:47:57 -05:00
|
|
|
|
|
|
|
|
// Button 2: Light 1 (top-right)
|
2026-02-28 19:48:28 -05:00
|
|
|
if (id(light1_state)) {
|
|
|
|
|
it.filled_rectangle(170, 50, 130, 80, Color(0xFFC107));
|
|
|
|
|
} else {
|
|
|
|
|
it.filled_rectangle(170, 50, 130, 80, Color(0x424242));
|
|
|
|
|
}
|
|
|
|
|
it.print(235, 90, id(button_font), Color(0xFFFFFF), TextAlign::CENTER, "Light 1");
|
2026-02-28 15:47:57 -05:00
|
|
|
|
|
|
|
|
// Button 3: Light 2 (bottom-left)
|
2026-02-28 19:48:28 -05:00
|
|
|
if (id(light2_state)) {
|
|
|
|
|
it.filled_rectangle(20, 150, 130, 80, Color(0xFFC107));
|
|
|
|
|
} else {
|
|
|
|
|
it.filled_rectangle(20, 150, 130, 80, Color(0x424242));
|
|
|
|
|
}
|
|
|
|
|
it.print(85, 190, id(button_font), Color(0xFFFFFF), TextAlign::CENTER, "Light 2");
|
2026-02-28 15:47:57 -05:00
|
|
|
|
|
|
|
|
// Button 4: Fan (bottom-right)
|
2026-02-28 19:48:28 -05:00
|
|
|
if (id(fan_state)) {
|
|
|
|
|
it.filled_rectangle(170, 150, 130, 80, Color(0x2196F3));
|
|
|
|
|
} else {
|
|
|
|
|
it.filled_rectangle(170, 150, 130, 80, Color(0x424242));
|
|
|
|
|
}
|
|
|
|
|
it.print(235, 190, id(button_font), Color(0xFFFFFF), TextAlign::CENTER, "Fan");
|
2026-02-28 15:47:57 -05:00
|
|
|
|
|
|
|
|
# XPT2046 Touchscreen
|
|
|
|
|
touchscreen:
|
|
|
|
|
- platform: xpt2046
|
|
|
|
|
cs_pin: GPIO33
|
|
|
|
|
interrupt_pin: GPIO36
|
2026-02-28 20:37:56 -05:00
|
|
|
calibration:
|
|
|
|
|
x_min: 280
|
|
|
|
|
x_max: 3860
|
|
|
|
|
y_min: 340
|
|
|
|
|
y_max: 3860
|
2026-02-28 15:47:57 -05:00
|
|
|
on_touch:
|
|
|
|
|
- lambda: |-
|
2026-02-28 19:48:28 -05:00
|
|
|
int tx = touch.x;
|
|
|
|
|
int ty = touch.y;
|
|
|
|
|
ESP_LOGD("touch", "Touch at x=%d, y=%d", tx, ty);
|
2026-02-28 15:47:57 -05:00
|
|
|
|
2026-02-28 19:48:28 -05:00
|
|
|
if (tx >= 20 && tx <= 150 && ty >= 50 && ty <= 130) {
|
|
|
|
|
id(touch_all) = true;
|
2026-02-28 15:47:57 -05:00
|
|
|
}
|
2026-02-28 19:48:28 -05:00
|
|
|
else if (tx >= 170 && tx <= 300 && ty >= 50 && ty <= 130) {
|
|
|
|
|
id(touch_light1) = true;
|
2026-02-28 15:47:57 -05:00
|
|
|
}
|
2026-02-28 19:48:28 -05:00
|
|
|
else if (tx >= 20 && tx <= 150 && ty >= 150 && ty <= 230) {
|
|
|
|
|
id(touch_light2) = true;
|
2026-02-28 15:47:57 -05:00
|
|
|
}
|
2026-02-28 19:48:28 -05:00
|
|
|
else if (tx >= 170 && tx <= 300 && ty >= 150 && ty <= 230) {
|
|
|
|
|
id(touch_fan) = true;
|
2026-02-28 15:47:57 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Backlight control
|
|
|
|
|
output:
|
|
|
|
|
- platform: ledc
|
|
|
|
|
pin: GPIO21
|
2026-02-28 20:57:44 -05:00
|
|
|
id: backlight_output
|
2026-02-28 15:47:57 -05:00
|
|
|
|
|
|
|
|
light:
|
|
|
|
|
- platform: monochromatic
|
2026-02-28 20:57:44 -05:00
|
|
|
output: backlight_output
|
2026-02-28 15:47:57 -05:00
|
|
|
name: "Display Backlight"
|
|
|
|
|
id: backlight
|
|
|
|
|
restore_mode: ALWAYS_ON
|
|
|
|
|
|
|
|
|
|
# Fonts
|
|
|
|
|
font:
|
|
|
|
|
- file: "gfonts://Roboto"
|
|
|
|
|
id: title_font
|
|
|
|
|
size: 24
|
|
|
|
|
- file: "gfonts://Roboto"
|
|
|
|
|
id: button_font
|
|
|
|
|
size: 20
|
|
|
|
|
|
|
|
|
|
# Global state tracking
|
|
|
|
|
globals:
|
|
|
|
|
- id: all_state
|
|
|
|
|
type: bool
|
2026-02-28 19:48:28 -05:00
|
|
|
initial_value: "false"
|
2026-02-28 15:47:57 -05:00
|
|
|
- id: light1_state
|
|
|
|
|
type: bool
|
2026-02-28 19:48:28 -05:00
|
|
|
initial_value: "false"
|
2026-02-28 15:47:57 -05:00
|
|
|
- id: light2_state
|
|
|
|
|
type: bool
|
2026-02-28 19:48:28 -05:00
|
|
|
initial_value: "false"
|
2026-02-28 15:47:57 -05:00
|
|
|
- id: fan_state
|
|
|
|
|
type: bool
|
2026-02-28 19:48:28 -05:00
|
|
|
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"
|
2026-02-28 15:47:57 -05:00
|
|
|
|
2026-02-28 19:48:28 -05:00
|
|
|
# Process touch events
|
|
|
|
|
interval:
|
|
|
|
|
- interval: 100ms
|
|
|
|
|
then:
|
|
|
|
|
- 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}
|
|
|
|
|
- if:
|
|
|
|
|
condition:
|
|
|
|
|
lambda: 'return id(touch_light1);'
|
|
|
|
|
then:
|
|
|
|
|
- lambda: 'id(touch_light1) = false;'
|
|
|
|
|
- homeassistant.service:
|
|
|
|
|
service: switch.toggle
|
|
|
|
|
data:
|
|
|
|
|
entity_id: ${light_1_entity}
|
|
|
|
|
- if:
|
|
|
|
|
condition:
|
|
|
|
|
lambda: 'return id(touch_light2);'
|
|
|
|
|
then:
|
|
|
|
|
- lambda: 'id(touch_light2) = false;'
|
|
|
|
|
- homeassistant.service:
|
|
|
|
|
service: switch.toggle
|
|
|
|
|
data:
|
|
|
|
|
entity_id: ${light_2_entity}
|
|
|
|
|
- if:
|
|
|
|
|
condition:
|
|
|
|
|
lambda: 'return id(touch_fan);'
|
|
|
|
|
then:
|
|
|
|
|
- lambda: 'id(touch_fan) = false;'
|
|
|
|
|
- homeassistant.service:
|
|
|
|
|
service: switch.toggle
|
|
|
|
|
data:
|
|
|
|
|
entity_id: ${fan_entity}
|
2026-02-28 15:47:57 -05:00
|
|
|
|
|
|
|
|
# Import states from Home Assistant
|
|
|
|
|
text_sensor:
|
|
|
|
|
- platform: homeassistant
|
|
|
|
|
entity_id: ${light_1_entity}
|
|
|
|
|
id: ha_light1_state
|
|
|
|
|
on_value:
|
|
|
|
|
then:
|
2026-02-28 19:48:28 -05:00
|
|
|
- lambda: 'id(light1_state) = (x == "on");'
|
|
|
|
|
- lambda: 'id(all_state) = id(light1_state) && id(light2_state) && id(fan_state);'
|
2026-02-28 15:47:57 -05:00
|
|
|
|
|
|
|
|
- platform: homeassistant
|
|
|
|
|
entity_id: ${light_2_entity}
|
|
|
|
|
id: ha_light2_state
|
|
|
|
|
on_value:
|
|
|
|
|
then:
|
2026-02-28 19:48:28 -05:00
|
|
|
- lambda: 'id(light2_state) = (x == "on");'
|
|
|
|
|
- lambda: 'id(all_state) = id(light1_state) && id(light2_state) && id(fan_state);'
|
2026-02-28 15:47:57 -05:00
|
|
|
|
|
|
|
|
- platform: homeassistant
|
|
|
|
|
entity_id: ${fan_entity}
|
|
|
|
|
id: ha_fan_state
|
|
|
|
|
on_value:
|
|
|
|
|
then:
|
2026-02-28 19:48:28 -05:00
|
|
|
- lambda: 'id(fan_state) = (x == "on");'
|
2026-02-28 20:47:58 -05:00
|
|
|
- lambda: 'id(all_state) = id(light1_state) && id(light2_state) && id(fan_state);'
|