So, after lot of digging in this post and espHome documentation: here’s my code.
Just a base for what i want to do, i only achieved the welcome page.
Code
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
manual_ip:
static_ip: !secret ip_m5stack_dial
gateway: !secret wifi_gtw
subnet: !secret wifi_sub
ap:
ssid: "m5stack-dial-fallback"
password: !secret wifi_ap_password
ota:
password: !secret ota_password
logger:
level: DEBUG
api:
encryption:
key: !secret api_encryption_key
esp32:
board: esp32-s3-devkitc-1
framework:
type: arduino
esphome:
name: ${name}
friendly_name: ${friendly_name}
on_boot:
then:
- light.turn_on: backlight
- delay: 3s
- display.page.show: page_01
- pcf8563.read_time: my_time
external_components:
- source: github://dgaust/esphome@gc9a01
components: [ gc9a01, ft3267 ]
refresh: 0s
<<: !include m5dial_files/colors_and_fonts.yaml
<<: !include m5dial_files/images.yaml
spi: #required by display.ili9xxx
mosi_pin: GPIO5
clk_pin: GPIO6
i2c:
- id: bus_internal #required by touchscreen.ft3267
sda: GPIO11
scl: GPIO12
scan: false
rc522_i2c: # RFiD
i2c_id: bus_internal
address: 0x28
on_tag:
then:
- rtttl.play: 'two_short:d=4,o=5,b=100:16e6,16e6'
- homeassistant.tag_scanned: !lambda 'return x;'
rtttl: # buzzer
output: rtttl_out
id: my_rtttl
output:
- id: lcdbacklight # screen backlight
platform: ledc
pin: GPIO9
min_power: 0
max_power: 1
- platform: ledc # buzzer
pin: GPIO3
id: rtttl_out
substitutions:
name: tab-m5stack-dial
friendly_name: M5Stack Dial
lux_sensor_id: sensor.ip030_mmw_aqara_living_luminance
alarm_panel_id: alarm_control_panel.alarme_maison
gate_sensor_id: binary_sensor.tplt_gate_open_too_long
windows_group_id: binary_sensor.windows_upstairs
# ======================================================================================
# ======================================================================================
# ================================================================================= time
time:
- platform: pcf8563
id: my_time
address: 0x38
update_interval: never # repeated synchronization is not necessary unless the external RTC is much more accurate than the internal clock
- platform: homeassistant
on_time_sync: # instead try to synchronize via network repeatedly ...
then:
- pcf8563.write_time: my_time # ... and update the RTC when the synchronization was successful
- logger.log: "time synced"
# ======================================================================================
# ======================================================================================
# =============================================================================== lights
light:
# ------------------------------------------------------------------ backlight
- platform: monochromatic
id: backlight
name: "Backlight"
output: lcdbacklight
default_transition_length: 500ms
# ======================================================================================
# ======================================================================================
# ============================================================================== selects
select:
- platform: template
name: Page Selector
id: page_selector
options:
- "page_off" # index 0
- "page_00" # index 1
- "page_01" # index 2
- "page_a1" # index 3
- "page_b1" # index 4
- "page_c1" # index 5
- "page_d1" # index 6
initial_option: "page_00"
optimistic: true
on_value:
- if:
condition:
- lambda: 'return id(page_selector).state == "page_off";'
then:
- light.turn_off: backlight
else:
- if:
condition:
- light.is_off: backlight
then:
- light.turn_on: backlight
- display.page.show: page_01
- if:
condition:
- lambda: 'return id(page_selector).state == "page_00";'
then:
- display.page.show: page_00
- if:
condition:
- lambda: 'return id(page_selector).state == "page_01";'
then:
- display.page.show: page_01
- if:
condition:
- lambda: 'return id(page_selector).state == "page_a1";'
then:
- display.page.show: page_a1
- if:
condition:
- lambda: 'return id(page_selector).state == "page_b1";'
then:
- display.page.show: page_b1
- if:
condition:
- lambda: 'return id(page_selector).state == "page_c1";'
then:
- display.page.show: page_c1
- if:
condition:
- lambda: 'return id(page_selector).state == "page_d1";'
then:
- display.page.show: page_d1
# ======================================================================================
# ======================================================================================
# ====================================================================== display + pages
touchscreen:
platform: ft3267
on_touch:
then:
- logger.log:
format: 'Touch ID: %d at _____________________ (%d, %d)'
args: [touch.id, touch.x, touch.y]
- if:
condition:
- light.is_off: backlight
then:
- light.turn_on: backlight
display:
- platform: ili9xxx
model: gc9a01a
reset_pin: GPIO8
id: screen
cs_pin: GPIO7
dc_pin: GPIO4
dimensions:
height: 240
width: 240
on_page_change:
- to: page_00
then:
- select.set:
id: page_selector
option: page_00
- to: page_01
then:
- select.set:
id: page_selector
option: page_01
- to: page_a1
then:
- select.set:
id: page_selector
option: page_a1
- to: page_b1
then:
- select.set:
id: page_selector
option: page_b1
- to: page_c1
then:
- select.set:
id: page_selector
option: page_c1
- to: page_d1
then:
- select.set:
id: page_selector
option: page_d1
pages:
# _____________________________________________________ startup page
- id: page_00
lambda: |-
it.image(120, 120, img_00, ImageAlign::CENTER);
# ___________________________________________________ welcome page
- id: page_01
lambda: |-
// variables =========================================================
float screenheight = it.get_height();
float screenwidth = it.get_width();
float halfscreenheight = screenheight / 2;
float halfscreenwidth = screenwidth /2;
// background draws ==================================================
it.filled_rectangle(0, 0, halfscreenwidth, halfscreenheight, pri_color);
it.filled_rectangle(halfscreenwidth, halfscreenheight, 155, 155, pri_color);
it.filled_rectangle(0, halfscreenheight, halfscreenwidth, 155, pri_color);
it.filled_rectangle(halfscreenwidth, 0, 155, halfscreenheight, pri_color);
it.line(0, halfscreenheight, screenwidth, halfscreenheight, off_color);
it.line(halfscreenwidth, 0, halfscreenwidth, screenheight, off_color);
// icons =============================================================
// alarm icon ----------------------------------------------------
if (id(alarm_text).state == "armed_away") {
it.image(65, 75, mdi_shield_lock, ImageAlign::CENTER, nova_color);
} else if (id(alarm_text).state == "armed_night") {
it.image(65, 75, mdi_shield_moon, ImageAlign::CENTER, nova_color);
} else if (id(alarm_text).state == "armed_home") {
it.image(65, 75, mdi_shield_account, ImageAlign::CENTER, blue_color);
} else if (id(alarm_text).state == "arming") {
it.image(65, 75, mdi_shield_sync, ImageAlign::CENTER, text_color);
} else if (id(alarm_text).state == "pending") {
it.image(65, 75, mdi_shield_lock_open, ImageAlign::CENTER, yellow_color);
} else if (id(alarm_text).state == "triggered") {
it.image(65, 75, mdi_shield_alert, ImageAlign::CENTER, red_color);
} else {
it.image(65, 75, mdi_shield_off, ImageAlign::CENTER, off_color);
}
// gate icon -----------------------------------------------------
if (id(gate_text).state == "closed") {
it.image(175, 75, mdi_gate_closed, ImageAlign::CENTER, text_color);
} else if (id(gate_text).state == "open") {
it.image(175, 75, mdi_gate_open, ImageAlign::CENTER, blue_color);
} else {
it.image(175, 75, mdi_gate_alert, ImageAlign::CENTER, yellow_color);
}
// thermostat icon -----------------------------------------------
it.image(65, 165, mdi_thermostat, ImageAlign::CENTER, text_color);
// other icon ----------------------------------------------------
if (id(windows_text).state == "off") {
it.image(175, 165, mdi_window_closed, ImageAlign::CENTER, text_color);
} else {
it.image(175, 165, mdi_window_open, ImageAlign::CENTER, blue_color);
}
// foreground draws ==================================================
it.filled_regular_polygon(halfscreenwidth, halfscreenheight -220, 144, EDGES_OCTAGON, VARIATION_FLAT_TOP, sec_color);
it.filled_regular_polygon(halfscreenwidth, halfscreenheight +220, 144, EDGES_OCTAGON, VARIATION_FLAT_TOP, sec_color);
it.regular_polygon(halfscreenwidth, halfscreenheight -220, 145, EDGES_OCTAGON, VARIATION_FLAT_TOP, off_color);
it.regular_polygon(halfscreenwidth, halfscreenheight +220, 145, EDGES_OCTAGON, VARIATION_FLAT_TOP, off_color);
// top text draw =====================================================
it.strftime(halfscreenwidth, 20, id(roboto_16_with_icons), orange_color, TextAlign::CENTER, "%d/%m %X", id(my_time).now());
// bottom text draw ==================================================
it.print(halfscreenwidth, screenheight -20, id(roboto_16_with_icons), orange_color, TextAlign::CENTER, "\U000F0046 ETEINDRE");
# ___________________________________________________ alarm page
- id: page_a1
lambda: |-
// variables ======================================================
float screenheight = it.get_height();
float screenwidth = it.get_width();
float halfscreenheight = screenheight / 2;
float halfscreenwidth = screenwidth /2;
// text draw ==================================================
it.print(halfscreenwidth, halfscreenheight, id(roboto_16_with_icons), orange_color, TextAlign::CENTER, "A1 PAGE");
# ___________________________________________________ gate page
- id: page_b1
lambda: |-
// variables ======================================================
float screenheight = it.get_height();
float screenwidth = it.get_width();
float halfscreenheight = screenheight / 2;
float halfscreenwidth = screenwidth /2;
// text draw ==================================================
it.print(halfscreenwidth, halfscreenheight, id(roboto_16_with_icons), orange_color, TextAlign::CENTER, "B1 PAGE");
# ___________________________________________________ thermostat page
- id: page_c1
lambda: |-
// variables ======================================================
float screenheight = it.get_height();
float screenwidth = it.get_width();
float halfscreenheight = screenheight / 2;
float halfscreenwidth = screenwidth /2;
// text draw ==================================================
it.print(halfscreenwidth, halfscreenheight, id(roboto_16_with_icons), orange_color, TextAlign::CENTER, "C1 PAGE");
# ___________________________________________________ windows page
- id: page_d1
lambda: |-
// variables ======================================================
float screenheight = it.get_height();
float screenwidth = it.get_width();
float halfscreenheight = screenheight / 2;
float halfscreenwidth = screenwidth /2;
// text draw ==================================================
it.print(halfscreenwidth, halfscreenheight, id(roboto_16_with_icons), orange_color, TextAlign::CENTER, "D1 PAGE");
# ======================================================================================
# ======================================================================================
# ============================================================================== sensors
sensor:
# ----------------------------------------------------------------- lux sensor
- platform: homeassistant
entity_id: $lux_sensor_id
name: "Lux Sensor"
id: sensor_lux
accuracy_decimals: 0
on_value:
- logger.log:
format: "$lux_sensor_id: %.0f"
args: [ 'id(sensor_lux).state' ]
- if:
condition:
- light.is_on: backlight
then:
- lambda: |-
float lux = id(sensor_lux).state;
if (lux < 10) {
id(lcdbacklight).set_level(0.1);
}
else if (lux < 40) {
id(lcdbacklight).set_level(0.3);
}
else if (lux < 80) {
id(lcdbacklight).set_level(0.5);
}
else {
id(lcdbacklight).set_level(0.7);
}
# ------------------------------------------------------------- rotary encoder
- platform: rotary_encoder
name: Rotary Encoder
id: rotaryencoder
resolution: 1
pin_a:
number: GPIO40
mode:
input: true
pullup: true
pin_b:
number: GPIO41
mode:
input: true
pullup: true
accuracy_decimals: 0
# accuracy_decimals: 1
on_clockwise:
- logger.log: "rotary_encoder : _____________________ turned_clockwise"
- if:
<<: &select_off
condition:
- lambda: 'return id(page_selector).state == "page_off";'
then:
- select.set:
id: page_selector
option: page_01
on_anticlockwise:
- logger.log: "rotary_encoder : _____________________ turned_ANTIclockwise"
- if:
<<: *select_off
# ======================================================================================
# ======================================================================================
# ======================================================================= binary sensors
binary_sensor:
# --------------------------------------------------------------------- button
- platform: gpio
pin:
number: GPIO42
inverted: true
name: M5 Button
on_press:
- logger.log: "button_pushed _____________________ button_pushed"
- if:
condition:
- lambda: 'return id(page_selector).state == "page_00";'
then:
- select.set:
id: page_selector
option: page_off
else:
- if:
condition:
- lambda: 'return id(page_selector).state == "page_01";'
then:
- select.set:
id: page_selector
option: page_00
else:
- select.set:
id: page_selector
option: page_01
# ================================================================= touch inputs
# -------------------------------------------------------------- startup
- platform: touchscreen
id: touch_00
internal: true
x_min: 0
x_max: 240
y_min: 0
y_max: 240
page_id: page_00
on_press:
- logger.log: "touch_input OVER page_00 : _____________________ startup"
on_release:
- if:
condition:
- light.is_on: backlight
then:
- display.page.show: page_01
# -------------------------------------------------------------- welcome
- platform: touchscreen
id: touch_01_tl
internal: true
x_min: 0
x_max: 118
y_min: 0
y_max: 118
page_id: page_01
on_press:
- logger.log: "touch_input OVER page_01 : _____________________ top_left"
on_release:
- if:
condition:
- light.is_on: backlight
then:
- display.page.show: page_a1
- platform: touchscreen
id: touch_01_tr
internal: true
x_min: 122
x_max: 240
y_min: 0
y_max: 118
page_id: page_01
on_press:
- logger.log: "touch_input OVER page_01 : _____________________ top_right"
on_release:
- if:
condition:
- light.is_on: backlight
then:
- display.page.show: page_b1
- platform: touchscreen
id: touch_01_bl
internal: true
x_min: 0
x_max: 118
y_min: 122
y_max: 240
page_id: page_01
on_press:
- logger.log: "touch_input OVER page_01 : _____________________ bottom_left"
on_release:
- if:
condition:
- light.is_on: backlight
then:
- display.page.show: page_c1
- platform: touchscreen
id: touch_01_br
internal: true
x_min: 122
x_max: 240
y_min: 122
y_max: 240
page_id: page_01
on_press:
- logger.log: "touch_input OVER page_01 : _____________________ bottom_right"
on_release:
- if:
condition:
- light.is_on: backlight
then:
- display.page.show: page_d1
# ======================================================================================
# ======================================================================================
# ============================================================================== numbers
number:
- platform: template
id: color_hue
initial_value: 0
min_value: 0
max_value: 359.9
step: 0.1
optimistic: True
- platform: template
id: color_saturation
initial_value: 0
min_value: 0
max_value: 100
step: 0.1
optimistic: True
- platform: template
id: dimmer_value
initial_value: 0
min_value: 0
max_value: 1
step: 0.001
optimistic: True
# ======================================================================================
# ======================================================================================
# ============================================================================== scripts
script:
- id: update_touch_script
mode: restart
parameters:
x: int
y: int
then:
- lambda: |-
// adjust center or coordinate plane to (120, 120)
float polar_x = x - 120;
float polar_y = 120 - y; // Screen y is opposite of cartesian.
// convert to polar coords
float r = sqrt(pow(polar_x, 2) + pow(polar_y, 2));
float theta = atan(polar_y / polar_x);
if (polar_x < 0) {
theta += M_PI; // Adjust atan for quadrants 2 & 3.
} else if (polar_y < 0) {
theta += 2 * M_PI; // Make quadrant 4 value positive.
}
// Update dimmer value.
float min_theta = M_PI * 310 / 180;
float max_theta = M_PI * 590 / 180;
// Normalize the relative positioning of the values.
float test_theta = theta;
if (test_theta < min_theta) {
test_theta += M_PI * 2;
}
ESP_LOGD("THETA", "%f < %f < %f", min_theta, test_theta, max_theta);
// page interactions
if (id(screen).get_active_page() == id(page_00)) {
id(color_hue).publish_state(360 - theta / M_PI * 180);
id(color_saturation).publish_state(min(r, (float)100));
}
if (id(screen).get_active_page() == id(page_01)) {
id(color_hue).publish_state(360 - theta / M_PI * 180);
id(color_saturation).publish_state(min(r, (float)100));
}
# - component.update: screen
# ======================================================================================
# ======================================================================================
# ============================================================================== text sensors
text_sensor:
- platform: homeassistant
id: alarm_text
entity_id: $alarm_panel_id
- platform: homeassistant
id: gate_text
entity_id: $gate_sensor_id
attribute: esphome_text_sensor
- platform: homeassistant
id: windows_text
entity_id: $windows_group_id
I didn’t find the way to reduce those parts with lambdas… Any guess?
Select component
on_value:
- if:
condition:
- lambda: 'return id(page_selector).state == "page_off";'
then:
- light.turn_off: backlight
else:
- if:
condition:
- light.is_off: backlight
then:
- light.turn_on: backlight
- display.page.show: page_01
- if:
condition:
- lambda: 'return id(page_selector).state == "page_00";'
then:
- display.page.show: page_00
- if:
condition:
- lambda: 'return id(page_selector)....... etc etc etc
Display component:
on_page_change:
- to: page_00
then:
- select.set:
id: page_selector
option: page_00
- to: page_01
then:
- select.set:
id: page_selector
option: page_01
- to: page_a1....... etc etc etc
