mirror of
https://github.com/dlitz/esphome-configs-dlitz.git
synced 2026-04-15 22:03:26 -06:00
651 lines
16 KiB
YAML
651 lines
16 KiB
YAML
substitutions:
|
|
calibration_touchscreen_x_min: 0
|
|
calibration_touchscreen_y_min: 0
|
|
calibration_touchscreen_x_max: 169
|
|
calibration_touchscreen_y_max: 319
|
|
calibration_pulse_power_meter: 0.07874192291723468
|
|
|
|
dashboard_import:
|
|
package_import_url: github://dlitz/esphome-configs-dlitz/lanbon-l9-hf.yaml@${git_branch}
|
|
|
|
packages:
|
|
- !include common/esp32s3.yaml
|
|
- !include common/cpu-temperature.yaml
|
|
- !include common/factory-reset.yaml
|
|
- !include common/flash-write-interval.yaml
|
|
- !include common/git-branch.yaml
|
|
- !include common/ota.yaml
|
|
- !include common/ota-update-password.yaml
|
|
- !include common/restart.yaml
|
|
- !include common/safe-mode.yaml
|
|
- !include common/time.yaml
|
|
- !include common/uptime-info.yaml
|
|
- !include common/wifi-info.yaml
|
|
|
|
esp32:
|
|
cpu_frequency: 240MHz
|
|
flash_size: 16MB
|
|
|
|
esphome:
|
|
name: lanbon-l9-hf
|
|
friendly_name: Lanbon L9-HF
|
|
comment: "Lanbon L9-HF smart switch/ceiling fan speed controller"
|
|
name_add_mac_suffix: true
|
|
project:
|
|
name: "dlitz.lanbon-l9-hf"
|
|
version: "v0.0.0"
|
|
|
|
on_boot:
|
|
# Fan initialization
|
|
priority: 800
|
|
then:
|
|
- script.execute: fan_uart_init
|
|
|
|
psram:
|
|
speed: 80MHz
|
|
mode: quad
|
|
|
|
# Enable logging
|
|
logger:
|
|
# level: DEBUG
|
|
# baud_rate: 115200
|
|
hardware_uart: uart0
|
|
|
|
# Enable Home Assistant API with dynamic key
|
|
api:
|
|
encryption:
|
|
|
|
on_client_connected:
|
|
- lvgl.label.update:
|
|
id: label_apistatus
|
|
text_color: 0x00FF00
|
|
|
|
on_client_disconnected:
|
|
- lvgl.label.update:
|
|
id: label_apistatus
|
|
text_color: 0xFF0000
|
|
|
|
wifi:
|
|
# ssid: !secret wifi_ssid
|
|
# password: !secret wifi_password
|
|
# fast_connect: true
|
|
min_auth_mode: WPA2
|
|
enable_btm: true
|
|
enable_rrm: true
|
|
|
|
# Enable fallback hotspot (captive portal) in case wifi connection fails
|
|
ap:
|
|
password: !secret default_ap_password
|
|
|
|
captive_portal:
|
|
|
|
network:
|
|
enable_ipv6: true
|
|
# min_ipv6_addr_count: 2
|
|
|
|
esp32_improv:
|
|
authorizer: authorize_improv_over_ble_binary_sensor
|
|
|
|
esp32_ble_tracker:
|
|
scan_parameters:
|
|
active: false
|
|
|
|
bluetooth_proxy:
|
|
active: false
|
|
|
|
time:
|
|
- id: !extend ${time_source}
|
|
on_time:
|
|
- hours: 23,4
|
|
minutes: 5
|
|
seconds: 0
|
|
then:
|
|
- switch.turn_on: switch_antiburn
|
|
- hours: 23,4
|
|
minutes: 35
|
|
seconds: 0
|
|
then:
|
|
- switch.turn_off: switch_antiburn
|
|
- seconds: 0
|
|
minutes: '*'
|
|
then:
|
|
- lvgl.label.update:
|
|
id: label_time
|
|
text:
|
|
time_format: "%l:%M %p"
|
|
time: ${time_source}
|
|
|
|
uart:
|
|
- id: fan_uart
|
|
tx_pin: GPIO38
|
|
baud_rate: 115200
|
|
debug:
|
|
direction: TX
|
|
after:
|
|
bytes: 4
|
|
|
|
script:
|
|
- id: fan_uart_command
|
|
parameters:
|
|
cmd: int
|
|
arg: int
|
|
then:
|
|
- lambda: |-
|
|
uint8_t buf[4] = { 0xef, uint8_t(cmd), uint8_t(arg), 0};
|
|
buf[3] = buf[0] ^ buf[1] ^ buf[2]; // checksum
|
|
id(fan_uart).write_array(buf, sizeof(buf));
|
|
|
|
- id: fan_uart_init
|
|
then:
|
|
- script.execute:
|
|
id: fan_uart_command
|
|
cmd: 0x01
|
|
arg: 0x4e
|
|
|
|
- id: fan_uart_set_speed
|
|
parameters:
|
|
percent: uint8_t
|
|
then:
|
|
- lambda: |-
|
|
if (percent > 100) {
|
|
ESP_LOGW("_percent", "called with too-large value %d%%", (int)percent);
|
|
percent = 100;
|
|
}
|
|
id(fan_uart_command)->execute(0x02, percent);
|
|
|
|
output:
|
|
- platform: ledc
|
|
id: lcd_backlight_output
|
|
pin:
|
|
number: GPIO42
|
|
inverted: false
|
|
|
|
- platform: gpio
|
|
id: relay_output
|
|
pin:
|
|
number: GPIO36
|
|
|
|
- platform: template
|
|
id: fan_output
|
|
type: float
|
|
write_action:
|
|
- lambda: |-
|
|
uint8_t speed = ::round(state);
|
|
const uint8_t speed_percentages[4] = { 0, 20, 55, 100 };
|
|
if (speed < 4) {
|
|
uint8_t percent = speed_percentages[speed];
|
|
id(fan_uart_set_speed)->execute(percent);
|
|
}
|
|
|
|
fan:
|
|
- id: fan_id
|
|
name: Fan
|
|
platform: speed
|
|
output: fan_output
|
|
restore_mode: RESTORE_DEFAULT_OFF
|
|
speed_count: 3
|
|
on_state:
|
|
- lvgl.widget.update:
|
|
id: fan_speed_0_button
|
|
state:
|
|
checked: !lambda 'return !x->state && id(highlight_fan_off_button).state;'
|
|
- lvgl.widget.update:
|
|
id: fan_speed_1_button
|
|
state:
|
|
checked: !lambda 'return x->state && x->speed == 1;'
|
|
- lvgl.widget.update:
|
|
id: fan_speed_2_button
|
|
state:
|
|
checked: !lambda 'return x->state && x->speed == 2;'
|
|
- lvgl.widget.update:
|
|
id: fan_speed_3_button
|
|
state:
|
|
checked: !lambda 'return x->state && x->speed == 3;'
|
|
|
|
binary_sensor:
|
|
- platform: gpio
|
|
name: Proximity Sensor
|
|
id: proximity_sensor
|
|
pin: GPIO11
|
|
on_press:
|
|
then:
|
|
- if:
|
|
condition: lvgl.is_paused
|
|
then:
|
|
- logger.log: "LVGL resuming due to proximity sensor."
|
|
- lvgl.resume:
|
|
- lvgl.widget.redraw:
|
|
- light.turn_on: display_backlight
|
|
|
|
- platform: template
|
|
id: lvgl_paused_sensor_id
|
|
name: LVGL Paused
|
|
entity_category: diagnostic
|
|
condition:
|
|
lvgl.is_paused:
|
|
|
|
- platform: template
|
|
id: authorize_improv_over_ble_binary_sensor
|
|
name: Authorizing Improv-over-BLE
|
|
entity_category: diagnostic
|
|
internal: true
|
|
|
|
light:
|
|
- platform: monochromatic
|
|
id: display_backlight
|
|
name: Display Backlight
|
|
output: lcd_backlight_output
|
|
restore_mode: ALWAYS_ON
|
|
entity_category: diagnostic
|
|
|
|
spi:
|
|
type: octal
|
|
id: display_spi
|
|
clk_pin: GPIO21
|
|
data_pins:
|
|
- GPIO6
|
|
- GPIO7
|
|
- GPIO15
|
|
- GPIO16
|
|
- GPIO10
|
|
- GPIO9
|
|
- ignore_strapping_warning: true
|
|
number: GPIO46
|
|
- ignore_strapping_warning: true
|
|
number: GPIO3
|
|
|
|
display:
|
|
- platform: mipi_spi
|
|
id: display_id
|
|
model: st7789v
|
|
spi_id: display_spi
|
|
bus_mode: octal
|
|
dc_pin: GPIO17
|
|
dimensions:
|
|
height: 320
|
|
width: 170
|
|
offset_height: 0
|
|
offset_width: 35
|
|
invert_colors: true
|
|
show_test_card: false
|
|
data_rate: 4MHz
|
|
color_depth: 16BIT
|
|
color_order: BGR
|
|
auto_clear_enabled: false
|
|
update_interval: 250ms
|
|
|
|
i2c:
|
|
- id: touchscreen_i2c
|
|
sda: GPIO35
|
|
scl:
|
|
number: GPIO0
|
|
ignore_strapping_warning: true
|
|
scan: true
|
|
|
|
touchscreen:
|
|
- platform: ft63x6
|
|
id: touchscreen_id
|
|
display: display_id
|
|
i2c_id: touchscreen_i2c
|
|
calibration:
|
|
x_min: ${calibration_touchscreen_x_min}
|
|
y_min: ${calibration_touchscreen_y_min}
|
|
x_max: ${calibration_touchscreen_x_max}
|
|
y_max: ${calibration_touchscreen_y_max}
|
|
on_release:
|
|
- if:
|
|
condition: lvgl.is_paused
|
|
then:
|
|
- logger.log: "LVGL resuming due to screen touch."
|
|
- lvgl.resume:
|
|
- lvgl.widget.redraw:
|
|
- light.turn_on: display_backlight
|
|
|
|
sensor:
|
|
- platform: pulse_meter
|
|
name: Power
|
|
id: power_pulse_meter
|
|
pin: GPIO5
|
|
unit_of_measurement: 'W'
|
|
device_class: power
|
|
state_class: measurement
|
|
internal_filter_mode: PULSE
|
|
accuracy_decimals: 1
|
|
filters:
|
|
- filter_out: nan
|
|
- throttle: 15s
|
|
- multiply: ${calibration_pulse_power_meter}
|
|
total:
|
|
name: "Total Pulses" # DEBUG
|
|
|
|
- platform: total_daily_energy
|
|
id: energy
|
|
name: "Energy"
|
|
restore: false
|
|
power_id: power_pulse_meter
|
|
unit_of_measurement: "kWh"
|
|
state_class: total_increasing
|
|
device_class: energy
|
|
accuracy_decimals: 3
|
|
filters:
|
|
# Convert W to kW
|
|
- multiply: 0.001
|
|
|
|
switch:
|
|
- platform: output
|
|
name: Relay
|
|
id: relay
|
|
output: relay_output
|
|
restore_mode: RESTORE_DEFAULT_OFF
|
|
on_turn_on:
|
|
- lvgl.widget.update:
|
|
id: light_toggle_button
|
|
state:
|
|
checked: true
|
|
on_turn_off:
|
|
- lvgl.widget.update:
|
|
id: light_toggle_button
|
|
state:
|
|
checked: false
|
|
|
|
- platform: template
|
|
name: Antiburn
|
|
id: switch_antiburn
|
|
icon: mdi:television-shimmer
|
|
optimistic: true
|
|
entity_category: diagnostic
|
|
turn_on_action:
|
|
- logger.log: "Starting Antiburn"
|
|
- if:
|
|
condition: lvgl.is_paused
|
|
then:
|
|
- lvgl.resume:
|
|
- lvgl.widget.redraw:
|
|
- lvgl.pause:
|
|
show_snow: true
|
|
- light.turn_off: display_backlight
|
|
turn_off_action:
|
|
- logger.log: "Stopping Antiburn"
|
|
- if:
|
|
condition: lvgl.is_paused
|
|
then:
|
|
- lvgl.resume:
|
|
- lvgl.widget.redraw:
|
|
|
|
- platform: template
|
|
id: highlight_fan_off_button
|
|
name: Highlight Fan-Off Button
|
|
optimistic: True
|
|
entity_category: config
|
|
restore_mode: RESTORE_DEFAULT_ON
|
|
|
|
font:
|
|
- # file: "https://github.com/Pictogrammers/pictogrammers.github.io/raw/refs/heads/main/@mdi/font/7.4.47/fonts/materialdesignicons-webfont.ttf"
|
|
file: "fonts/pictogrammers/@mdi/font/7.4.47/fonts/materialdesignicons-webfont.ttf"
|
|
id: mdi_icons
|
|
bpp: 4
|
|
size: 52
|
|
glyphs:
|
|
- "\U000F0336" # mdi:lightbulb-outline
|
|
- "\U000F06E9" # mdi:lightbulb-on-outline
|
|
- "\U000F081D" # mdi:fan-off
|
|
- "\U000F1472" # mdi:fan-speed-1
|
|
- "\U000F1473" # mdi:fan-speed-2
|
|
- "\U000F1474" # mdi:fan-speed-3
|
|
|
|
- file: "gfonts://Roboto"
|
|
id: roboto_20
|
|
size: 20
|
|
|
|
lvgl:
|
|
id: lvgl_id
|
|
displays:
|
|
- display_id
|
|
touchscreens:
|
|
- touchscreen_id: touchscreen_id
|
|
long_press_repeat_time: 100ms
|
|
buffer_size: 25%
|
|
log_level: WARN
|
|
on_idle:
|
|
timeout: 30s
|
|
then:
|
|
- logger.log: "LVGL is idle"
|
|
- light.turn_off: display_backlight
|
|
- lvgl.pause:
|
|
style_definitions:
|
|
- id: header_footer
|
|
bg_color: 0x2F8CD8
|
|
bg_grad_color: 0x005782
|
|
bg_grad_dir: VER
|
|
bg_opa: COVER
|
|
border_opa: TRANSP
|
|
radius: 0
|
|
pad_all: 0
|
|
pad_row: 0
|
|
pad_column: 0
|
|
border_color: 0x0077b3
|
|
text_color: 0xFFFFFF
|
|
width: 100%
|
|
height: 30
|
|
- id: factory_reset_button_style
|
|
bg_color: 0xFF0000
|
|
top_layer:
|
|
widgets:
|
|
- obj:
|
|
align: TOP_MID
|
|
styles: header_footer
|
|
widgets:
|
|
- label:
|
|
text:
|
|
time_format: "%l:%M %p"
|
|
time: ${time_source}
|
|
id: label_time
|
|
align: CENTER
|
|
text_align: CENTER
|
|
text_color: 0xFFFFFF
|
|
- label:
|
|
text: "\uF1EB"
|
|
id: label_apistatus
|
|
align: top_right
|
|
x: -2
|
|
y: 7
|
|
text_align: right
|
|
text_color: 0xFFFFFF
|
|
- buttonmatrix:
|
|
align: bottom_mid
|
|
styles: header_footer
|
|
pad_all: 0
|
|
outline_width: 0
|
|
id: top_layer
|
|
items:
|
|
styles: header_footer
|
|
rows:
|
|
- buttons:
|
|
- id: page_prev
|
|
text: "\uF053"
|
|
on_press:
|
|
then:
|
|
lvgl.page.previous:
|
|
- id: page_home
|
|
text: "\uF015"
|
|
on_press:
|
|
then:
|
|
lvgl.page.show: page_main
|
|
- id: page_next
|
|
text: "\uF054"
|
|
on_press:
|
|
then:
|
|
lvgl.page.next:
|
|
pages:
|
|
- id: page_main
|
|
pad_all: 0
|
|
widgets:
|
|
- buttonmatrix:
|
|
id: matrix_id
|
|
width: 100%
|
|
height: 260
|
|
x: 0
|
|
y: 30
|
|
pad_top: 2
|
|
pad_bottom: 0
|
|
pad_left: 2
|
|
pad_right: 0
|
|
pad_row: 3
|
|
pad_column: 1
|
|
text_font: mdi_icons
|
|
rows:
|
|
- buttons:
|
|
- id: light_toggle_button
|
|
text: "\U000F0336" # mdi:lightbulb-outline
|
|
width: 2
|
|
control:
|
|
checkable: true
|
|
on_click:
|
|
- switch.toggle: relay
|
|
- buttons:
|
|
- id: fan_speed_0_button
|
|
text: "\U000F081D" # mdi:fan-off
|
|
control:
|
|
checkable: true
|
|
on_click:
|
|
- fan.turn_off: fan_id
|
|
- id: fan_speed_1_button
|
|
text: "\U000F1472" # mdi:fan-speed-1
|
|
control:
|
|
checkable: true
|
|
on_click:
|
|
- fan.turn_on:
|
|
id: fan_id
|
|
speed: 1
|
|
- buttons:
|
|
- id: fan_speed_2_button
|
|
text: "\U000F1473" # mdi:fan-speed-2
|
|
control:
|
|
checkable: true
|
|
on_click:
|
|
- fan.turn_on:
|
|
id: fan_id
|
|
speed: 2
|
|
- id: fan_speed_3_button
|
|
text: "\U000F1474" # mdi:fan-speed-3
|
|
control:
|
|
checkable: true
|
|
on_click:
|
|
- fan.turn_on:
|
|
id: fan_id
|
|
speed: 3
|
|
|
|
- id: page_diagnostics
|
|
widgets:
|
|
- button:
|
|
width: 164
|
|
height: 100
|
|
x: 2
|
|
y: 58
|
|
id: factory_reset_lvgl_button
|
|
styles: factory_reset_button_style
|
|
on_release:
|
|
- if:
|
|
condition:
|
|
lambda: "return id(factory_reset_lvgl_button_longpress_ms) >= 10000;"
|
|
then:
|
|
- light.turn_off: display_backlight
|
|
- button.press: button_factory_reset
|
|
else:
|
|
- lambda: |-
|
|
id(factory_reset_lvgl_button_longpress_ms) = 0;
|
|
- lvgl.style.update:
|
|
id: factory_reset_button_style
|
|
bg_color: 0xFF0000
|
|
text_color: 0xFFFFFFF
|
|
on_press:
|
|
- lambda: |-
|
|
id(factory_reset_lvgl_button_longpress_ms) = 0;
|
|
on_long_press_repeat:
|
|
- logger.log:
|
|
format: "Long press repeat %d ms"
|
|
args: ["id(factory_reset_lvgl_button_longpress_ms)"]
|
|
- lambda: |-
|
|
id(factory_reset_lvgl_button_longpress_ms) += 100;
|
|
- if:
|
|
condition:
|
|
lambda: "return id(factory_reset_lvgl_button_longpress_ms) == 10000;"
|
|
then:
|
|
- lvgl.style.update:
|
|
id: factory_reset_button_style
|
|
bg_color: 0xFFFF00
|
|
text_color: 0xFF0000
|
|
- lvgl.widget.redraw: factory_reset_lvgl_button
|
|
widgets:
|
|
- label:
|
|
id: factory_reset_lvgl_button_label
|
|
align: CENTER
|
|
text: "Hold 10s\nfor Factory Reset"
|
|
text_align: CENTER
|
|
text_font: roboto_20
|
|
|
|
- button:
|
|
width: 164
|
|
height: 100
|
|
x: 2
|
|
y: 162
|
|
id: factory_reset_lvgl_cancel_button
|
|
on_release:
|
|
- lambda: |-
|
|
id(factory_reset_lvgl_button_longpress_ms) = 0;
|
|
- lvgl.page.show: page_main
|
|
widgets:
|
|
- label:
|
|
id: factory_reset_lvgl_cancel_button_label
|
|
align: CENTER
|
|
text: "Cancel"
|
|
text_align: CENTER
|
|
text_font: roboto_20
|
|
|
|
- id: page_setup
|
|
widgets:
|
|
- button:
|
|
width: 164
|
|
height: 100
|
|
x: 2
|
|
y: 58
|
|
id: authorize_improv_over_ble_lvgl_button
|
|
on_press:
|
|
- logger.log: "Authorizing Improv-over-BLE"
|
|
- binary_sensor.template.publish:
|
|
id: authorize_improv_over_ble_binary_sensor
|
|
state: ON
|
|
on_release:
|
|
- binary_sensor.template.publish:
|
|
id: authorize_improv_over_ble_binary_sensor
|
|
state: OFF
|
|
widgets:
|
|
- label:
|
|
id: authorize_improv_over_ble_lvgl_button_label
|
|
align: CENTER
|
|
text: "Improv-over-BLE\nAuthorize"
|
|
text_align: CENTER
|
|
text_font: roboto_20
|
|
|
|
- button:
|
|
width: 164
|
|
height: 100
|
|
x: 2
|
|
y: 162
|
|
id: page_setup_lvgl_cancel_button
|
|
on_release:
|
|
- lvgl.page.show: page_main
|
|
widgets:
|
|
- label:
|
|
id: page_setup_lvgl_cancel_button_label
|
|
align: CENTER
|
|
text: "Cancel"
|
|
text_align: CENTER
|
|
text_font: roboto_20
|
|
|
|
globals:
|
|
- id: factory_reset_lvgl_button_longpress_ms
|
|
type: int
|
|
restore_value: no
|
|
initial_value: '0'
|