I love it - my method is to put a yellow sticky note on the switches with “magic enabled” written on it. Works, but your solution is far more elegant.
Fully understand the whole pushing the thing out before calling it a day - I did the same thing when initially putting the original post together ie did a big burst of trying to nut out the screen and then when I finally got it to work I had to put it aside for a while. But huge thank you for getting the touch code working - I agree it does pretty much everything we need. It would be nice to have the ability to identify where on the screen it’s being touched, but it’s so damn small the only use case might be for dimming the lights , changing music volume, or temperature settings - but all of those can be done with swipes as well. Anyhoo, found a few more spare minutes and have added light on/off rather than toggle.
# V0.3 - 10/10/2023
# Compiled and tested on esphome 2023.9.3
substitutions:
devicename: wallwatch01
friendname: WallWatch01
location: master
board: esp32-c3-devkitm-1
#GPIO pins for the LCD screen
repin: GPIO1
dcpin: GPIO2
bkpin: GPIO3
clpin: GPIO6
mopin: GPIO7
cspin: GPIO10
# GPIO pins for the touch screen
sdapin: GPIO4
sclpin: GPIO5
intpin: GPIO8
esphome:
name: $devicename
friendly_name: $friendname
esp32:
board: $board
framework:
type: arduino
# Enable logging
# Change to avoid "Components should block for at most 20-30ms" warning messages in the log - an issue since 2023.7.0
# Not really a breaking change - it's an issue I suspect due to the device being slow and this error previously
# simply not being reported
logger:
level: DEBUG
logs:
component: ERROR
# Enable Home Assistant API
api:
encryption:
key: !secret esphome_encryption_key
ota:
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: "Wallwatch01 Fallback Hotspot"
password: !secret fallback_password
captive_portal:
time:
- platform: homeassistant
timezone: "Australia/Melbourne"
id: esptime
# Test - see if the thing picks up BLE info
#esp32_ble_tracker:
#nope - locks up
#SpokeTooSoon - actually does work, kinda. Slows down the startup significantly then claims to scan but nothing is found
#Either is not supported or antenna is really weak. Suspect both.
sensor:
- platform: uptime
name: "$devicename Uptime"
- platform: wifi_signal
name: "$devicename WiFi Signal"
update_interval: 60s
- platform: homeassistant
id: outdoor_temperature
entity_id: sensor.gw1000_v1_7_6_outdoor_temperature
- platform: homeassistant
id: max_temperature
entity_id: sensor.brighton_east_temp_max_0
- platform: homeassistant
id: min_temperature
entity_id: sensor.brighton_east_temp_min_1
# Add in the thermostat and temp sensor we want to see
- platform: homeassistant
id: thermostat_temperature
entity_id: climate.thermostat_master
attribute: temperature
- platform: homeassistant
id: room_temperature
entity_id: sensor.wall_control_06_wall06_temp
external_components:
- source: github://zagnuts/esphome-components
components: [ gc9a01 ]
refresh: 30min
- source: github://GadgetFactory/[email protected]
spi:
mosi_pin: $mopin
clk_pin: $clpin
# Don't use software - makes it crawl
# force_sw: True
#mosi = Master Out Slave In
#miso = Master In Slave Out or fermented bean paste. In this case, most likely the former rather than the latter. Doesn't matter as not used.
i2c:
sda: $sdapin
scl: $sclpin
#Following helped someone get rid of error messages but didn't for me
#frequency: 400kHz
number:
# Create some virtual sensors - use automations to perform actions based on the state of these
# This is to adjust the virtual thermostat
- platform: template
optimistic: true
name: "$devicename thermostat"
id: thermostat_adjust
min_value: 18
max_value: 35
initial_value: 20
update_interval: 1s
step: 1
# This is a binary off or on to toggle the virtual thermostat states between off or heat or cool
# Where 0 is off and 1 is heat and 2 is cool
- platform: template
optimistic: true
name: "$devicename therm state"
id: thermostat_state
min_value: 0
max_value: 2
initial_value: 0
update_interval: 1s
step: 1
# This is a binary off or on to toggle the light off or on
- platform: template
optimistic: true
name: "$devicename light state"
id: light_state
min_value: 0
max_value: 1
initial_value: 0
update_interval: 1s
step: 1
text_sensor:
- platform: CST816S_touchscreen
id: my_touch_screen
on_value:
then:
- if:
condition:
text_sensor.state:
id: my_touch_screen
state: 'SWIPE RIGHT'
then:
- display.page.show_next: watchface
- component.update: watchface
- logger.log: "Swiped right, go to next page"
- if:
condition:
text_sensor.state:
id: my_touch_screen
state: 'SWIPE LEFT'
then:
- display.page.show_previous: watchface
- component.update: watchface
- logger.log: "Swiped left, go back a page"
- if:
condition:
and:
- text_sensor.state:
id: my_touch_screen
state: 'SINGLE CLICK'
- display.is_displaying_page: page1
then:
- logger.log: "Clicked on page1"
- if:
condition:
and:
- text_sensor.state:
id: my_touch_screen
state: 'SINGLE CLICK'
- display.is_displaying_page: page2
then:
- logger.log: "Clicked on page2"
- if:
condition:
and:
- text_sensor.state:
id: my_touch_screen
state: 'SWIPE UP'
- display.is_displaying_page: page1
then:
- logger.log: "Swiped up on page1"
#Note that this is the reverse of what you might expect
- number.decrement: thermostat_adjust
- if:
condition:
and:
- text_sensor.state:
id: my_touch_screen
state: 'SWIPE DOWN'
- display.is_displaying_page: page1
then:
- logger.log: "Swiped down on page1"
#Note that this is the reverse of what you might expect
- number.increment: thermostat_adjust
- if:
condition:
and:
- text_sensor.state:
id: my_touch_screen
state: 'SINGLE CLICK'
- display.is_displaying_page: page4
then:
- logger.log: "Single click on page4"
- number.increment: light_state
# refresh the screen
- component.update: watchface
# Have found that the 'single click' does not always register hence also using 'long press' to toggle light
- if:
condition:
and:
- text_sensor.state:
id: my_touch_screen
state: 'LONG PRESS'
- display.is_displaying_page: page4
then:
- logger.log: "Long press on page4"
- number.increment: light_state
# refresh the screen
- component.update: watchface
# States currently detected
# 'SWIPE UP' - note that this appears to be reversed
# 'SWIPE DOWN - note that this appears to be reversed
# 'SWIPE LEFT'
# 'SWIPE RIGHT'
# 'LONG PRESS'
# 'SINGLE CLICK'
# Need to turn on backlight as by default is not on
output:
- platform: ledc
pin: $bkpin
id: gpio_3_backlight_pwm
light:
- platform: monochromatic
output: gpio_3_backlight_pwm
name: "Display Backlight"
id: back_light
restore_mode: ALWAYS_ON
# The following assumes you have the named fonts in config\fonts
# If not, you can always download on the fly eg
# - file: "gfonts://Roboto"
# id: font_16
# size: 16
font:
- file: 'fonts/GoogleSans-Medium.ttf'
id: font_16
size: 16
- file: 'fonts/GoogleSans-Medium.ttf'
id: font_24
size: 24
- file: 'fonts/GoogleSans-Medium.ttf'
id: font_32
size: 32
color:
- id: my_red
red: 100%
green: 3%
blue: 5%
- id: my_green
red: 3%
green: 100%
blue: 5%
- id: my_blue
red: 3%
green: 5%
blue: 100%
# Some pretty images - not critical
# Again assumes you have them in config\icons
# Do not use large or complex images as the poor processor will struggle
animation:
- file: "icons/2051v2.gif"
id: clear_day
resize: 40x40
type: RGB565 #default is binary ie greyscale
- file: "images/lightoff.png"
id: light_off
- file: "images/lighton.png"
id: light_on
type: RGB565
# resize: 80x80
# type: RGB565
# Need the following image section currently with esphome since 2023.7.0
# Otherwise you will probably get 'id' is a required option for [0] errors when trying to compile
# I will note here a pet hate with breaking changes - I know that this is community developed code
# but still, it's a total pain when updating brings in weird issues like this and I continue to be in
# awe when the community discover work arounds that are so left field
image:
- file: "icons/2051v2.gif"
id: gifArt
display:
# - platform: ili9xxx
# model: gc9a01
# Above is for when or if this is merged into the ili9xxx platform
# Until then though, need to use an external component
- platform: gc9a01
id: watchface
reset_pin: $repin
cs_pin: $cspin
dc_pin: $dcpin
# Rotate the screen so usb socket is pointing down
rotation: 90
# Print the date on one line in blue
# Print the current time on the next line, but in a bigger font and in default white
# Print the outside temp on the next line but in green
# Surround the lot by a red circle with centre at 120, 120 and a radius of 115 because why not
# Procrastinating now on the touchscreen, so instead added a second page and cycling between two screens because again why not
pages:
- id: page1
lambda: |-
id(clear_day).next_frame();
it.image(100, 30, id(clear_day), COLOR_ON, COLOR_OFF);
it.printf(120,80, id(font_16), id(my_blue), TextAlign::CENTER, "Thermostat");
it.printf(120,120, id(font_24), TextAlign::CENTER, "Set Temp: %.1f°", id(thermostat_temperature).state);
it.printf(120, 170, id(font_32), id(my_green), TextAlign::CENTER, "Now: %.1f°", id(room_temperature).state);
it.circle(120, 120, 115, id(my_red));
- id: page2
lambda: |-
id(clear_day).next_frame();
it.image(100, 30, id(clear_day), COLOR_ON, COLOR_OFF);
it.strftime(120,80, id(font_16), id(my_blue), TextAlign::CENTER, "%A %b %d", id(esptime).now());
it.strftime(120,120, id(font_32), TextAlign::CENTER, "%I:%M %p", id(esptime).now());
it.printf(120, 170, id(font_32), id(my_green), TextAlign::CENTER, "Now: %.1f°", id(outdoor_temperature).state);
it.circle(120, 120, 115, id(my_red));
- id: page3
lambda: |-
id(clear_day).next_frame();
it.image(100, 30, id(clear_day), COLOR_ON, COLOR_OFF);
it.strftime(120,80, id(font_16), id(my_blue), TextAlign::CENTER, "%A %b %d", id(esptime).now());
it.printf(120,120, id(font_24), TextAlign::CENTER, "Today Min/Max:");
it.printf(120, 170, id(font_32), id(my_green), TextAlign::CENTER, "%.1f/%.1f°", id(min_temperature).state,id(max_temperature).state);
it.circle(120, 120, 115, id(my_blue));
- id: page4
lambda: |-
it.image(80, 80, id(light_off), COLOR_ON, COLOR_OFF);
if (id(light_state).state == 0)
it.image(80, 80, id(light_off), COLOR_ON, COLOR_OFF);
if (id(light_state).state == 1)
it.image(80, 80, id(light_on), COLOR_ON, COLOR_OFF);
it.circle(120, 120, 115, id(my_blue));
and two automations
alias: Wallwatch - light turn on
description: ""
trigger:
- platform: numeric_state
entity_id: number.wallwatch01_wallwatch01_light_state
above: 0
condition: []
action:
- service: light.turn_on
data: {}
target:
entity_id: light.hue_white_lamp_1_4
mode: single
and
alias: Wallwatch - light turn off
description: ""
trigger:
- platform: numeric_state
entity_id: number.wallwatch01_wallwatch01_light_state
below: 1
condition: []
action:
- service: light.turn_off
data: {}
target:
entity_id: light.hue_white_lamp_1_4
mode: single
…or could combine into one automation eg:
alias: Wallwatch - turn light on or off
description: ""
trigger:
- platform: state
entity_id:
- number.wallwatch01_wallwatch01_light_state
condition: []
action:
- if:
- condition: numeric_state
entity_id: number.wallwatch01_wallwatch01_light_state
above: 0
then:
- service: light.turn_on
data: {}
target:
entity_id: light.hue_white_lamp_1_4
else:
- service: light.turn_off
data: {}
target:
entity_id: light.hue_white_lamp_1_4
mode: single