14 Commits
28 changed files with 1681 additions and 2702 deletions
+23
View File
@@ -0,0 +1,23 @@
# === .gitignore for Global/VisualStudioCode (https://raw.githubusercontent.com/github/gitignore/main/Global/VisualStudioCode.gitignore) ===
# Visual Studio Code
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets
!*.code-workspace
# Built Visual Studio Code Extensions
*.vsix
# === .gitignore for community/embedded/esp-idf (https://raw.githubusercontent.com/github/gitignore/main/community/embedded/esp-idf.gitignore) ===
# gitignore template for esp-idf, the official development framework for ESP32
# https://github.com/espressif/esp-idf
build/
sdkconfig
sdkconfig.old
+5
View File
@@ -0,0 +1,5 @@
{
"recommendations": [
"esphome.esphome-vscode"
]
}
+155
View File
@@ -0,0 +1,155 @@
# Base module V2
## Controller
Brand: Espressif
Type: ESP32-S3-WROOM-1-N16R8
Driver IC: ST7735S (ESPHome compatible)
[Datasheet](../Datasheets/esp32-s3-wroom-1_wroom-1u_datasheet_en.pdf)
### Pinout / IO
```
1 GND
2 3.3V
3 EN
4 IO4 Neopixel LED's
5 IO5 SPI MOSI
6 IO6 SPI SCK
7 IO7 SPI Register select
8 IO15
9 IO16
10 IO17
11 IO18
12 IO8
13 IO19
14 IO20
15 IO3 Backlight main display
16 IO46 Backlight mini displays
17 IO9
18 IO10
19 IO11
20 IO12
21 IO13
22 IO14
23 IO21
24 IO47 I2C SDA > Buttons
25 IO48 I2C SCL > Buttons
26 IO45
27 IO0
28 IO35
29 IO36
30 IO37
31 IO38
32 IO39
33 IO40
34 IO41
35 IO42
36 TXD0
37 RXD0
38 IO1 I2C SDA > Temperature & Ambient Sensor
39 IO2 I2S SCL > Temperature & Ambient Sensor
40 GND
```
## Connectors
### Connector J0
```
MCP I2C address: 0x20
PIN Description Destination
1 GND
2 +3.3V
3 Reset ESP32 #3 EN
4 I2C SDA ESP32 #24 IO47
5 SPI MOSI ESP32 #5 IO5
6 I2C SCK ESP32 #25 IO48
7 SPI SCK ESP32 #6 IO6
8 I2C Address MSP A0 GND
9 SPI Register select ESP32 #7 IO7
10 I2C Address MSP A1 GND
11 Mini Display Backlight anode ESP32 #16 IO46 (via transistor)
12 I2C Address MSP A2 GND
13 Extensionplate...
14 Connected with J1,2,3
15 Main Display Backlight anode ESP32 #15 IO3 (via transistor)
16 Neopixel Data OUT J1 #17
17 Neopixel Data IN ESP32 #4 IO4
```
### Connector J1
```
MCP I2C address: 0x21
PIN Description Destination
1 GND
2 +3.3V
3 Reset ESP32 #3 EN
4 I2C SDA ESP32 #24 IO47
5 SPI MOSI ESP32 #5 IO5
6 I2C SCK ESP32 #25 IO48
7 SPI SCK ESP32 #6 IO6
8 I2C Address MSP A0 3.3V
9 SPI Register select ESP32 #7 IO7
10 I2C Address MSP A1 GND
11 Mini Display Backlight anode ESP32 #16 IO46 (via transistor)
12 I2C Address MSP A2 GND
13 Extensionplate...
14 Connected with J0,2,3
15 Main Display Backlight anode ESP32 #15 IO3 (via transistor)
16 Neopixel Data OUT J2 #17
17 Neopixel Data IN J0 #16
```
### Connector J2
```
MCP I2C address: 0x22
PIN Description Destination
1 GND
2 +3.3V
3 Reset ESP32 #3 EN
4 I2C SDA ESP32 #24 IO47
5 SPI MOSI ESP32 #5 IO5
6 I2C SCK ESP32 #25 IO48
7 SPI SCK ESP32 #6 IO6
8 I2C Address MSP A0 GND
9 SPI Register select ESP32 #7 IO7
10 I2C Address MSP A1 3.3V
11 Mini Display Backlight anode ESP32 #16 IO46 (via transistor)
12 I2C Address MSP A2 GND
13 Extensionplate...
14 Connected with J0,1,3
15 Main Display Backlight anode ESP32 #15 IO3 (via transistor)
16 Neopixel Data OUT J3 #17
17 Neopixel Data IN J1 #16
```
### Connector J3
```
MCP I2C address: 0x23
PIN Description Destination
1 GND
2 +3.3V
3 Reset ESP32 #3 EN
4 I2C SDA ESP32 #24 IO47
5 SPI MOSI ESP32 #5 IO5
6 I2C SCK ESP32 #25 IO48
7 SPI SCL ESP32 #6 IO6
8 I2C Address MSP A0 3.3V
9 SPI Register select ESP32 #7 IO7
10 I2C Address MSP A1 3.3V
11 Mini Display Backlight anode ESP32 #16 IO46 (via transistor)
12 I2C Address MSP A2 GND
13 Extensionplate...
14 Connected with J0,1,2
15 Main Display Backlight anode ESP32 #15 IO3 (via transistor)
16 Neopixel Data OUT Extensionplate #5
17 Neopixel Data IN J2 #16
```
Binary file not shown.

After

Width:  |  Height:  |  Size: 446 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

-7
View File
@@ -1,7 +0,0 @@
# ESPHome Button+ Integration
*work in progress*
## Introduction
TODO
+14 -1
View File
@@ -1,7 +1,7 @@
# Getting Started
In order to get started with a 3 bar module, you should
1. Make sure you have [ESPHome](https://esphome.io) installed
2. Copy the file [3pages_TotalExample.yml](/examples/3pages_TotalExample.yml) to the root of the repository
2. Copy the file [3BAR_1Display.yml](/Examples/3BAR_1Display.yml) to the root of the repository
3. Create a file secrets.yaml and add the following
```
wifi_ssid: "YOUR_SSID"
@@ -12,3 +12,16 @@ wifi_password: "YOUR_PASSWORD"
7. Connect the B+ to your computer
8. Open a compatible browser such as Chrome and visit https://web.esphome.io
9. Load the downloaded file to your device and enjoy
# Generating different events / pages
If you want a different number of pages, and you want the matching events, you can generate a new [3bar_1display_events.yaml](/package/3bar_1display_events.yaml) file using python.
The python file is called [generate_events.py](/generate/generate_events.py). The top of the file has three configuration options:
```python
NR_OF_PAGES = 3
INCLUDE_DISPLAY_EXTRA = True # Enable Double, Triple, Hold for main display buttons
LED_CONFIRMATION = True # Flash LED on press
```
Set these to your preferred config before running. Then run `python generate_events.py`. Copy the output file to the package folder, and use that package in your main config. Make sure the number of pages in your main config and the generated file are the same.
+8
View File
@@ -0,0 +1,8 @@
{
"folders": [
{
"path": "."
}
],
"settings": {}
}
-178
View File
@@ -1,178 +0,0 @@
packages:
remote_package_files:
url: https://github.com/dixi83/ESPHome_ButtonPlus
files:
- 'package/1bar_1display.yaml'
ref: main
refresh: 1d
globals:
- id: !extend nr_of_pages
initial_value: '1'
font:
- file: "https://github.com/web-fonts/ttf/raw/refs/heads/master/bpg-ingiri-arial-webfont.ttf"
id: font_arial20
size: 20
bpp: 4
- file: "https://torinak.com/font/7segment.ttf"
id: font_clock
size: 64
bpp: 4
- file: "https://torinak.com/font/7segment.ttf"
id: font_date
size: 24
bpp: 4
image:
- file: mdi:lightbulb
id: lightbulb
resize: 80x80
- file: mdi:alert
id: alert
resize: 80x80
- file: mdi:chevron-right
id: icon_next_page
resize: 30x30
- file: mdi:chevron-left
id: icon_prev_page
resize: 30x30
- file: mdi:home-thermometer-outline
id: icon_temp_inside
resize: 40x40
color:
- id: red
hex: FF0000
- id: blue
hex: 0000FF
- id: yellow
hex: FFFF00
- id: green
hex: 00FF00
- id: orange
hex: FF7F00
- id: white
hex: FFFFFF
- id: grey
hex: A6A6A6
event:
- platform: template
name: BAR1 button Left event
id: bar1_btn_left_event
event_types:
- "press page 0"
- "release page 0"
- "long press page 0"
- platform: template
name: BAR1 button Right event
id: bar1_btn_right_event
event_types:
- "press page 0"
- "release page 0"
- "long press page 0"
binary_sensor:
- id: !extend main_display_btn_left
on_click:
- script.execute: prev_page
- id: !extend main_display_btn_right
on_click:
- script.execute: next_page
- id: !extend bar1_btn_left
on_click:
lambda: |-
switch (id(actual_page)){
case 0:
id(bar1_btn_left_event).trigger("press page 0");
break;
}
on_release:
lambda: |-
switch (id(actual_page)){
case 0:
id(bar1_btn_left_event).trigger("release page 0");
break;
}
on_multi_click:
- timing:
- ON for at least 2s
then:
- lambda: |-
switch (id(actual_page)){
case 0:
id(bar1_btn_left_event).trigger("long press page 0");
break;
}
- id: !extend bar1_btn_right
on_click:
lambda: |-
switch (id(actual_page)){
case 0:
id(bar1_btn_right_event).trigger("press page 0");
break;
}
on_release:
lambda: |-
switch (id(actual_page)){
case 0:
id(bar1_btn_right_event).trigger("release page 0");
break;
}
on_multi_click:
- timing:
- ON for at least 2s
then:
- lambda: |-
switch (id(actual_page)){
case 0:
id(bar1_btn_right_event).trigger("long press page 0");
break;
}
display:
#########
# MAIN ##
#########
- id: !extend main_display
# data_rate: 40MHz
lambda: |-
id(all_pages_main).execute();
switch (id(actual_page)){
case 0:
it.print(100, 160, id(font_arial20), "This is page 0!");
break;
}
script:
- id: all_pages_main
then:
- lambda: |-
//>>> prev/next icons next to butotns
id(main_display).image(0, 208, id(icon_prev_page), ImageAlign::TOP_LEFT, id(red));
id(main_display).image(320, 208, id(icon_next_page), ImageAlign::TOP_RIGHT, id(red));
//
//>>> temperature inside:
id(main_display).image(36, 15, id(icon_temp_inside), ImageAlign::TOP_CENTER, id(red));
id(main_display).printf(36, 80, id(font_arial20), id(white), TextAlign::TOP_CENTER , "%.1f°", id(inside_temperature).state);
//
//>>> print time and date
id(main_display).strftime(160, 5, id(font_clock), TextAlign::TOP_CENTER, "%H:%M", id(datetime).now());
id(main_display).strftime(160, 80, id(font_date), TextAlign::TOP_CENTER, "%d-%m-%Y", id(datetime).now());
- id: !extend bar1_left_draw
then:
- lambda: |-
switch (id(actual_page)){
case 0:
id(bar_display).image(80, 0, id(lightbulb), ImageAlign::TOP_CENTER, id(red));
break;
}
- id: !extend bar1_right_draw
then:
- lambda: |-
switch (id(actual_page)){
case 0:
id(bar_display).image(80, 0, id(lightbulb), ImageAlign::TOP_CENTER, id(red));
break;
}
-259
View File
@@ -1,259 +0,0 @@
packages:
remote_package_files:
url: https://github.com/dixi83/ESPHome_ButtonPlus
files:
- 'package/2bar_1display.yaml'
ref: main
refresh: 1d
globals:
- id: !extend nr_of_pages
initial_value: '1'
font:
- file: "https://github.com/web-fonts/ttf/raw/refs/heads/master/bpg-ingiri-arial-webfont.ttf"
id: font_arial20
size: 20
bpp: 4
- file: "https://torinak.com/font/7segment.ttf"
id: font_clock
size: 64
bpp: 4
- file: "https://torinak.com/font/7segment.ttf"
id: font_date
size: 24
bpp: 4
image:
- file: mdi:lightbulb
id: lightbulb
resize: 80x80
- file: mdi:alert
id: alert
resize: 80x80
- file: mdi:chevron-right
id: icon_next_page
resize: 30x30
- file: mdi:chevron-left
id: icon_prev_page
resize: 30x30
- file: mdi:home-thermometer-outline
id: icon_temp_inside
resize: 40x40
color:
- id: red
hex: FF0000
- id: blue
hex: 0000FF
- id: yellow
hex: FFFF00
- id: green
hex: 00FF00
- id: orange
hex: FF7F00
- id: white
hex: FFFFFF
- id: grey
hex: A6A6A6
event:
- platform: template
name: BAR1 button Left event
id: bar1_btn_left_event
event_types:
- "press page 0"
- "release page 0"
- "long press page 0"
- platform: template
name: BAR1 button Right event
id: bar1_btn_right_event
event_types:
- "press page 0"
- "release page 0"
- "long press page 0"
- platform: template
name: BAR2 button Left event
id: bar2_btn_left_event
event_types:
- "press page 0"
- "release page 0"
- "long press page 0"
- platform: template
name: BAR2 button Right event
id: bar2_btn_right_event
event_types:
- "press page 0"
- "release page 0"
- "long press page 0"
binary_sensor:
- id: !extend main_display_btn_left
on_click:
- script.execute: prev_page
- id: !extend main_display_btn_right
on_click:
- script.execute: next_page
- id: !extend bar1_btn_left
on_click:
lambda: |-
switch (id(actual_page)){
case 0:
id(bar1_btn_left_event).trigger("press page 0");
break;
}
on_release:
lambda: |-
switch (id(actual_page)){
case 0:
id(bar1_btn_left_event).trigger("release page 0");
break;
}
on_multi_click:
- timing:
- ON for at least 2s
then:
- lambda: |-
switch (id(actual_page)){
case 0:
id(bar1_btn_left_event).trigger("long press page 0");
break;
}
- id: !extend bar1_btn_right
on_click:
lambda: |-
switch (id(actual_page)){
case 0:
id(bar1_btn_right_event).trigger("press page 0");
break;
}
on_release:
lambda: |-
switch (id(actual_page)){
case 0:
id(bar1_btn_right_event).trigger("release page 0");
break;
}
on_multi_click:
- timing:
- ON for at least 2s
then:
- lambda: |-
switch (id(actual_page)){
case 0:
id(bar1_btn_right_event).trigger("long press page 0");
break;
}
- id: !extend bar2_btn_left
on_click:
lambda: |-
switch (id(actual_page)){
case 0:
id(bar2_btn_left_event).trigger("press page 0");
break;
}
on_release:
lambda: |-
switch (id(actual_page)){
case 0:
id(bar2_btn_left_event).trigger("release page 0");
break;
}
on_multi_click:
- timing:
- ON for at least 2s
then:
- lambda: |-
switch (id(actual_page)){
case 0:
id(bar2_btn_left_event).trigger("long press page 0");
break;
}
- id: !extend bar2_btn_right
on_click:
lambda: |-
switch (id(actual_page)){
case 0:
id(bar2_btn_right_event).trigger("press page 0");
break;
}
on_release:
lambda: |-
switch (id(actual_page)){
case 0:
id(bar2_btn_right_event).trigger("release page 0");
break;
}
on_multi_click:
- timing:
- ON for at least 2s
then:
- lambda: |-
switch (id(actual_page)){
case 0:
id(bar2_btn_right_event).trigger("long press page 0");
break;
}
display:
#########
# MAIN ##
#########
- id: !extend main_display
# data_rate: 40MHz
lambda: |-
id(all_pages_main).execute();
switch (id(actual_page)){
case 0:
it.print(100, 160, id(font_arial20), "This is page 0!");
break;
}
script:
- id: all_pages_main
then:
- lambda: |-
//>>> prev/next icons next to butotns
id(main_display).image(0, 208, id(icon_prev_page), ImageAlign::TOP_LEFT, id(red));
id(main_display).image(320, 208, id(icon_next_page), ImageAlign::TOP_RIGHT, id(red));
//
//>>> temperature inside:
id(main_display).image(36, 15, id(icon_temp_inside), ImageAlign::TOP_CENTER, id(red));
id(main_display).printf(36, 80, id(font_arial20), id(white), TextAlign::TOP_CENTER , "%.1f°", id(inside_temperature).state);
//
//>>> print time and date
id(main_display).strftime(160, 5, id(font_clock), TextAlign::TOP_CENTER, "%H:%M", id(datetime).now());
id(main_display).strftime(160, 80, id(font_date), TextAlign::TOP_CENTER, "%d-%m-%Y", id(datetime).now());
- id: !extend bar1_left_draw
then:
- lambda: |-
switch (id(actual_page)){
case 0:
id(bar_display).image(80, 0, id(lightbulb), ImageAlign::TOP_CENTER, id(red));
break;
}
- id: !extend bar1_right_draw
then:
- lambda: |-
switch (id(actual_page)){
case 0:
id(bar_display).image(80, 0, id(lightbulb), ImageAlign::TOP_CENTER, id(red));
break;
}
- id: !extend bar2_left_draw
then:
- lambda: |-
switch (id(actual_page)){
case 0:
id(bar_display).image(80, 0, id(lightbulb), ImageAlign::TOP_CENTER, id(red));
break;
}
- id: !extend bar2_right_draw
then:
- lambda: |-
switch (id(actual_page)){
case 0:
id(bar_display).image(80, 0, id(lightbulb), ImageAlign::TOP_CENTER, id(red));
break;
}
+277
View File
@@ -0,0 +1,277 @@
esphome:
name: buttonplus
friendly_name: ButtonPlus
esp32:
variant: ESP32S3
board: esp32-s3-devkitc1-n16r8
flash_size: 16MB
framework:
type: esp-idf
# Enable logging
logger:
level: DEBUG
# Enable Home Assistant API
api:
encryption:
key: ""
ota:
- platform: esphome
password: ""
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Buttonplus"
password: ""
packages:
remote_package_files:
url: https://gitea.furb.it/kennyboy55/ESPHome_ButtonPlusV2
ref: main
files:
- path: 'package/3bar_1display.yaml'
vars:
nr_of_pages: '3'
page_names: '{"Start", "Music", "Page 3"}'
- path: 'package/cards/music.yaml'
vars:
music_card_entity_id: 'media_player.spotifyplus_kennyboy44'
music_card_text_color: FFFFFF
music_card_icon_main_color: FF7F00
music_card_icon_bar_color: FF7F00
font:
- file: "https://github.com/web-fonts/ttf/raw/refs/heads/master/bpg-ingiri-arial-webfont.ttf"
id: font_arial20
size: 20
bpp: 4
- file: "https://torinak.com/font/7segment.ttf"
id: font_clock
size: 64
bpp: 4
- file: "https://torinak.com/font/7segment.ttf"
id: font_date
size: 24
bpp: 4
image:
- file: mdi:lightbulb
id: lightbulb
resize: 80x80
type: GRAYSCALE
- file: mdi:alert
id: alert
resize: 80x80
type: GRAYSCALE
- file: mdi:chevron-right
id: icon_next_page
resize: 30x30
type: GRAYSCALE
- file: mdi:chevron-left
id: icon_prev_page
resize: 30x30
type: GRAYSCALE
- file: mdi:home-thermometer-outline
id: icon_temp_inside
resize: 40x40
type: GRAYSCALE
- file: mdi:brightness-percent
id: icon_ambient_inside
resize: 40x40
type: GRAYSCALE
color:
- id: red
hex: FF0000
- id: blue
hex: 0000FF
- id: yellow
hex: FFFF00
- id: green
hex: 00FF00
- id: orange
hex: FF7F00
- id: white
hex: FFFFFF
- id: grey
hex: A6A6A6
display:
#########
# MAIN ##
#########
- id: !extend main_display
# data_rate: 40MHz
lambda: |-
id(all_pages_main).execute();
switch (id(actual_page)){
case 0:
it.print(100, 150, id(font_arial20), "Hooray, it works!");
break;
case 1:
id(music_card).execute(20, 125);
break;
case 2:
it.print(100, 150, id(font_arial20), "Last Page");
break;
}
script:
- id: all_pages_main
then:
- lambda: |-
//>>> prev/next icons next to butotns
id(main_display).image(0, 208, id(icon_prev_page), ImageAlign::TOP_LEFT, id(red));
id(main_display).image(320, 208, id(icon_next_page), ImageAlign::TOP_RIGHT, id(red));
//
//>>> Page name
id(main_display).print(100, 208, id(font_arial20), id(active_page_name).c_str());
//
//>>> temperature inside:
id(main_display).image(36, 15, id(icon_temp_inside), ImageAlign::TOP_CENTER, id(red));
id(main_display).printf(36, 80, id(font_arial20), id(white), TextAlign::TOP_CENTER , "%.1f°", id(buttonplus_temperature).state);
//
//>>> ambient light:
id(main_display).image(285, 15, id(icon_ambient_inside), ImageAlign::TOP_CENTER, id(red));
id(main_display).printf(285, 80, id(font_arial20), id(white), TextAlign::TOP_CENTER , "%.1flux", id(buttonplus_ambientlight).state);
//
//>>> print time and date
id(main_display).strftime(160, 5, id(font_clock), TextAlign::TOP_CENTER, "%H:%M", id(datetime).now());
id(main_display).strftime(160, 80, id(font_date), TextAlign::TOP_CENTER, "%d-%m-%Y", id(datetime).now());
- id: !extend bar1_left_draw
then:
- lambda: |-
switch (id(actual_page)){
case 0:
id(bar_display).image(80, 0, id(lightbulb), ImageAlign::TOP_CENTER, id(red));
break;
case 1:
id(music_card_bar).execute("volume_down");
break;
}
- id: !extend bar1_right_draw
then:
- lambda: |-
switch (id(actual_page)){
case 0:
id(bar_display).image(80, 0, id(lightbulb), ImageAlign::TOP_CENTER, id(red));
break;
case 1:
id(music_card_bar).execute("volume_up");
break;
}
- id: !extend bar2_left_draw
then:
- lambda: |-
switch (id(actual_page)){
case 0:
id(bar_display).image(80, 0, id(lightbulb), ImageAlign::TOP_CENTER, id(red));
break;
case 1:
id(music_card_bar).execute("prev");
break;
}
- id: !extend bar2_right_draw
then:
- lambda: |-
switch (id(actual_page)){
case 0:
id(bar_display).image(80, 0, id(lightbulb), ImageAlign::TOP_CENTER, id(red));
break;
case 1:
id(music_card_bar).execute("next");
break;
}
- id: !extend bar3_left_draw
then:
- lambda: |-
switch (id(actual_page)){
case 0:
id(bar_display).image(80, 0, id(lightbulb), ImageAlign::TOP_CENTER, id(red));
break;
case 1:
id(music_card_bar).execute("power");
break;
}
- id: !extend bar3_right_draw
then:
- lambda: |-
switch (id(actual_page)){
case 0:
id(bar_display).image(80, 0, id(lightbulb), ImageAlign::TOP_CENTER, id(red));
break;
case 1:
id(music_card_bar).execute("playpause");
break;
}
event:
- id: !extend bar1_btn_left_event
on_event:
then:
- lambda: |-
ESP_LOGD("main", "BAR1LEFT event %s triggered.", event_type.c_str());
- lambda: |-
if (event_type == "click page 1"){
id(music_card_vol_down).execute();
ESP_LOGD("main", "VOL DOWN");
}
- id: !extend bar1_btn_right_event
on_event:
then:
- lambda: |-
ESP_LOGD("main", "BAR1RIGHT event %s triggered.", event_type.c_str());
- lambda: |-
if (event_type == "click page 1"){
id(music_card_vol_up).execute();
ESP_LOGD("main", "VOL UP");
}
- id: !extend bar2_btn_left_event
on_event:
then:
- lambda: |-
ESP_LOGD("main", "BAR2LEFT event %s triggered.", event_type.c_str());
- lambda: |-
if (event_type == "click page 1"){
id(music_card_prev).execute();
ESP_LOGD("main", "PREV");
}
- id: !extend bar2_btn_right_event
on_event:
then:
- lambda: |-
ESP_LOGD("main", "BAR2RIGHT event %s triggered.", event_type.c_str());
- lambda: |-
if (event_type == "click page 1"){
id(music_card_next).execute();
ESP_LOGD("main", "NEXT");
}
- id: !extend bar3_btn_left_event
on_event:
then:
- lambda: |-
ESP_LOGD("main", "BAR3LEFT event %s triggered.", event_type.c_str());
- lambda: |-
if (event_type == "click page 1"){
id(music_card_power).execute();
ESP_LOGD("main", "POWER");
}
- id: !extend bar3_btn_right_event
on_event:
then:
- lambda: |-
ESP_LOGD("main", "BAR3RIGHT event %s triggered.", event_type.c_str());
- lambda: |-
if (event_type == "click page 1"){
id(music_card_playpause).execute();
ESP_LOGD("main", "PLAY PAUSE");
}
-338
View File
@@ -1,338 +0,0 @@
packages:
remote_package_files:
url: https://github.com/dixi83/ESPHome_ButtonPlus
files:
- 'package/3bar_1display.yaml'
ref: main
refresh: 1d
globals:
- id: !extend nr_of_pages
initial_value: '1'
font:
- file: "https://github.com/web-fonts/ttf/raw/refs/heads/master/bpg-ingiri-arial-webfont.ttf"
id: font_arial20
size: 20
bpp: 4
- file: "https://torinak.com/font/7segment.ttf"
id: font_clock
size: 64
bpp: 4
- file: "https://torinak.com/font/7segment.ttf"
id: font_date
size: 24
bpp: 4
image:
- file: mdi:lightbulb
id: lightbulb
resize: 80x80
- file: mdi:alert
id: alert
resize: 80x80
- file: mdi:chevron-right
id: icon_next_page
resize: 30x30
- file: mdi:chevron-left
id: icon_prev_page
resize: 30x30
- file: mdi:home-thermometer-outline
id: icon_temp_inside
resize: 40x40
color:
- id: red
hex: FF0000
- id: blue
hex: 0000FF
- id: yellow
hex: FFFF00
- id: green
hex: 00FF00
- id: orange
hex: FF7F00
- id: white
hex: FFFFFF
- id: grey
hex: A6A6A6
event:
- platform: template
name: BAR1 button Left event
id: bar1_btn_left_event
event_types:
- "press page 0"
- "release page 0"
- "long press page 0"
- platform: template
name: BAR1 button Right event
id: bar1_btn_right_event
event_types:
- "press page 0"
- "release page 0"
- "long press page 0"
- platform: template
name: BAR2 button Left event
id: bar2_btn_left_event
event_types:
- "press page 0"
- "release page 0"
- "long press page 0"
- platform: template
name: BAR2 button Right event
id: bar2_btn_right_event
event_types:
- "press page 0"
- "release page 0"
- "long press page 0"
- platform: template
name: BAR3 button Left event
id: bar3_btn_left_event
event_types:
- "press page 0"
- "release page 0"
- "long press page 0"
- platform: template
name: BAR3 button Right event
id: bar3_btn_right_event
event_types:
- "press page 0"
- "release page 0"
- "long press page 0"
binary_sensor:
- id: !extend main_display_btn_left
on_click:
- script.execute: prev_page
- id: !extend main_display_btn_right
on_click:
- script.execute: next_page
- id: !extend bar1_btn_left
on_click:
lambda: |-
switch (id(actual_page)){
case 0:
id(bar1_btn_left_event).trigger("press page 0");
break;
}
on_release:
lambda: |-
switch (id(actual_page)){
case 0:
id(bar1_btn_left_event).trigger("release page 0");
break;
}
on_multi_click:
- timing:
- ON for at least 2s
then:
- lambda: |-
switch (id(actual_page)){
case 0:
id(bar1_btn_left_event).trigger("long press page 0");
break;
}
- id: !extend bar1_btn_right
on_click:
lambda: |-
switch (id(actual_page)){
case 0:
id(bar1_btn_right_event).trigger("press page 0");
break;
}
on_release:
lambda: |-
switch (id(actual_page)){
case 0:
id(bar1_btn_right_event).trigger("release page 0");
break;
}
on_multi_click:
- timing:
- ON for at least 2s
then:
- lambda: |-
switch (id(actual_page)){
case 0:
id(bar1_btn_right_event).trigger("long press page 0");
break;
}
- id: !extend bar2_btn_left
on_click:
lambda: |-
switch (id(actual_page)){
case 0:
id(bar2_btn_left_event).trigger("press page 0");
break;
}
on_release:
lambda: |-
switch (id(actual_page)){
case 0:
id(bar2_btn_left_event).trigger("release page 0");
break;
}
on_multi_click:
- timing:
- ON for at least 2s
then:
- lambda: |-
switch (id(actual_page)){
case 0:
id(bar2_btn_left_event).trigger("long press page 0");
break;
}
- id: !extend bar2_btn_right
on_click:
lambda: |-
switch (id(actual_page)){
case 0:
id(bar2_btn_right_event).trigger("press page 0");
break;
}
on_release:
lambda: |-
switch (id(actual_page)){
case 0:
id(bar2_btn_right_event).trigger("release page 0");
break;
}
on_multi_click:
- timing:
- ON for at least 2s
then:
- lambda: |-
switch (id(actual_page)){
case 0:
id(bar2_btn_right_event).trigger("long press page 0");
break;
}
- id: !extend bar3_btn_left
on_click:
lambda: |-
switch (id(actual_page)){
case 0:
id(bar3_btn_left_event).trigger("press page 0");
break;
}
on_release:
lambda: |-
switch (id(actual_page)){
case 0:
id(bar3_btn_left_event).trigger("release page 0");
break;
}
on_multi_click:
- timing:
- ON for at least 2s
then:
- lambda: |-
switch (id(actual_page)){
case 0:
id(bar3_btn_left_event).trigger("long press page 0");
break;
}
- id: !extend bar3_btn_right
on_click:
lambda: |-
switch (id(actual_page)){
case 0:
id(bar3_btn_right_event).trigger("press page 0");
break;
}
on_release:
lambda: |-
switch (id(actual_page)){
case 0:
id(bar3_btn_right_event).trigger("release page 0");
break;
}
on_multi_click:
- timing:
- ON for at least 2s
then:
lambda: |-
switch (id(actual_page)){
case 0:
id(bar3_btn_right_event).trigger("long press page 0");
break;
}
display:
#########
# MAIN ##
#########
- id: !extend main_display
# data_rate: 40MHz
lambda: |-
id(all_pages_main).execute();
switch (id(actual_page)){
case 0:
it.print(100, 160, id(font_arial20), "This is page 0!");
break;
}
script:
- id: all_pages_main
then:
- lambda: |-
//>>> prev/next icons next to butotns
id(main_display).image(0, 208, id(icon_prev_page), ImageAlign::TOP_LEFT, id(red));
id(main_display).image(320, 208, id(icon_next_page), ImageAlign::TOP_RIGHT, id(red));
//
//>>> temperature inside:
id(main_display).image(36, 15, id(icon_temp_inside), ImageAlign::TOP_CENTER, id(red));
id(main_display).printf(36, 80, id(font_arial20), id(white), TextAlign::TOP_CENTER , "%.1f°", id(inside_temperature).state);
//
//>>> print time and date
id(main_display).strftime(160, 5, id(font_clock), TextAlign::TOP_CENTER, "%H:%M", id(datetime).now());
id(main_display).strftime(160, 80, id(font_date), TextAlign::TOP_CENTER, "%d-%m-%Y", id(datetime).now());
- id: !extend bar1_left_draw
then:
- lambda: |-
switch (id(actual_page)){
case 0:
id(bar_display).image(80, 0, id(lightbulb), ImageAlign::TOP_CENTER, id(red));
break;
}
- id: !extend bar1_right_draw
then:
- lambda: |-
switch (id(actual_page)){
case 0:
id(bar_display).image(80, 0, id(lightbulb), ImageAlign::TOP_CENTER, id(red));
break;
}
- id: !extend bar2_left_draw
then:
- lambda: |-
switch (id(actual_page)){
case 0:
id(bar_display).image(80, 0, id(lightbulb), ImageAlign::TOP_CENTER, id(red));
break;
}
- id: !extend bar2_right_draw
then:
- lambda: |-
switch (id(actual_page)){
case 0:
id(bar_display).image(80, 0, id(lightbulb), ImageAlign::TOP_CENTER, id(red));
break;
}
- id: !extend bar3_left_draw
then:
- lambda: |-
switch (id(actual_page)){
case 0:
id(bar_display).image(80, 0, id(lightbulb), ImageAlign::TOP_CENTER, id(red));
break;
}
- id: !extend bar3_right_draw
then:
- lambda: |-
switch (id(actual_page)){
case 0:
id(bar_display).image(80, 0, id(lightbulb), ImageAlign::TOP_CENTER, id(red));
break;
}
-641
View File
@@ -1,641 +0,0 @@
esphome:
name: button-keuken
friendly_name: Button+ Keuken
esp32:
board: esp32-s3-devkitc-1
flash_size: 16MB
framework:
type: arduino
# Enable logging
logger:
level: DEBUG
debug:
update_interval: 5s
# Enable Home Assistant API
api:
encryption:
key: "*"
ota:
- platform: esphome
password: "*"
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
substitutions:
music_card_enity_id: 'media_player.media_keuken_2'
music_card_text_color: FFFFFF
music_card_icon_main_color: FF7F00
music_card_icon_bar_color: FF7F00
packages:
remote_package_files:
url: https://github.com/dixi83/ESPHome_ButtonPlus
files:
- 'package/3bar_1display.yaml'
- 'package/cards/music.yaml'
ref: main
refresh: 1d
globals:
- id: !extend nr_of_pages
initial_value: '3'
- id: page_names
type: std::vector<std::string>
initial_value: '{"Page 1","Music","Page 3"}'
- id: active_page_name
type: std::string
initial_value: ""
font:
- file: "https://github.com/web-fonts/ttf/raw/refs/heads/master/bpg-ingiri-arial-webfont.ttf"
id: font_arial20
size: 20
bpp: 4
- file: "https://torinak.com/font/7segment.ttf"
id: font_clock
size: 64
bpp: 4
- file: "https://torinak.com/font/7segment.ttf"
id: font_date
size: 24
bpp: 4
image:
- file: mdi:lightbulb
id: lightbulb
resize: 80x80
- file: mdi:alert
id: alert
resize: 80x80
- file: mdi:chevron-right
id: icon_next_page
resize: 30x30
- file: mdi:chevron-left
id: icon_prev_page
resize: 30x30
- file: mdi:sun-thermometer-outline
id: icon_temp_outside
resize: 40x40
- file: mdi:home-thermometer-outline
id: icon_temp_inside
resize: 40x40
- file: mdi:trash-can
id: icon_trashcan
resize: 20x20
color:
- id: red
hex: FF0000
- id: blue
hex: 0000FF
- id: yellow
hex: FFFF00
- id: green
hex: 00FF00
- id: orange
hex: FF7F00
- id: white
hex: FFFFFF
- id: grey
hex: A6A6A6
event:
- platform: template
name: BAR1 button Left event
id: bar1_btn_left_event
event_types:
- "press page 0"
- "release page 0"
- "long press page 0"
- "press page 1"
- "release page 1"
- "long press page 1"
- "press page 2"
- "release page 2"
- "long press page 2"
- platform: template
name: BAR1 button Right event
id: bar1_btn_right_event
event_types:
- "press page 0"
- "release page 0"
- "long press page 0"
- "press page 1"
- "release page 1"
- "long press page 1"
- "press page 2"
- "release page 2"
- "long press page 2"
- platform: template
name: BAR2 button Left event
id: bar2_btn_left_event
event_types:
- "press page 0"
- "release page 0"
- "long press page 0"
- "press page 1"
- "release page 1"
- "long press page 1"
- "press page 2"
- "release page 2"
- "long press page 2"
- platform: template
name: BAR2 button Right event
id: bar2_btn_right_event
event_types:
- "press page 0"
- "release page 0"
- "long press page 0"
- "press page 1"
- "release page 1"
- "long press page 1"
- "press page 2"
- "release page 2"
- "long press page 2"
- platform: template
name: BAR3 button Left event
id: bar3_btn_left_event
event_types:
- "press page 0"
- "release page 0"
- "long press page 0"
- "press page 1"
- "release page 1"
- "long press page 1"
- "press page 2"
- "release page 2"
- "long press page 2"
- platform: template
name: BAR3 button Right event
id: bar3_btn_right_event
event_types:
- "press page 0"
- "release page 0"
- "long press page 0"
- "press page 1"
- "release page 1"
- "long press page 1"
- "press page 2"
- "release page 2"
- "long press page 2"
binary_sensor:
- id: !extend main_display_btn_left
on_click:
- script.execute: prev_page
- id: !extend main_display_btn_right
on_click:
- script.execute: next_page
- id: !extend bar1_btn_left
on_click:
lambda: |-
switch (id(actual_page)){
case 0:
id(bar1_btn_left_event).trigger("press page 0");
break;
case 1:
id(bar1_btn_left_event).trigger("press page 1");
id(music_card_vol_down).execute();
break;
case 2:
id(bar1_btn_left_event).trigger("press page 2");
break;
}
on_release:
lambda: |-
switch (id(actual_page)){
case 0:
id(bar1_btn_left_event).trigger("release page 0");
break;
case 1:
id(bar1_btn_left_event).trigger("release page 1");
break;
case 2:
id(bar1_btn_left_event).trigger("release page 2");
break;
}
on_multi_click:
- timing:
- ON for at least 2s
then:
- lambda: |-
switch (id(actual_page)){
case 0:
id(bar1_btn_left_event).trigger("long press page 0");
break;
case 1:
id(bar1_btn_left_event).trigger("long press page 1");
break;
case 2:
id(bar1_btn_left_event).trigger("long press page 2");
break;
}
- id: !extend bar1_btn_right
on_click:
lambda: |-
switch (id(actual_page)){
case 0:
id(bar1_btn_right_event).trigger("press page 0");
break;
case 1:
id(bar1_btn_right_event).trigger("press page 1");
id(music_card_vol_up).execute();
break;
case 2:
id(bar1_btn_right_event).trigger("press page 2");
break;
}
on_release:
lambda: |-
switch (id(actual_page)){
case 0:
id(bar1_btn_right_event).trigger("release page 0");
break;
case 1:
id(bar1_btn_right_event).trigger("release page 1");
break;
case 2:
id(bar1_btn_right_event).trigger("release page 2");
break;
}
on_multi_click:
- timing:
- ON for at least 2s
then:
- lambda: |-
switch (id(actual_page)){
case 0:
id(bar1_btn_right_event).trigger("long press page 0");
break;
case 1:
id(bar1_btn_right_event).trigger("long press page 1");
break;
case 2:
id(bar1_btn_right_event).trigger("long press page 2");
break;
}
- id: !extend bar2_btn_left
on_click:
lambda: |-
switch (id(actual_page)){
case 0:
id(bar2_btn_left_event).trigger("press page 0");
break;
case 1:
id(bar2_btn_left_event).trigger("press page 1");
id(music_card_prev).execute();
break;
case 2:
id(bar2_btn_left_event).trigger("press page 2");
break;
}
on_release:
lambda: |-
switch (id(actual_page)){
case 0:
id(bar2_btn_left_event).trigger("release page 0");
break;
case 1:
id(bar2_btn_left_event).trigger("release page 1");
break;
case 2:
id(bar2_btn_left_event).trigger("release page 2");
break;
}
on_multi_click:
- timing:
- ON for at least 2s
then:
- lambda: |-
switch (id(actual_page)){
case 0:
id(bar2_btn_left_event).trigger("long press page 0");
break;
case 1:
id(bar2_btn_left_event).trigger("long press page 1");
break;
case 2:
id(bar2_btn_left_event).trigger("long press page 2");
break;
}
- id: !extend bar2_btn_right
on_click:
lambda: |-
switch (id(actual_page)){
case 0:
id(bar2_btn_right_event).trigger("press page 0");
break;
case 1:
id(bar2_btn_right_event).trigger("press page 1");
id(music_card_next).execute();
break;
case 2:
id(bar2_btn_right_event).trigger("press page 2");
break;
}
on_release:
lambda: |-
switch (id(actual_page)){
case 0:
id(bar2_btn_right_event).trigger("release page 0");
break;
case 1:
id(bar2_btn_right_event).trigger("release page 1");
break;
case 2:
id(bar2_btn_right_event).trigger("release page 2");
break;
}
on_multi_click:
- timing:
- ON for at least 2s
then:
- lambda: |-
switch (id(actual_page)){
case 0:
id(bar2_btn_right_event).trigger("long press page 0");
break;
case 1:
id(bar2_btn_right_event).trigger("long press page 1");
break;
case 2:
id(bar2_btn_right_event).trigger("long press page 2");
break;
}
- id: !extend bar3_btn_left
on_click:
lambda: |-
switch (id(actual_page)){
case 0:
id(bar3_btn_left_event).trigger("press page 0");
break;
case 1:
id(bar3_btn_left_event).trigger("press page 1");
id(music_card_power).execute();
break;
case 2:
id(bar3_btn_left_event).trigger("press page 2");
break;
}
on_release:
lambda: |-
switch (id(actual_page)){
case 0:
id(bar3_btn_left_event).trigger("release page 0");
break;
case 1:
id(bar3_btn_left_event).trigger("release page 1");
break;
case 2:
id(bar3_btn_left_event).trigger("release page 2");
break;
}
on_multi_click:
- timing:
- ON for at least 2s
then:
- lambda: |-
switch (id(actual_page)){
case 0:
id(bar3_btn_left_event).trigger("long press page 0");
break;
case 1:
id(bar3_btn_left_event).trigger("long press page 1");
break;
case 2:
id(bar3_btn_left_event).trigger("long press page 2");
break;
}
- id: !extend bar3_btn_right
on_click:
lambda: |-
switch (id(actual_page)){
case 0:
id(bar3_btn_right_event).trigger("press page 0");
break;
case 1:
id(bar3_btn_right_event).trigger("press page 1");
id(music_card_playpause).execute();
break;
case 2:
id(bar3_btn_right_event).trigger("press page 2");
break;
}
on_release:
lambda: |-
switch (id(actual_page)){
case 0:
id(bar3_btn_right_event).trigger("release page 0");
break;
case 1:
id(bar3_btn_right_event).trigger("release page 1");
break;
case 2:
id(bar3_btn_right_event).trigger("release page 2");
break;
}
on_multi_click:
- timing:
- ON for at least 2s
then:
lambda: |-
switch (id(actual_page)){
case 0:
id(bar3_btn_right_event).trigger("long press page 0");
break;
case 1:
id(bar3_btn_right_event).trigger("long press page 1");
break;
case 2:
id(bar3_btn_right_event).trigger("long press page 2");
break;
}
sensor:
- platform: homeassistant
entity_id: sensor.gw1100a_outdoor_temperature
id: outside_temperature
###################
### containers: ###
###################
- platform: debug
free:
name: "Heap Free"
loop_time:
name: "Loop Time"
text_sensor:
- platform: homeassistant
id: container_first
entity_id: sensor.afvalinfo_containers_first_trash
- platform: homeassistant
id: container_first_color
entity_id: sensor.afvalinfo_containers_first_trash_color
- platform: homeassistant
id: container_second
entity_id: sensor.afvalinfo_containers_second_trash
- platform: homeassistant
id: container_second_color
entity_id: sensor.afvalinfo_containers_second_trash_color
display:
#########
# MAIN ##
#########
- id: !extend main_display
# data_rate: 40MHz
lambda: |-
id(all_pages_main).execute();
switch (id(actual_page)){
case 0:
id(containers).execute();
break;
case 1:
id(music_card).execute(20, 125);
break;
case 2:
it.print(100, 160, id(font_arial20), "This is page 3!");
break;
}
script:
- id: containers
then:
- lambda: |-
// first trashcan:
int x = 23;
int y = 125;
if(strcmp(id(container_first_color).state.c_str(), "grey") == 0) {
id(main_display).image(x, y+4, id(icon_trashcan), ImageAlign::TOP_LEFT, id(grey));
} else if (strcmp(id(container_first_color).state.c_str(), "green") == 0) {
id(main_display).image(x, y+4, id(icon_trashcan), ImageAlign::TOP_LEFT, id(green));
} else if (strcmp(id(container_first_color).state.c_str(), "blue") == 0) {
id(main_display).image(x, y+4, id(icon_trashcan), ImageAlign::TOP_LEFT, id(blue));
} else if (strcmp(id(container_first_color).state.c_str(), "orange") == 0) {
id(main_display).image(x, y+4, id(icon_trashcan), ImageAlign::TOP_LEFT, id(orange));
}
id(main_display).print(x+22, y, id(font_arial20), id(music_card_text_color), TextAlign::TOP_LEFT , id(container_first).state.c_str());
// secomnd trashcan:
y = 151;
if(strcmp(id(container_second_color).state.c_str(), "grey") == 0) {
id(main_display).image(x, y+4, id(icon_trashcan), ImageAlign::TOP_LEFT, id(grey));
} else if (strcmp(id(container_second_color).state.c_str(), "green") == 0) {
id(main_display).image(x, y+4, id(icon_trashcan), ImageAlign::TOP_LEFT, id(green));
} else if (strcmp(id(container_second_color).state.c_str(), "blue") == 0) {
id(main_display).image(x, y+4, id(icon_trashcan), ImageAlign::TOP_LEFT, id(blue));
} else if (strcmp(id(container_second_color).state.c_str(), "orange") == 0) {
id(main_display).image(x, y+4, id(icon_trashcan), ImageAlign::TOP_LEFT, id(orange));
}
id(main_display).print(x+22, y, id(font_arial20), id(music_card_text_color), TextAlign::TOP_LEFT , id(container_second).state.c_str());
- id: all_pages_main
then:
- lambda: |-
//>>> prev/next icons next to butotns
id(main_display).image(0, 208, id(icon_prev_page), ImageAlign::TOP_LEFT, id(red));
id(main_display).image(320, 208, id(icon_next_page), ImageAlign::TOP_RIGHT, id(red));
//
//>>> Show page name at bottom of display
id(main_display).printf(160, 238, id(font_arial20), id(white), TextAlign::BASELINE_CENTER, "%s", id(active_page_name).c_str());
//
//>>> temperature inside:
id(main_display).image(36, 15, id(icon_temp_inside), ImageAlign::TOP_CENTER, id(red));
id(main_display).printf(36, 80, id(font_arial20), id(white), TextAlign::TOP_CENTER , "%.1f°", id(inside_temperature).state);
//
//>>> print time and date
id(main_display).strftime(160, 5, id(font_clock), TextAlign::TOP_CENTER, "%H:%M", id(datetime).now());
id(main_display).strftime(160, 80, id(font_date), TextAlign::TOP_CENTER, "%d-%m-%Y", id(datetime).now());
//
//>>> outside temperature
id(main_display).image(285, 15, id(icon_temp_outside), ImageAlign::TOP_CENTER, id(red));
id(main_display).printf(285, 80, id(font_arial20), id(white), TextAlign::TOP_CENTER , "%.1f°", id(outside_temperature).state);
- id: !extend bar1_left_draw
then:
- lambda: |-
switch (id(actual_page)){
case 0:
id(bar_display).image(80, 0, id(lightbulb), ImageAlign::TOP_CENTER, id(red));
break;
case 1:
id(music_card_bar).execute("volume_down");
break;
case 2:
id(bar_display).image(80, 0, id(lightbulb), ImageAlign::TOP_CENTER, id(blue));
break;
}
- id: !extend bar1_right_draw
then:
- lambda: |-
switch (id(actual_page)){
case 0:
id(bar_display).image(80, 0, id(lightbulb), ImageAlign::TOP_CENTER, id(red));
break;
case 1:
id(music_card_bar).execute("volume_up");
break;
case 2:
id(bar_display).image(80, 0, id(lightbulb), ImageAlign::TOP_CENTER, id(blue));
break;
}
- id: !extend bar2_left_draw
then:
- lambda: |-
switch (id(actual_page)){
case 0:
id(bar_display).image(80, 0, id(lightbulb), ImageAlign::TOP_CENTER, id(red));
break;
case 1:
id(music_card_bar).execute("prev");
break;
case 2:
id(bar_display).image(80, 0, id(lightbulb), ImageAlign::TOP_CENTER, id(blue));
break;
}
- id: !extend bar2_right_draw
then:
- lambda: |-
switch (id(actual_page)){
case 0:
id(bar_display).image(80, 0, id(lightbulb), ImageAlign::TOP_CENTER, id(red));
break;
case 1:
id(music_card_bar).execute("next");
break;
case 2:
id(bar_display).image(80, 0, id(lightbulb), ImageAlign::TOP_CENTER, id(blue));
break;
}
- id: !extend bar3_left_draw
then:
- lambda: |-
switch (id(actual_page)){
case 0:
id(bar_display).image(80, 0, id(lightbulb), ImageAlign::TOP_CENTER, id(red));
break;
case 1:
id(music_card_bar).execute("power");
break;
case 2:
id(bar_display).image(80, 0, id(lightbulb), ImageAlign::TOP_CENTER, id(blue));
break;
}
- id: !extend bar3_right_draw
then:
- lambda: |-
switch (id(actual_page)){
case 0:
id(bar_display).image(80, 0, id(lightbulb), ImageAlign::TOP_CENTER, id(red));
break;
case 1:
id(music_card_bar).execute("playpause");
break;
case 2:
id(bar_display).image(80, 0, id(lightbulb), ImageAlign::TOP_CENTER, id(blue));
break;
}
-96
View File
@@ -1,96 +0,0 @@
###
### This is a basic example, showing all features of the Button+
###
esphome:
name: buttonplus-test
friendly_name: ButtonPlus_test
esp32:
board: esp32-s3-devkitc-1
flash_size: 16MB
framework:
type: arduino
# Enable logging
logger:
# Enable Home Assistant API
api:
encryption:
key: "xxx"
ota:
- platform: esphome
password: "xxx"
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
###
### Code below is specific for the Button+
###
packages:
remote_package_files:
url: https://github.com/dixi83/ESPHome_ButtonPlus
files: [package/1bar_1display.yaml]
ref: main
refresh: 1d
font:
- file: "https://github.com/web-fonts/ttf/raw/refs/heads/master/bpg-ingiri-arial-webfont.ttf"
id: my_font20
size: 20
- file: "https://github.com/web-fonts/ttf/raw/refs/heads/master/bpg-ingiri-arial-webfont.ttf"
id: my_font15
size: 15
- file: "https://github.com/web-fonts/ttf/raw/refs/heads/master/bpg-ingiri-arial-webfont.ttf"
id: my_font40
size: 40
image:
- file: mdi:lightbulb
id: lightbulb
resize: 80x80
- file: mdi:alert
id: alert
resize: 80x80
- file: "https://i.postimg.cc/CMXFmZ5n/henk.png"
id: henk
resize: 120x100
type: RGB565
use_transparency: true
color:
- id: red
hex: FF0000
- id: blue
hex: 0000FF
- id: yellow
hex: FFFF00
- id: green
hex: 00FF00
display:
- id: !extend display_j2_main
lambda: |-
// Print inside temperature (from homeassistant sensor)
if (id(inside_temperature).has_state()) {
it.printf(2, 60, id(my_font20), id(blue), TextAlign::BASELINE_LEFT , "%.1f°", id(inside_temperature).state);
}
it.printf(2, 80, id(my_font15), id(blue), TextAlign::BASELINE_LEFT, "Binnen");
// Print time and date
it.strftime(160, 60, id(my_font40), TextAlign::BASELINE_CENTER, "%H:%M", id(datetime).now());
it.strftime(160, 80, id(my_font20), id(red), TextAlign::BASELINE_CENTER, "%d-%m-%Y", id(datetime).now());
// Draw the image my_online_image at position
// it.image(160, 100, id(henk), ImageAlign::TOP_CENTER);
- id: !extend display_j3_Left
lambda: |-
it.image(80, 0, id(lightbulb), ImageAlign::TOP_CENTER, id(red));
- id: !extend display_j3_Right
lambda: |-
it.image(80, 0, id(alert), ImageAlign::TOP_CENTER);
+1
View File
@@ -1,6 +1,7 @@
MIT License
Copyright (c) 2024 Martijn
Copyright (c) 2026 Kenneth van Ewijk
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+13 -30
View File
@@ -1,26 +1,25 @@
# ESPhome_ButtonPlus
> Forked from [https://github.com/dixi83/ESPhome_ButtonPlus](https://github.com/dixi83/ESPhome_ButtonPlus), all initial effort for this was done by [dixi83](https://github.com/dixi83) and [balk77](https://github.com/balk77)
*ESPHome documentation and examples to make ESPHome working on the Button+*
# ESPhome_ButtonPlus V2
*ESPHome documentation and examples to make ESPHome working on the Button+ with the V2 PCB*
_(not yet tested with the V2 display module and buttons, only with the V2 PCB with the old display and bars)_
<img src="./Docs/ButtonPlusImage.png" alt="Buttonplus running ESPhome" width="300" height="397"/>
## Button+
## Changes compared to V1
This project was intended for the Button+ V1. Now that the V2 is out, I wanted to also try this out. However, the original project did not work out of the box. This version is currently only for the 1Display+3Bars version. It now uses the esp-idf framework instead of arduino. PSRAM is configured. The GPIO pins have been updated to match the V2 PCB, and the ambient light sensor has been added.
### Generating events for all pages
I also added an events yaml generator, written in python (mostly by AI). It can be used to generate a `3bar_1display_events.yaml` file that defines events for all buttons for the number of pages you want.
## Button+ (V2)
The Button+ is a modular control panel for home automation systems. It is designed to work with MQTT. It has several modules: a "Base module", a "Main display" and "BAR modules" where the position and amount can be changed according your needs.
__You can order the Button+ on there [website](https://button.plus).__ this is also the place to find the original firmware, in case you want to revert back.
To make the Button+ work with ESPHome we needed at least a list of used components and a pinout/IO-list. I spend a couple of evenings with my multimeter, making my wife crazy with the beeps, googled and collected data sheets of the components, broke a the display of 1 BAR module and did a trail-on-error to make stuf work with ESPHome. Here in this repo you'll find the [results](./Components) of my journy.
The ESPHome integration adds more flexebility and features, but also add some more complexity. To minimize the complexity I started this repo to collect examples and improve my work.
> Want to help? Pull requests are welcome!
[Dutch support topic on Tweakers.net](https://gathering.tweakers.net/forum/list_messages/2270086)
<!--
[English support topic on Home Assistant Community:](https://)
TODO
-->
## Usefull Links
@@ -30,20 +29,4 @@ TODO
## Documentation
The documentaion of the Button+ ESPHome integration can befound in the [Docs directory](./Docs)
## Status
All the features, i the Buton+ has to offer should work with the [`basic_example.yaml`](./Examples/basic_example.yaml). Please reffer the ESPHome documentaion for more information on how to build your own menu or ... (?)
The documentaion is yet to be made... Again If you want to help... Pull requests are welcome!
## TODO:
- [x] Neonleds
- [x] figure out how to control them individually. [> maybe with Light Partition](https://esphome.io/components/light/partition)
- [x] find correct [type](https://esphome.io/components/light/neopixelbus.html#configuration-variables)
- [x] Temperature sensor STS35
- [ ] Make a basic [package](https://esphome.io/components/packages.html) so there is less initial code to be copied.
- [ ] Documentation
- [ ] More examples
The documentaion of the Button+ ESPHome integration can befound in the [Docs directory](./Docs)
-529
View File
@@ -1,529 +0,0 @@
esphome:
name: buttonplus-test
friendly_name: ButtonPlus_test
on_boot:
priority: -100
then:
- script.execute: setup_bar_displays
- script.execute: update_bar1_left
- script.execute: update_bar1_right
- script.execute: update_bar2_left
- script.execute: update_bar2_right
- script.execute: update_bar3_left
- script.execute: update_bar3_right
esp32:
board: esp32-s3-devkitc-1
flash_size: 16MB
framework:
type: arduino
debug:
update_interval: 5s
text_sensor:
- platform: debug
device:
name: "Device Info"
reset_reason:
name: "Reset Reason"
# Logger must be at least debug (default)
logger:
level: debug
# Enable Home Assistant API
api:
encryption:
key: ""
ota:
- platform: esphome
password: ""
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
output_power: 15.5dB
i2c:
- id: temperature_sensor
sda: GPIO1
scl: GPIO2
- id: buttons
sda: GPIO47
scl: GPIO48
frequency: 400khz
spi:
clk_pin: GPIO36
mosi_pin: GPIO35
mcp23008:
- id: base_J0
i2c_id: buttons
address: 0x20
- id: base_J1
i2c_id: buttons
address: 0x21
- id: base_J2
i2c_id: buttons
address: 0x22
- id: base_J3
i2c_id: buttons
address: 0x23
output:
- platform: ledc
pin: GPIO3
id: display_main_backlight_pwm
inverted: True
max_power: 1.0
frequency: 500Hz
- platform: ledc
pin: GPIO46
id: displays_mini_backlight_pwm
inverted: True
max_power: 0.6
frequency: 500Hz
- id: cs_pin_bar1_left
platform: gpio
pin:
mcp23xxx: base_J1
number: 5
mode:
output: true
inverted: true
- id: cs_pin_bar1_right
platform: gpio
pin:
mcp23xxx: base_J1
number: 1
mode:
output: true
inverted: true
- id: cs_pin_bar2_left
platform: gpio
pin:
mcp23xxx: base_J2
number: 5
mode:
output: true
inverted: true
- id: cs_pin_bar2_right
platform: gpio
pin:
mcp23xxx: base_J2
number: 1
mode:
output: true
inverted: true
- id: cs_pin_bar3_left
platform: gpio
pin:
mcp23xxx: base_J3
number: 5
mode:
output: true
inverted: true
- id: cs_pin_bar3_right
platform: gpio
pin:
mcp23xxx: base_J3
number: 1
mode:
output: true
inverted: true
sensor:
- platform: sts3x
id: inside_temperature
i2c_id: temperature_sensor
name: "Temperature"
address: 0x4A
update_interval: 10s
- platform: homeassistant
entity_id: sensor.gw1100a_outdoor_temperature
id: outside_temperature
- platform: debug
free:
name: "Heap Free"
block:
name: "Heap Max Block"
loop_time:
name: "Loop Time"
psram:
name: "Free PSRAM"
time:
- platform: homeassistant
id: datetime
font:
- file: "https://github.com/web-fonts/ttf/raw/refs/heads/master/bpg-ingiri-arial-webfont.ttf"
id: font_arial20
size: 20
bpp: 4
- file: "https://github.com/web-fonts/ttf/raw/refs/heads/master/bpg-ingiri-arial-webfont.ttf"
id: font_arial15
size: 15
bpp: 4
- file: "https://github.com/web-fonts/ttf/raw/refs/heads/master/bpg-ingiri-arial-webfont.ttf"
id: font_arial40
size: 40
bpp: 4
- file: "https://torinak.com/font/7segment.ttf"
id: font_clock
size: 80
bpp: 4
- file: "https://torinak.com/font/7segment.ttf"
id: font_date
size: 30
bpp: 4
image:
- file: mdi:lightbulb
id: lightbulb
resize: 80x80
- file: mdi:alert
id: alert
resize: 80x80
- file: mdi:chevron-right
id: icon_next_page
resize: 30x30
- file: mdi:chevron-left
id: icon_prev_page
resize: 30x30
- file: mdi:sun-thermometer-outline
id: icon_temp_outside
resize: 40x40
- file: mdi:home-thermometer-outline
id: icon_temp_inside
resize: 40x40
color:
- id: red
hex: FF0000
- id: blue
hex: 0000FF
- id: yellow
hex: FFFF00
- id: green
hex: 00FF00
- id: orange
hex: FF7F00
- id: white
hex: FFFFFF
binary_sensor:
- platform: gpio
id: 'main_display_btn_left'
name: "Main Display button Left"
pin:
mcp23xxx: base_J2
number: 6
mode: INPUT_PULLUP
inverted: true
on_click:
- script.execute: prev_page_all
- platform: gpio
id: 'main_display_btn_right'
name: "Main Display button Right"
pin:
mcp23xxx: base_J2
number: 2
mode: INPUT_PULLUP
inverted: true
on_click:
- script.execute: next_page_all
- platform: gpio
id: 'bar1_btn_left'
name: "BAR 1 button Left"
pin:
mcp23xxx: base_J3
number: 6
mode: INPUT_PULLUP
inverted: true
- platform: gpio
id: 'bar1_btn_right'
name: "BAR 1 button Right"
pin:
mcp23xxx: base_J3
number: 2
mode: INPUT_PULLUP
inverted: true
light:
- platform: neopixelbus
id: neopixels
type: RGB
variant: WS2812
pin: GPIO4
num_leds: 5
- platform: monochromatic
output: display_main_backlight_pwm
gamma_correct : 2.2
name: "Main Display Backlight"
id: display_main_backlight
restore_mode: ALWAYS_ON
- platform: monochromatic
output: displays_mini_backlight_pwm
gamma_correct : 2.2
name: "Mini Display Backlight"
id: displays_mini_backlight
restore_mode: ALWAYS_ON
- platform: partition
name: "BAR 1 Left RGB Front"
id: bar1_left_rgb_front
segments:
- id: neopixels
from: 0
to: 0
- platform: partition
name: "BAR 1 Left RGB Back"
id: bar1_left_rgb_back
segments:
- id: neopixels
from: 1
to: 1
- platform: partition
name: "BAR 1 Right RGB Front"
id: bar1_right_rgb_front
segments:
- id: neopixels
from: 2
to: 2
- platform: partition
name: "BAR 1 Right RGB Back"
id: bar1_right_rgb_back
segments:
- id: neopixels
from: 3
to: 3
display:
- id: main_display
platform: ili9xxx
model: ili9341
color_order: rgb
update_interval: 30s
invert_colors: false
dc_pin:
number: GPIO37
allow_other_uses: true
cs_pin:
mcp23xxx: base_J0
number: 5
mode:
output: true
inverted: false
show_test_card: false
dimensions:
height: 240
width: 320
rotation: 180
pages:
- id: page1
lambda: |-
id(all_pages).execute();
it.print(100, 160, id(font_arial20), "This is page 1!");
- id: bar_display
platform: ili9xxx
model: ST7735
color_order: bgr
update_interval: never
dc_pin:
number: GPIO37
allow_other_uses: true
invert_colors: false
show_test_card: false
auto_clear_enabled: false
dimensions:
height: 160
width: 80
offset_width: 24
script:
- id: update_bar1_left
then:
- lambda: |-
id(cs_pin_bar1_left).turn_on(); // <<
id(cs_pin_bar1_right).turn_off();
id(cs_pin_bar2_left).turn_off();
id(cs_pin_bar2_right).turn_off();
id(cs_pin_bar3_left).turn_off();
id(cs_pin_bar3_right).turn_off();
id(bar_display).clear();
id(bar_display).set_rotation(display::DisplayRotation::DISPLAY_ROTATION_270_DEGREES);
id(bar1_left_page1).execute();
id(bar_display).update();
delay(1);
id(cs_pin_bar1_left).turn_off();
- id: update_bar1_right
then:
- lambda: |-
id(cs_pin_bar1_left).turn_off();
id(cs_pin_bar1_right).turn_on(); // <<
id(cs_pin_bar2_left).turn_off();
id(cs_pin_bar2_right).turn_off();
id(cs_pin_bar3_left).turn_off();
id(cs_pin_bar3_right).turn_off();
id(bar_display).clear();
id(bar_display).set_rotation(display::DisplayRotation::DISPLAY_ROTATION_90_DEGREES);
id(bar1_right_page1).execute();
id(bar_display).update();
delay(1);
id(cs_pin_bar1_right).turn_off();
- id: update_bar2_left
then:
- lambda: |-
id(cs_pin_bar1_left).turn_off();
id(cs_pin_bar1_right).turn_off();
id(cs_pin_bar2_left).turn_on(); // <<
id(cs_pin_bar2_right).turn_off();
id(cs_pin_bar3_left).turn_off();
id(cs_pin_bar3_right).turn_off();
//
id(bar_display).fill(id(orange));
id(bar_display).update();
delay(1);
id(cs_pin_bar2_left).turn_off();
- id: update_bar2_right
then:
- lambda: |-
id(cs_pin_bar1_left).turn_off();
id(cs_pin_bar1_right).turn_off();
id(cs_pin_bar2_left).turn_off();
id(cs_pin_bar2_right).turn_on(); // <<
id(cs_pin_bar3_left).turn_off();
id(cs_pin_bar3_right).turn_off();
//
id(bar_display).fill(id(orange));
id(bar_display).update();
delay(1);
id(cs_pin_bar2_right).turn_off();
- id: update_bar3_left
then:
- lambda: |-
id(cs_pin_bar1_left).turn_off();
id(cs_pin_bar1_right).turn_off();
id(cs_pin_bar2_left).turn_off();
id(cs_pin_bar2_right).turn_off();
id(cs_pin_bar3_left).turn_on(); // <<
id(cs_pin_bar3_right).turn_off();
//
id(bar_display).fill(id(orange));
id(bar_display).update();
delay(1);
id(cs_pin_bar3_left).turn_off();
- id: update_bar3_right
then:
- lambda: |-
id(cs_pin_bar1_left).turn_off();
id(cs_pin_bar1_right).turn_off();
id(cs_pin_bar2_left).turn_off();
id(cs_pin_bar2_right).turn_off();
id(cs_pin_bar3_left).turn_off();
id(cs_pin_bar3_right).turn_on();
//
id(bar_display).fill(id(orange));
id(bar_display).update();
delay(1);
id(cs_pin_bar3_right).turn_off();
- id: bar1_left_page1
then:
- lambda: |-
id(bar_display).image(80, 0, id(lightbulb), ImageAlign::TOP_CENTER, id(orange));
- id: bar1_right_page1
then:
- lambda: |-
id(bar_display).image(80, 0, id(alert), ImageAlign::TOP_CENTER, id(red));
- id: setup_bar_displays
then:
- lambda: |-
id(cs_pin_bar1_left).turn_on();
id(bar_display).setup();
id(cs_pin_bar1_left).turn_off();
delay(5);
id(cs_pin_bar1_right).turn_on();
id(bar_display).setup();
id(cs_pin_bar1_right).turn_off();
delay(5);
id(cs_pin_bar2_left).turn_on();
id(bar_display).setup();
id(cs_pin_bar2_left).turn_off();
delay(5);
id(cs_pin_bar2_right).turn_on();
id(bar_display).setup();
id(cs_pin_bar2_right).turn_off();
delay(5);
id(cs_pin_bar3_left).turn_on();
id(bar_display).setup();
id(cs_pin_bar3_left).turn_off();
delay(5);
id(cs_pin_bar3_right).turn_on();
id(bar_display).setup();
id(cs_pin_bar3_right).turn_off();
- id: update_main
then:
- component.update: main_display
- id: update_all
then:
- component.update: main_display
- script.execute: update_bar1_left
- script.execute: update_bar1_right
- script.execute: update_bar2_left
- script.execute: update_bar2_right
- script.execute: update_bar3_left
- script.execute: update_bar3_right
- id: next_page_main
then:
- display.page.show_next: main_display
- script.execute: update_main
- id: next_page_all
then:
- display.page.show_next: main_display
- script.execute: update_all
- id: prev_page_main
then:
- display.page.show_previous: main_display
- script.execute: update_main
- id: prev_page_all
then:
- display.page.show_previous: main_display
- script.execute: update_all
- id: all_pages
then:
- lambda: |-
//>>> make a border arround the display
id(main_display).filled_rectangle( 0, 0, 320, 3, id(red));
id(main_display).filled_rectangle( 0, 0, 3, 240, id(red));
id(main_display).filled_rectangle( 0, 237, 320, 3, id(red));
//id(main_display).filled_rectangle( 0, 206, 320, 3, id(red));
id(main_display).filled_rectangle(317, 0, 3, 240, id(red));
//
//>>> prev/next icons next to butotns
id(main_display).image(0, 208, id(icon_prev_page), ImageAlign::TOP_LEFT, id(orange));
id(main_display).image(320, 208, id(icon_next_page), ImageAlign::TOP_RIGHT, id(orange));
//
//>>> temperature inside:
id(main_display).image(36, 15, id(icon_temp_inside), ImageAlign::TOP_CENTER, id(orange));
id(main_display).printf(36, 85, id(font_arial20), id(white), TextAlign::BASELINE_CENTER , "%.1f°", id(inside_temperature).state);
//
//>>> vertical line:
id(main_display).filled_rectangle(70, 0, 3, 115, id(red));
//
//>>> print time and date
id(main_display).strftime(160, 5, id(font_clock), TextAlign::TOP_CENTER, "%H:%M", id(datetime).now());
id(main_display).strftime(160, 80, id(font_date), TextAlign::TOP_CENTER, "%d-%m-%Y", id(datetime).now());
//
//>>> vertical line:
id(main_display).filled_rectangle(250, 0, 3, 115, id(red));
//
//>>> outside temperature
id(main_display).image(285, 15, id(icon_temp_outside), ImageAlign::TOP_CENTER, id(orange));
id(main_display).printf(285, 85, id(font_arial20), id(white), TextAlign::BASELINE_CENTER , "%.1f°", id(outside_temperature).state);
//
//>>> horizontal line:
id(main_display).filled_rectangle(0, 115, 320, 3, id(red));
@@ -0,0 +1,358 @@
## Dynamically generated on xx-xx-xxxx
## Run python script again to update
substitutions:
nr_of_pages: 2
# Dynamically generated list
display_events:
- "double_click page 0"
- "triple_click page 0"
- "hold page 0"
- "double_click page 1"
- "triple_click page 1"
- "hold page 1"
# Dynamically generated list
button_events:
- "click page 0"
- "double_click page 0"
- "triple_click page 0"
- "hold page 0"
- "click page 1"
- "double_click page 1"
- "triple_click page 1"
- "hold page 1"
event:
- platform: template
name: DISPLAY Button Left event
id: display_btn_left_event
device_class: button
event_types: ${display_events}
- platform: template
name: DISPLAY Button Right event
id: display_btn_right_event
device_class: button
event_types: ${display_events}
- platform: template
name: BAR1 Button Left event
id: bar1_btn_left_event
device_class: button
event_types: ${button_events}
- platform: template
name: BAR1 Button Right event
id: bar1_btn_right_event
device_class: button
event_types: ${button_events}
- platform: template
name: BAR2 Button Left event
id: bar2_btn_left_event
device_class: button
event_types: ${button_events}
- platform: template
name: BAR2 Button Right event
id: bar2_btn_right_event
device_class: button
event_types: ${button_events}
- platform: template
name: BAR3 Button Left event
id: bar3_btn_left_event
device_class: button
event_types: ${button_events}
- platform: template
name: BAR3 Button Right event
id: bar3_btn_right_event
device_class: button
event_types: ${button_events}
binary_sensor:
- id: !extend main_display_btn_left
filters:
- delayed_off: 10ms
internal: true
on_multi_click:
- timing:
- ON for at most 300ms
- OFF for at least 0.2s
then:
- script.execute: prev_page
- timing:
- ON for at most 300ms
- OFF for at most 0.2s
- ON for at most 300ms
- OFF for at least 0.2s
then:
lambda: |-
id(display_btn_left_event).trigger("double_click page " + std::to_string(id(actual_page)));
- timing:
- ON for at most 300ms
- OFF for at most 0.2s
- ON for at most 300ms
- OFF for at most 0.2s
- ON for at most 300ms
- OFF for at least 0.2s
then:
lambda: |-
id(display_btn_left_event).trigger("triple_click page " + std::to_string(id(actual_page)));
- timing:
- ON for 1s to 5s
- OFF for at least 0.2s
then:
lambda: |-
id(display_btn_left_event).trigger("hold page " + std::to_string(id(actual_page)));
- id: !extend main_display_btn_right
filters:
- delayed_off: 10ms
internal: true
on_multi_click:
- timing:
- ON for at most 300ms
- OFF for at least 0.2s
then:
- script.execute: next_page
- timing:
- ON for at most 300ms
- OFF for at most 0.2s
- ON for at most 300ms
- OFF for at least 0.2s
then:
lambda: |-
id(display_btn_right_event).trigger("double_click page " + std::to_string(id(actual_page)));
- timing:
- ON for at most 300ms
- OFF for at most 0.2s
- ON for at most 300ms
- OFF for at most 0.2s
- ON for at most 300ms
- OFF for at least 0.2s
then:
lambda: |-
id(display_btn_right_event).trigger("triple_click page " + std::to_string(id(actual_page)));
- timing:
- ON for 1s to 5s
- OFF for at least 0.2s
then:
lambda: |-
id(display_btn_right_event).trigger("hold page " + std::to_string(id(actual_page)));
- id: !extend bar1_btn_left
filters:
- delayed_off: 10ms
internal: true
on_multi_click:
- timing:
- ON for at most 300ms
- OFF for at least 0.2s
then:
lambda: |-
id(bar1_btn_left_event).trigger("click page " + std::to_string(id(actual_page)));
- timing:
- ON for at most 300ms
- OFF for at most 0.2s
- ON for at most 300ms
- OFF for at least 0.2s
then:
lambda: |-
id(bar1_btn_left_event).trigger("double_click page " + std::to_string(id(actual_page)));
- timing:
- ON for at most 300ms
- OFF for at most 0.2s
- ON for at most 300ms
- OFF for at most 0.2s
- ON for at most 300ms
- OFF for at least 0.2s
then:
lambda: |-
id(bar1_btn_left_event).trigger("triple_click page " + std::to_string(id(actual_page)));
- timing:
- ON for 1s to 5s
- OFF for at least 0.2s
then:
lambda: |-
id(bar1_btn_left_event).trigger("hold page " + std::to_string(id(actual_page)));
- id: !extend bar1_btn_right
filters:
- delayed_off: 10ms
internal: true
on_multi_click:
- timing:
- ON for at most 300ms
- OFF for at least 0.2s
then:
lambda: |-
id(bar1_btn_right_event).trigger("click page " + std::to_string(id(actual_page)));
- timing:
- ON for at most 300ms
- OFF for at most 0.2s
- ON for at most 300ms
- OFF for at least 0.2s
then:
lambda: |-
id(bar1_btn_right_event).trigger("double_click page " + std::to_string(id(actual_page)));
- timing:
- ON for at most 300ms
- OFF for at most 0.2s
- ON for at most 300ms
- OFF for at most 0.2s
- ON for at most 300ms
- OFF for at least 0.2s
then:
lambda: |-
id(bar1_btn_right_event).trigger("triple_click page " + std::to_string(id(actual_page)));
- timing:
- ON for 1s to 5s
- OFF for at least 0.2s
then:
lambda: |-
id(bar1_btn_right_event).trigger("hold page " + std::to_string(id(actual_page)));
- id: !extend bar2_btn_left
filters:
- delayed_off: 10ms
internal: true
on_multi_click:
- timing:
- ON for at most 300ms
- OFF for at least 0.2s
then:
lambda: |-
id(bar2_btn_left_event).trigger("click page " + std::to_string(id(actual_page)));
- timing:
- ON for at most 300ms
- OFF for at most 0.2s
- ON for at most 300ms
- OFF for at least 0.2s
then:
lambda: |-
id(bar2_btn_left_event).trigger("double_click page " + std::to_string(id(actual_page)));
- timing:
- ON for at most 300ms
- OFF for at most 0.2s
- ON for at most 300ms
- OFF for at most 0.2s
- ON for at most 300ms
- OFF for at least 0.2s
then:
lambda: |-
id(bar2_btn_left_event).trigger("triple_click page " + std::to_string(id(actual_page)));
- timing:
- ON for 1s to 5s
- OFF for at least 0.2s
then:
lambda: |-
id(bar2_btn_left_event).trigger("hold page " + std::to_string(id(actual_page)));
- id: !extend bar2_btn_right
filters:
- delayed_off: 10ms
internal: true
on_multi_click:
- timing:
- ON for at most 300ms
- OFF for at least 0.2s
then:
lambda: |-
id(bar2_btn_right_event).trigger("click page " + std::to_string(id(actual_page)));
- timing:
- ON for at most 300ms
- OFF for at most 0.2s
- ON for at most 300ms
- OFF for at least 0.2s
then:
lambda: |-
id(bar2_btn_right_event).trigger("double_click page " + std::to_string(id(actual_page)));
- timing:
- ON for at most 300ms
- OFF for at most 0.2s
- ON for at most 300ms
- OFF for at most 0.2s
- ON for at most 300ms
- OFF for at least 0.2s
then:
lambda: |-
id(bar2_btn_right_event).trigger("triple_click page " + std::to_string(id(actual_page)));
- timing:
- ON for 1s to 5s
- OFF for at least 0.2s
then:
lambda: |-
id(bar2_btn_right_event).trigger("hold page " + std::to_string(id(actual_page)));
- id: !extend bar3_btn_left
filters:
- delayed_off: 10ms
internal: true
on_multi_click:
- timing:
- ON for at most 300ms
- OFF for at least 0.2s
then:
lambda: |-
id(bar3_btn_left_event).trigger("click page " + std::to_string(id(actual_page)));
- timing:
- ON for at most 300ms
- OFF for at most 0.2s
- ON for at most 300ms
- OFF for at least 0.2s
then:
lambda: |-
id(bar3_btn_left_event).trigger("double_click page " + std::to_string(id(actual_page)));
- timing:
- ON for at most 300ms
- OFF for at most 0.2s
- ON for at most 300ms
- OFF for at most 0.2s
- ON for at most 300ms
- OFF for at least 0.2s
then:
lambda: |-
id(bar3_btn_left_event).trigger("triple_click page " + std::to_string(id(actual_page)));
- timing:
- ON for 1s to 5s
- OFF for at least 0.2s
then:
lambda: |-
id(bar3_btn_left_event).trigger("hold page " + std::to_string(id(actual_page)));
- id: !extend bar3_btn_right
filters:
- delayed_off: 10ms
internal: true
on_multi_click:
- timing:
- ON for at most 300ms
- OFF for at least 0.2s
then:
lambda: |-
id(bar3_btn_right_event).trigger("click page " + std::to_string(id(actual_page)));
- timing:
- ON for at most 300ms
- OFF for at most 0.2s
- ON for at most 300ms
- OFF for at least 0.2s
then:
lambda: |-
id(bar3_btn_right_event).trigger("double_click page " + std::to_string(id(actual_page)));
- timing:
- ON for at most 300ms
- OFF for at most 0.2s
- ON for at most 300ms
- OFF for at most 0.2s
- ON for at most 300ms
- OFF for at least 0.2s
then:
lambda: |-
id(bar3_btn_right_event).trigger("triple_click page " + std::to_string(id(actual_page)));
- timing:
- ON for 1s to 5s
- OFF for at least 0.2s
then:
lambda: |-
id(bar3_btn_right_event).trigger("hold page " + std::to_string(id(actual_page)));
+162
View File
@@ -0,0 +1,162 @@
import yaml
# CONFIGURATION
NR_OF_PAGES = 3
INCLUDE_DISPLAY_EXTRA = True # Enable Double, Triple, Hold for main display buttons
LED_CONFIRMATION = True # Flash LED on press
FILENAME = "3bar_1display_events.yaml"
# --- ESPHome YAML Handling Fixes ---
class ESPHomeExtend:
def __init__(self, value): self.value = value
class ESPHomeLambda:
def __init__(self, value): self.value = value
class LiteralString(str): pass
def represent_extend(dumper, data):
return dumper.represent_scalar('!extend', data.value)
def represent_lambda(dumper, data):
# Ensures !lambda is followed by a quoted string or block for ESPHome compatibility
return dumper.represent_scalar('!lambda', data.value, style='"')
def represent_literal(dumper, data):
return dumper.represent_scalar('tag:yaml.org,2002:str', data, style='|')
yaml.add_representer(ESPHomeExtend, represent_extend)
yaml.add_representer(ESPHomeLambda, represent_lambda)
yaml.add_representer(LiteralString, represent_literal)
# -----------------------------------
def generate():
# 1. Define event lists for substitutions
base_types = ["click", "double_click", "triple_click", "hold"]
display_types = [] + (base_types[1:] if INCLUDE_DISPLAY_EXTRA else [])
display_events = []
button_events = []
for p in range(NR_OF_PAGES):
display_events.extend([f"{t} page {p}" for t in display_types])
button_events.extend([f"{t} page {p}" for t in base_types])
data = {
"substitutions": {
"nr_of_pages": str(NR_OF_PAGES),
"display_events": display_events,
"button_events": button_events
},
"script": [{
"id": "trigger_with_feedback",
"parameters": {
"event_id": "esphome::template_::TemplateEvent*",
"event_msg": "std::string",
"led_id": "light::LightState*",
"r": "float", "g": "float", "b": "float"
},
"then": [
{"lambda": "event_id->trigger(event_msg);"},
{"if": {
"condition": {"lambda": "return led_id != nullptr;"},
"then": [{"lambda": LiteralString(
"auto call = led_id->make_call();\n"
"call.set_state(true);\n"
"call.set_rgb(r, g, b);\n"
"call.set_brightness(1.0);\n"
"call.set_flash_length(200); // Returns to previous state automatically\n"
"call.perform();"
)}]
}}
]
}],
"event": []
}
# 2. Event Entities
buttons = [
("DISPLAY Button Left", "display_btn_left_event", "${display_events}"),
("DISPLAY Button Right", "display_btn_right_event", "${display_events}"),
("BAR1 Button Left", "bar1_btn_left_event", "${button_events}"),
("BAR1 Button Right", "bar1_btn_right_event", "${button_events}"),
("BAR2 Button Left", "bar2_btn_left_event", "${button_events}"),
("BAR2 Button Right", "bar2_btn_right_event", "${button_events}"),
("BAR3 Button Left", "bar3_btn_left_event", "${button_events}"),
("BAR3 Button Right", "bar3_btn_right_event", "${button_events}"),
]
for name, eid, types in buttons:
data["event"].append({
"platform": "template", "name": name, "id": eid,
"device_class": "button", "event_types": types
})
# 3. Binary Sensor Extensions
data["binary_sensor"] = []
triggers = {
"click": (0.0, 1.0, 0.0), # Green
"double_click": (0.0, 0.0, 1.0), # Blue
"triple_click": (1.0, 0.0, 1.0), # Purple
"hold": (1.0, 0.5, 0.0) # Orange
}
btn_configs = [
("main_display_btn_left", "display_btn_left_event", None, "prev_page"),
("main_display_btn_right", "display_btn_right_event", None, "next_page"),
("bar1_btn_left", "bar1_btn_left_event", "bar1_left_rgb_front", None),
("bar1_btn_right", "bar1_btn_right_event", "bar1_right_rgb_front", None),
("bar2_btn_left", "bar2_btn_left_event", "bar2_left_rgb_front", None),
("bar2_btn_right", "bar2_btn_right_event", "bar2_right_rgb_front", None),
("bar3_btn_left", "bar3_btn_left_event", "bar3_left_rgb_front", None),
("bar3_btn_right", "bar3_btn_right_event", "bar3_right_rgb_front", None),
]
for btn_id, event_id, led_id, page_script in btn_configs:
sensor = {
"id": ESPHomeExtend(btn_id),
"filters": [{"delayed_off": "10ms"}],
"internal": True,
"on_multi_click": []
}
is_display = "main_display" in btn_id
for t_name, color in triggers.items():
if is_display and t_name != "click" and not INCLUDE_DISPLAY_EXTRA:
continue
multi_click = {"timing": []}
if t_name == "click":
multi_click["timing"] = ["ON for at most 300ms", "OFF for at least 0.2s"]
elif t_name == "double_click":
multi_click["timing"] = ["ON for at most 300ms", "OFF for at most 0.2s", "ON for at most 300ms", "OFF for at least 0.2s"]
elif t_name == "triple_click":
multi_click["timing"] = ["ON for at most 300ms", "OFF for at most 0.2s", "ON for at most 300ms", "OFF for at most 0.2s", "ON for at most 300ms", "OFF for at least 0.2s"]
elif t_name == "hold":
multi_click["timing"] = ["ON for at least 1s", "OFF for at least 0.2s"]
actions = []
if t_name == "click" and page_script:
actions.append({"script.execute": page_script})
else:
# The Feedback call with proper ESPHome formatting
actions.append({
"script.execute": {
"id": "trigger_with_feedback",
"event_id": event_id,
"event_msg": ESPHomeLambda(f'return "{t_name} page " + std::to_string(id(actual_page));'),
"led_id": led_id if (led_id and LED_CONFIRMATION) else ESPHomeLambda("return nullptr;"),
"r": color[0], "g": color[1], "b": color[2]
}
})
multi_click["then"] = actions
sensor["on_multi_click"].append(multi_click)
data["binary_sensor"].append(sensor)
# 4. Write output
with open(FILENAME, "w") as f:
f.write("## Dynamically generated event configuration\n")
f.write("## Do not edit manually - run generator script to update\n\n")
yaml.dump(data, f, sort_keys=False, default_flow_style=False)
print(f"Generated {FILENAME} successfully.")
if __name__ == "__main__":
generate()
-167
View File
@@ -1,167 +0,0 @@
############################################
## ##
## filename: 1bar_1display.yaml ##
## description: Specific configuration ##
## for the 1 BAR 1 Display setup ##
## ##
############################################
packages:
base: !include base.yaml
scripts: !include 1bar_1display_scripts.yaml
esphome:
on_boot:
priority: -100
then:
- script.execute: setup_bar_displays
- script.execute: update_bar1_left
- script.execute: update_bar1_right
mcp23008:
- id: 'base_J2'
i2c_id: buttons
address: 0x22
- id: 'base_J3'
i2c_id: buttons
address: 0x23
binary_sensor:
############################
### Buttons Main Display ###
############################
- platform: gpio
id: 'main_display_btn_left'
name: "Main Display button Left"
pin:
mcp23xxx: base_J2
number: 6
mode: INPUT_PULLUP
inverted: true
- platform: gpio
id: 'main_display_btn_right'
name: "Main Display button Right"
pin:
mcp23xxx: base_J2
number: 2
mode: INPUT_PULLUP
inverted: true
############################
### Buttons BAR module 1 ###
############################
- platform: gpio
id: 'bar1_btn_left'
name: "BAR 1 button Left"
pin:
mcp23xxx: base_J3
number: 6
mode: INPUT_PULLUP
inverted: true
- platform: gpio
id: 'bar1_btn_right'
name: "BAR 1 button Right"
pin:
mcp23xxx: base_J3
number: 2
mode: INPUT_PULLUP
inverted: true
light:
- id: !extend neopixels
num_leds: 5
############################
### RGB LED's BAR module ###
############################
- platform: partition
name: "BAR 1 Left RGB Front"
id: bar1_left_rgb_front
segments:
- id: neopixels
from: 0
to: 0
- platform: partition
name: "BAR 1 Left RGB Back"
id: bar1_left_rgb_back
segments:
- id: neopixels
from: 1
to: 1
- platform: partition
name: "BAR 1 Right RGB Front"
id: bar1_right_rgb_front
segments:
- id: neopixels
from: 2
to: 2
- platform: partition
name: "BAR 1 Right RGB Back"
id: bar1_right_rgb_back
segments:
- id: neopixels
from: 3
to: 3
output:
#######################################
### CS pins to control BAR Displays ###
#######################################
- id: cs_pin_bar1_left
platform: gpio
pin:
mcp23xxx: base_J3
number: 5
mode:
output: true
inverted: true
- id: cs_pin_bar1_right
platform: gpio
pin:
mcp23xxx: base_J3
number: 1
mode:
output: true
inverted: true
display:
####################
### Main Display ###
####################
- id: main_display
platform: ili9xxx
model: ili9341
invert_colors: false
color_order: rgb
update_interval: 30s
dc_pin:
number: GPIO37
allow_other_uses: true
cs_pin:
mcp23xxx: base_J2
number: 5
mode:
output: true
inverted: false
show_test_card: false
dimensions:
height: 240
width: 320
rotation: 180
##################
### BAR module ###
##################
- id: bar_display
platform: ili9xxx
model: ST7735
color_order: bgr
update_interval: never
dc_pin:
number: GPIO37
allow_other_uses: true
invert_colors: false
show_test_card: false
auto_clear_enabled: false
dimensions:
height: 160
width: 80
offset_width: 24
-68
View File
@@ -1,68 +0,0 @@
##############################################
## ##
## filename: 1bar_1display_scripts.yaml ##
## description: Specific pre-defined ##
## scripts for 1 BAR 1 Display setup ##
## ##
##############################################
script:
######################
### update scripts ###
######################
- id: update_bar1_left
then:
- lambda: |-
id(cs_pin_bar1_left).turn_on(); // <<
id(cs_pin_bar1_right).turn_off();
id(bar_display).clear();
id(bar_display).set_rotation(display::DisplayRotation::DISPLAY_ROTATION_270_DEGREES);
id(bar1_left_draw).execute();
id(bar_display).update();
delay(2);
id(cs_pin_bar1_left).turn_off();
- id: update_bar1_right
then:
- lambda: |-
id(cs_pin_bar1_left).turn_off();
id(cs_pin_bar1_right).turn_on(); // <<
id(bar_display).clear();
id(bar_display).set_rotation(display::DisplayRotation::DISPLAY_ROTATION_90_DEGREES);
id(bar1_right_draw).execute();
id(bar_display).update();
delay(2);
id(cs_pin_bar1_right).turn_off();
##################################
### setup mini display scripts ###
##################################
- id: setup_bar_displays
then:
- lambda: |-
id(cs_pin_bar1_left).turn_on();
id(bar_display).setup();
id(cs_pin_bar1_left).turn_off();
delay(5);
id(cs_pin_bar1_right).turn_on();
id(bar_display).setup();
id(cs_pin_bar1_right).turn_off();
####################
### draw scripts ###
####################
- id: bar1_left_draw
then:
- id: bar1_right_draw
then:
##############################
### other handling scripts ###
##############################
- id: update_main
then:
- component.update: main_display
- id: update_all
then:
- component.update: main_display
- delay: 0.1s
- script.execute: update_bar1_left
- delay: 0.1s
- script.execute: update_bar1_right
- delay: 0.1s
-239
View File
@@ -1,239 +0,0 @@
############################################
## ##
## filename: 2bar_1display.yaml ##
## description: Specific configuration ##
## for the 2 BAR's, 1 Display setup ##
## ##
############################################
packages:
base: !include base.yaml
scripts: !include 2bar_1display_scripts.yaml
esphome:
on_boot:
priority: -100
then:
- script.execute: setup_bar_displays
- script.execute: update_bar1_left
- script.execute: update_bar1_right
- script.execute: update_bar2_left
- script.execute: update_bar2_right
mcp23008:
- id: 'base_J1'
i2c_id: buttons
address: 0x21
- id: 'base_J2'
i2c_id: buttons
address: 0x22
- id: 'base_J3'
i2c_id: buttons
address: 0x23
binary_sensor:
############################
### Buttons Main Display ###
############################
- platform: gpio
id: 'main_display_btn_left'
name: "Main Display button Left"
pin:
mcp23xxx: base_J1
number: 6
mode: INPUT_PULLUP
inverted: true
- platform: gpio
id: 'main_display_btn_right'
name: "Main Display button Right"
pin:
mcp23xxx: base_J1
number: 2
mode: INPUT_PULLUP
inverted: true
############################
### Buttons BAR module 1 ###
############################
- platform: gpio
id: 'bar1_btn_left'
name: "BAR 1 button Left"
pin:
mcp23xxx: base_J2
number: 6
mode: INPUT_PULLUP
inverted: true
- platform: gpio
id: 'bar1_btn_right'
name: "BAR 1 button Right"
pin:
mcp23xxx: base_J2
number: 2
mode: INPUT_PULLUP
inverted: true
############################
### Buttons BAR module 2 ###
############################
- platform: gpio
id: 'bar2_btn_left'
name: "BAR 2 button Left"
pin:
mcp23xxx: base_J3
number: 6
mode: INPUT_PULLUP
inverted: true
- platform: gpio
id: 'bar2_btn_right'
name: "BAR 2 button Right"
pin:
mcp23xxx: base_J3
number: 2
mode: INPUT_PULLUP
inverted: true
light:
- id: !extend neopixels
num_leds: 9
##############################
### RGB LED's BAR module 1 ###
##############################
- platform: partition
name: "BAR 1 Left RGB Front"
id: bar1_left_rgb_front
segments:
- id: neopixels
from: 0
to: 0
- platform: partition
name: "BAR 1 Left RGB Back"
id: bar1_left_rgb_back
segments:
- id: neopixels
from: 1
to: 1
- platform: partition
name: "BAR 1 Right RGB Front"
id: bar1_right_rgb_front
segments:
- id: neopixels
from: 2
to: 2
- platform: partition
name: "BAR 1 Right RGB Back"
id: bar1_right_rgb_back
segments:
- id: neopixels
from: 3
to: 3
##############################
### RGB LED's BAR module 2 ###
##############################
- platform: partition
name: "BAR 2 Left RGB Front"
id: bar2_left_rgb_front
segments:
- id: neopixels
from: 4
to: 4
- platform: partition
name: "BAR 2 Left RGB Back"
id: bar2_left_rgb_back
segments:
- id: neopixels
from: 5
to: 5
- platform: partition
name: "BAR 2 Right RGB Front"
id: bar2_right_rgb_front
segments:
- id: neopixels
from: 6
to: 6
- platform: partition
name: "BAR 2 Right RGB Back"
id: bar2_right_rgb_back
segments:
- id: neopixels
from: 7
to: 7
output:
#######################################
### CS pins to control BAR Displays ###
#######################################
- id: cs_pin_bar1_left
platform: gpio
pin:
mcp23xxx: base_J2
number: 5
mode:
output: true
inverted: true
- id: cs_pin_bar1_right
platform: gpio
pin:
mcp23xxx: base_J2
number: 1
mode:
output: true
inverted: true
- id: cs_pin_bar2_left
platform: gpio
pin:
mcp23xxx: base_J3
number: 5
mode:
output: true
inverted: true
- id: cs_pin_bar2_right
platform: gpio
pin:
mcp23xxx: base_J3
number: 1
mode:
output: true
inverted: true
display:
####################
### Main Display ###
####################
- id: main_display
platform: ili9xxx
model: ili9341
invert_colors: false
color_order: rgb
update_interval: 30s
dc_pin:
number: GPIO37
allow_other_uses: true
cs_pin:
mcp23xxx: base_J1
number: 5
mode:
output: true
inverted: false
show_test_card: false
dimensions:
height: 240
width: 320
rotation: 180
###################
### BAR modules ###
###################
- id: bar_display
platform: ili9xxx
model: ST7735
color_order: bgr
update_interval: never
dc_pin:
number: GPIO37
allow_other_uses: true
invert_colors: false
show_test_card: false
auto_clear_enabled: false
dimensions:
height: 160
width: 80
offset_width: 24
-114
View File
@@ -1,114 +0,0 @@
##############################################
## ##
## filename: 1bar_1display_scripts.yaml ##
## description: Specific pre-defined ##
## scripts for 2 BAR 1 Display setup ##
## ##
##############################################
script:
######################
### update scripts ###
######################
- id: update_bar1_left
then:
- lambda: |-
id(cs_pin_bar1_left).turn_on(); // <<
id(cs_pin_bar1_right).turn_off();
id(cs_pin_bar2_left).turn_off();
id(cs_pin_bar2_right).turn_off();
id(bar_display).clear();
id(bar_display).set_rotation(display::DisplayRotation::DISPLAY_ROTATION_270_DEGREES);
id(bar1_left_draw).execute();
id(bar_display).update();
delay(1);
id(cs_pin_bar1_left).turn_off();
- id: update_bar1_right
then:
- lambda: |-
id(cs_pin_bar1_left).turn_off();
id(cs_pin_bar1_right).turn_on(); // <<
id(cs_pin_bar2_left).turn_off();
id(cs_pin_bar2_right).turn_off();
id(bar_display).clear();
id(bar_display).set_rotation(display::DisplayRotation::DISPLAY_ROTATION_90_DEGREES);
id(bar1_right_draw).execute();
id(bar_display).update();
delay(1);
id(cs_pin_bar1_right).turn_off();
- id: update_bar2_left
then:
- lambda: |-
id(cs_pin_bar1_left).turn_off();
id(cs_pin_bar1_right).turn_off();
id(cs_pin_bar2_left).turn_on(); // <<
id(cs_pin_bar2_right).turn_off();
id(bar_display).clear();
id(bar_display).set_rotation(display::DisplayRotation::DISPLAY_ROTATION_270_DEGREES);
id(bar2_left_draw).execute();
id(bar_display).update();
delay(1);
id(cs_pin_bar2_left).turn_off();
- id: update_bar2_right
then:
- lambda: |-
id(cs_pin_bar1_left).turn_off();
id(cs_pin_bar1_right).turn_off();
id(cs_pin_bar2_left).turn_off();
id(cs_pin_bar2_right).turn_on(); // <<
id(bar_display).clear();
id(bar_display).set_rotation(display::DisplayRotation::DISPLAY_ROTATION_90_DEGREES);
id(bar2_right_draw).execute();
id(bar_display).update();
delay(1);
id(cs_pin_bar2_right).turn_off();
##################################
### setup mini display scripts ###
##################################
- id: setup_bar_displays
then:
- lambda: |-
id(cs_pin_bar1_left).turn_on();
id(bar_display).setup();
id(cs_pin_bar1_left).turn_off();
delay(5);
id(cs_pin_bar1_right).turn_on();
id(bar_display).setup();
id(cs_pin_bar1_right).turn_off();
delay(5);
id(cs_pin_bar2_left).turn_on();
id(bar_display).setup();
id(cs_pin_bar2_left).turn_off();
delay(5);
id(cs_pin_bar2_right).turn_on();
id(bar_display).setup();
id(cs_pin_bar2_right).turn_off();
####################
### draw scripts ###
####################
- id: bar1_left_draw
then:
- id: bar1_right_draw
then:
- id: bar2_left_draw
then:
- id: bar2_right_draw
then:
##############################
### other handling scripts ###
##############################
- id: update_main
then:
- component.update: main_display
- id: update_all
then:
- component.update: main_display
- delay: 0.1s
- script.execute: update_bar1_left
- delay: 0.1s
- script.execute: update_bar1_right
- delay: 0.1s
- script.execute: update_bar2_left
- delay: 0.1s
- script.execute: update_bar2_right
- delay: 0.1s
+3 -2
View File
@@ -9,6 +9,7 @@
packages:
base: !include base.yaml
scripts: !include 3bar_1display_scripts.yaml
events: !include 3bar_1display_events.yaml
esphome:
on_boot:
@@ -278,7 +279,7 @@ display:
color_order: rgb
update_interval: 30s
dc_pin:
number: GPIO37
number: GPIO7
allow_other_uses: true
cs_pin:
mcp23xxx: base_J0
@@ -300,7 +301,7 @@ display:
color_order: bgr
update_interval: never
dc_pin:
number: GPIO37
number: GPIO7
allow_other_uses: true
invert_colors: false
show_test_card: false
+550
View File
@@ -0,0 +1,550 @@
## Dynamically generated event configuration
## Do not edit manually - run generator script to update
substitutions:
nr_of_pages: '3'
display_events:
- double_click page 0
- triple_click page 0
- hold page 0
- double_click page 1
- triple_click page 1
- hold page 1
- double_click page 2
- triple_click page 2
- hold page 2
button_events:
- click page 0
- double_click page 0
- triple_click page 0
- hold page 0
- click page 1
- double_click page 1
- triple_click page 1
- hold page 1
- click page 2
- double_click page 2
- triple_click page 2
- hold page 2
script:
- id: trigger_with_feedback
parameters:
event_id: esphome::template_::TemplateEvent*
event_msg: std::string
led_id: light::LightState*
r: float
g: float
b: float
then:
- lambda: event_id->trigger(event_msg);
- if:
condition:
lambda: return led_id != nullptr;
then:
- lambda: |-
auto call = led_id->make_call();
call.set_state(true);
call.set_rgb(r, g, b);
call.set_brightness(1.0);
call.set_flash_length(200); // Returns to previous state automatically
call.perform();
event:
- platform: template
name: DISPLAY Button Left
id: display_btn_left_event
device_class: button
event_types: ${display_events}
- platform: template
name: DISPLAY Button Right
id: display_btn_right_event
device_class: button
event_types: ${display_events}
- platform: template
name: BAR1 Button Left
id: bar1_btn_left_event
device_class: button
event_types: ${button_events}
- platform: template
name: BAR1 Button Right
id: bar1_btn_right_event
device_class: button
event_types: ${button_events}
- platform: template
name: BAR2 Button Left
id: bar2_btn_left_event
device_class: button
event_types: ${button_events}
- platform: template
name: BAR2 Button Right
id: bar2_btn_right_event
device_class: button
event_types: ${button_events}
- platform: template
name: BAR3 Button Left
id: bar3_btn_left_event
device_class: button
event_types: ${button_events}
- platform: template
name: BAR3 Button Right
id: bar3_btn_right_event
device_class: button
event_types: ${button_events}
binary_sensor:
- id: !extend 'main_display_btn_left'
filters:
- delayed_off: 10ms
internal: true
on_multi_click:
- timing:
- ON for at most 300ms
- OFF for at least 0.2s
then:
- script.execute: prev_page
- timing:
- ON for at most 300ms
- OFF for at most 0.2s
- ON for at most 300ms
- OFF for at least 0.2s
then:
- script.execute:
id: trigger_with_feedback
event_id: display_btn_left_event
event_msg: !lambda "return \"double_click page \" + std::to_string(id(actual_page));"
led_id: !lambda "return nullptr;"
r: 0.0
g: 0.0
b: 1.0
- timing:
- ON for at most 300ms
- OFF for at most 0.2s
- ON for at most 300ms
- OFF for at most 0.2s
- ON for at most 300ms
- OFF for at least 0.2s
then:
- script.execute:
id: trigger_with_feedback
event_id: display_btn_left_event
event_msg: !lambda "return \"triple_click page \" + std::to_string(id(actual_page));"
led_id: !lambda "return nullptr;"
r: 1.0
g: 0.0
b: 1.0
- timing:
- ON for at least 1s
- OFF for at least 0.2s
then:
- script.execute:
id: trigger_with_feedback
event_id: display_btn_left_event
event_msg: !lambda "return \"hold page \" + std::to_string(id(actual_page));"
led_id: !lambda "return nullptr;"
r: 1.0
g: 0.5
b: 0.0
- id: !extend 'main_display_btn_right'
filters:
- delayed_off: 10ms
internal: true
on_multi_click:
- timing:
- ON for at most 300ms
- OFF for at least 0.2s
then:
- script.execute: next_page
- timing:
- ON for at most 300ms
- OFF for at most 0.2s
- ON for at most 300ms
- OFF for at least 0.2s
then:
- script.execute:
id: trigger_with_feedback
event_id: display_btn_right_event
event_msg: !lambda "return \"double_click page \" + std::to_string(id(actual_page));"
led_id: !lambda "return nullptr;"
r: 0.0
g: 0.0
b: 1.0
- timing:
- ON for at most 300ms
- OFF for at most 0.2s
- ON for at most 300ms
- OFF for at most 0.2s
- ON for at most 300ms
- OFF for at least 0.2s
then:
- script.execute:
id: trigger_with_feedback
event_id: display_btn_right_event
event_msg: !lambda "return \"triple_click page \" + std::to_string(id(actual_page));"
led_id: !lambda "return nullptr;"
r: 1.0
g: 0.0
b: 1.0
- timing:
- ON for at least 1s
- OFF for at least 0.2s
then:
- script.execute:
id: trigger_with_feedback
event_id: display_btn_right_event
event_msg: !lambda "return \"hold page \" + std::to_string(id(actual_page));"
led_id: !lambda "return nullptr;"
r: 1.0
g: 0.5
b: 0.0
- id: !extend 'bar1_btn_left'
filters:
- delayed_off: 10ms
internal: true
on_multi_click:
- timing:
- ON for at most 300ms
- OFF for at least 0.2s
then:
- script.execute:
id: trigger_with_feedback
event_id: bar1_btn_left_event
event_msg: !lambda "return \"click page \" + std::to_string(id(actual_page));"
led_id: bar1_left_rgb_front
r: 0.0
g: 1.0
b: 0.0
- timing:
- ON for at most 300ms
- OFF for at most 0.2s
- ON for at most 300ms
- OFF for at least 0.2s
then:
- script.execute:
id: trigger_with_feedback
event_id: bar1_btn_left_event
event_msg: !lambda "return \"double_click page \" + std::to_string(id(actual_page));"
led_id: bar1_left_rgb_front
r: 0.0
g: 0.0
b: 1.0
- timing:
- ON for at most 300ms
- OFF for at most 0.2s
- ON for at most 300ms
- OFF for at most 0.2s
- ON for at most 300ms
- OFF for at least 0.2s
then:
- script.execute:
id: trigger_with_feedback
event_id: bar1_btn_left_event
event_msg: !lambda "return \"triple_click page \" + std::to_string(id(actual_page));"
led_id: bar1_left_rgb_front
r: 1.0
g: 0.0
b: 1.0
- timing:
- ON for at least 1s
- OFF for at least 0.2s
then:
- script.execute:
id: trigger_with_feedback
event_id: bar1_btn_left_event
event_msg: !lambda "return \"hold page \" + std::to_string(id(actual_page));"
led_id: bar1_left_rgb_front
r: 1.0
g: 0.5
b: 0.0
- id: !extend 'bar1_btn_right'
filters:
- delayed_off: 10ms
internal: true
on_multi_click:
- timing:
- ON for at most 300ms
- OFF for at least 0.2s
then:
- script.execute:
id: trigger_with_feedback
event_id: bar1_btn_right_event
event_msg: !lambda "return \"click page \" + std::to_string(id(actual_page));"
led_id: bar1_right_rgb_front
r: 0.0
g: 1.0
b: 0.0
- timing:
- ON for at most 300ms
- OFF for at most 0.2s
- ON for at most 300ms
- OFF for at least 0.2s
then:
- script.execute:
id: trigger_with_feedback
event_id: bar1_btn_right_event
event_msg: !lambda "return \"double_click page \" + std::to_string(id(actual_page));"
led_id: bar1_right_rgb_front
r: 0.0
g: 0.0
b: 1.0
- timing:
- ON for at most 300ms
- OFF for at most 0.2s
- ON for at most 300ms
- OFF for at most 0.2s
- ON for at most 300ms
- OFF for at least 0.2s
then:
- script.execute:
id: trigger_with_feedback
event_id: bar1_btn_right_event
event_msg: !lambda "return \"triple_click page \" + std::to_string(id(actual_page));"
led_id: bar1_right_rgb_front
r: 1.0
g: 0.0
b: 1.0
- timing:
- ON for at least 1s
- OFF for at least 0.2s
then:
- script.execute:
id: trigger_with_feedback
event_id: bar1_btn_right_event
event_msg: !lambda "return \"hold page \" + std::to_string(id(actual_page));"
led_id: bar1_right_rgb_front
r: 1.0
g: 0.5
b: 0.0
- id: !extend 'bar2_btn_left'
filters:
- delayed_off: 10ms
internal: true
on_multi_click:
- timing:
- ON for at most 300ms
- OFF for at least 0.2s
then:
- script.execute:
id: trigger_with_feedback
event_id: bar2_btn_left_event
event_msg: !lambda "return \"click page \" + std::to_string(id(actual_page));"
led_id: bar2_left_rgb_front
r: 0.0
g: 1.0
b: 0.0
- timing:
- ON for at most 300ms
- OFF for at most 0.2s
- ON for at most 300ms
- OFF for at least 0.2s
then:
- script.execute:
id: trigger_with_feedback
event_id: bar2_btn_left_event
event_msg: !lambda "return \"double_click page \" + std::to_string(id(actual_page));"
led_id: bar2_left_rgb_front
r: 0.0
g: 0.0
b: 1.0
- timing:
- ON for at most 300ms
- OFF for at most 0.2s
- ON for at most 300ms
- OFF for at most 0.2s
- ON for at most 300ms
- OFF for at least 0.2s
then:
- script.execute:
id: trigger_with_feedback
event_id: bar2_btn_left_event
event_msg: !lambda "return \"triple_click page \" + std::to_string(id(actual_page));"
led_id: bar2_left_rgb_front
r: 1.0
g: 0.0
b: 1.0
- timing:
- ON for at least 1s
- OFF for at least 0.2s
then:
- script.execute:
id: trigger_with_feedback
event_id: bar2_btn_left_event
event_msg: !lambda "return \"hold page \" + std::to_string(id(actual_page));"
led_id: bar2_left_rgb_front
r: 1.0
g: 0.5
b: 0.0
- id: !extend 'bar2_btn_right'
filters:
- delayed_off: 10ms
internal: true
on_multi_click:
- timing:
- ON for at most 300ms
- OFF for at least 0.2s
then:
- script.execute:
id: trigger_with_feedback
event_id: bar2_btn_right_event
event_msg: !lambda "return \"click page \" + std::to_string(id(actual_page));"
led_id: bar2_right_rgb_front
r: 0.0
g: 1.0
b: 0.0
- timing:
- ON for at most 300ms
- OFF for at most 0.2s
- ON for at most 300ms
- OFF for at least 0.2s
then:
- script.execute:
id: trigger_with_feedback
event_id: bar2_btn_right_event
event_msg: !lambda "return \"double_click page \" + std::to_string(id(actual_page));"
led_id: bar2_right_rgb_front
r: 0.0
g: 0.0
b: 1.0
- timing:
- ON for at most 300ms
- OFF for at most 0.2s
- ON for at most 300ms
- OFF for at most 0.2s
- ON for at most 300ms
- OFF for at least 0.2s
then:
- script.execute:
id: trigger_with_feedback
event_id: bar2_btn_right_event
event_msg: !lambda "return \"triple_click page \" + std::to_string(id(actual_page));"
led_id: bar2_right_rgb_front
r: 1.0
g: 0.0
b: 1.0
- timing:
- ON for at least 1s
- OFF for at least 0.2s
then:
- script.execute:
id: trigger_with_feedback
event_id: bar2_btn_right_event
event_msg: !lambda "return \"hold page \" + std::to_string(id(actual_page));"
led_id: bar2_right_rgb_front
r: 1.0
g: 0.5
b: 0.0
- id: !extend 'bar3_btn_left'
filters:
- delayed_off: 10ms
internal: true
on_multi_click:
- timing:
- ON for at most 300ms
- OFF for at least 0.2s
then:
- script.execute:
id: trigger_with_feedback
event_id: bar3_btn_left_event
event_msg: !lambda "return \"click page \" + std::to_string(id(actual_page));"
led_id: bar3_left_rgb_front
r: 0.0
g: 1.0
b: 0.0
- timing:
- ON for at most 300ms
- OFF for at most 0.2s
- ON for at most 300ms
- OFF for at least 0.2s
then:
- script.execute:
id: trigger_with_feedback
event_id: bar3_btn_left_event
event_msg: !lambda "return \"double_click page \" + std::to_string(id(actual_page));"
led_id: bar3_left_rgb_front
r: 0.0
g: 0.0
b: 1.0
- timing:
- ON for at most 300ms
- OFF for at most 0.2s
- ON for at most 300ms
- OFF for at most 0.2s
- ON for at most 300ms
- OFF for at least 0.2s
then:
- script.execute:
id: trigger_with_feedback
event_id: bar3_btn_left_event
event_msg: !lambda "return \"triple_click page \" + std::to_string(id(actual_page));"
led_id: bar3_left_rgb_front
r: 1.0
g: 0.0
b: 1.0
- timing:
- ON for at least 1s
- OFF for at least 0.2s
then:
- script.execute:
id: trigger_with_feedback
event_id: bar3_btn_left_event
event_msg: !lambda "return \"hold page \" + std::to_string(id(actual_page));"
led_id: bar3_left_rgb_front
r: 1.0
g: 0.5
b: 0.0
- id: !extend 'bar3_btn_right'
filters:
- delayed_off: 10ms
internal: true
on_multi_click:
- timing:
- ON for at most 300ms
- OFF for at least 0.2s
then:
- script.execute:
id: trigger_with_feedback
event_id: bar3_btn_right_event
event_msg: !lambda "return \"click page \" + std::to_string(id(actual_page));"
led_id: bar3_right_rgb_front
r: 0.0
g: 1.0
b: 0.0
- timing:
- ON for at most 300ms
- OFF for at most 0.2s
- ON for at most 300ms
- OFF for at least 0.2s
then:
- script.execute:
id: trigger_with_feedback
event_id: bar3_btn_right_event
event_msg: !lambda "return \"double_click page \" + std::to_string(id(actual_page));"
led_id: bar3_right_rgb_front
r: 0.0
g: 0.0
b: 1.0
- timing:
- ON for at most 300ms
- OFF for at most 0.2s
- ON for at most 300ms
- OFF for at most 0.2s
- ON for at most 300ms
- OFF for at least 0.2s
then:
- script.execute:
id: trigger_with_feedback
event_id: bar3_btn_right_event
event_msg: !lambda "return \"triple_click page \" + std::to_string(id(actual_page));"
led_id: bar3_right_rgb_front
r: 1.0
g: 0.0
b: 1.0
- timing:
- ON for at least 1s
- OFF for at least 0.2s
then:
- script.execute:
id: trigger_with_feedback
event_id: bar3_btn_right_event
event_msg: !lambda "return \"hold page \" + std::to_string(id(actual_page));"
led_id: bar3_right_rgb_front
r: 1.0
g: 0.5
b: 0.0
+81 -20
View File
@@ -10,31 +10,49 @@
esp32:
flash_size: 16MB
psram:
mode: octal
speed: 80MHz
interval:
- interval: 1min
then:
- logger.log: "ESPHome for Button+ v0.1.1"
- logger.log: "ESPHome for Button+ V2 v0.1.0"
i2c:
- id: temperature_sensor
# Devices at 0x20, 0x21, 0x22, 0x23
- id: buttons
sda: GPIO1
scl: GPIO2
- id: buttons
frequency: 50khz
# Devices at 0x29 (ambient), 0x4A (temp)
- id: sensors
sda: GPIO47
scl: GPIO48
frequency: 400khz
frequency: 50khz
spi:
clk_pin: GPIO36
mosi_pin: GPIO35
clk_pin: GPIO6
mosi_pin: GPIO5
substitutions:
nr_of_pages: '"2"'
page_names: '{"Page 1", "Page 2"}'
globals:
- id: nr_of_pages
type: int
initial_value: '1'
initial_value: ${nr_of_pages}
- id: actual_page
type: int
initial_value: '0'
- id: page_names
type: std::vector<std::string>
initial_value: ${page_names}
- id: active_page_name
type: std::string
initial_value: ""
output:
- platform: ledc
@@ -52,27 +70,67 @@ output:
sensor:
- platform: sts3x
id: inside_temperature
i2c_id: temperature_sensor
name: "Temperature"
address: 0x4A
id: buttonplus_temperature
i2c_id: sensors
name: "Temperature"
update_interval: 10s
unit_of_measurement: "°C"
icon: "mdi:thermometer"
device_class: "temperature"
state_class: "measurement"
accuracy_decimals: 1
- platform: ltr_als_ps
address: 0x29
i2c_id: sensors
update_interval: 10s
type: ALS
auto_mode: false
gain: 48x
ambient_light:
name: "Ambient Light"
id: buttonplus_ambientlight
unit_of_measurement: "lx"
icon: "mdi:brightness-6"
device_class: "illuminance"
state_class: "measurement"
accuracy_decimals: 1
infrared_counts:
name: "Infrared Counts"
id: buttonplus_infraredcounts
unit_of_measurement: "#"
icon: "mdi:brightness-5"
accuracy_decimals: 0
full_spectrum_counts :
name: "Full Spectrum Counts"
id: buttonplus_fullspectrumcounts
unit_of_measurement: "#"
icon: "mdi:brightness-7"
accuracy_decimals: 0
actual_gain:
name: "Actual Gain"
id: buttonplus_actualgain
icon: "mdi:multiplication"
accuracy_decimals: 0
- platform: template
name: "Active Page ID"
icon: "mdi:order-numeric-ascending"
id: active_page_id_ha
update_interval: never
accuracy_decimals: 0
text_sensor:
- platform: template
name: "Active Page Name"
icon: "mdi:order-alphabetical-ascending"
id: active_page_name_ha
update_interval: never
light:
- platform: neopixelbus
- platform: esp32_rmt_led_strip
id: neopixels
type: RGB
variant: WS2812
rgb_order: RGB
chipset: ws2812
pin: GPIO4
num_leds: 17
- platform: monochromatic
@@ -84,7 +142,7 @@ light:
- platform: monochromatic
output: displays_mini_backlight_pwm
gamma_correct : 2.2
name: "Mini Display Backlight"
name: "Bars Display Backlight"
id: displays_mini_backlight
restore_mode: ALWAYS_ON
@@ -99,8 +157,8 @@ number:
name: "Menu Selector"
id: menuselector
optimistic: true
min_value: 0 # keep at 0
max_value: 10 # TODO replace page numbers with substitution
min_value: 0
max_value: ${nr_of_pages | int - 1}
step: 1
on_value:
then:
@@ -110,8 +168,8 @@ number:
} else {
id(actual_page) = id(menuselector).state;
}
# # Refresh the screen
# - script.execute: update_page <<< this ends in an loop...
- logger.log: "Changing page"
- script.execute: set_page
script:
######################
@@ -133,7 +191,7 @@ script:
id(actual_page) = id(nr_of_pages) - 1;
}
- script.execute: update_page
- id: update_page
- id: set_page
then:
- lambda: |-
id(active_page_name) = id(page_names)[id(actual_page)];
@@ -143,8 +201,11 @@ script:
- text_sensor.template.publish:
id: active_page_name_ha
state: !lambda 'return id(active_page_name);'
- script.execute: update_all
- id: update_page
then:
- number.set:
id: menuselector
value: !lambda 'return id(actual_page);'
- script.execute: update_all
- script.execute: set_page
+1 -1
View File
@@ -8,7 +8,7 @@
music_card_enity_id: 'media_player.media_keuken_2'
package:
remote_package_files:
url: https://github.com/dixi83/ESPHome_ButtonPlus
url: https://gitea.furb.it/kennyboy55/ESPHome_ButtonPlusV2
files: [<other files>,'package/cards/music.yaml']
```
* Add the music card to the main display `lambda` and give it the position you want
+30 -12
View File
@@ -6,10 +6,10 @@
## players current state
## USAGE:
## substitutions:
## music_card_enity_id: 'media_player.media_keuken_2'
## music_card_entity_id: 'media_player.media_keuken_2'
## package:
## remote_package_files:
## url: https://github.com/dixi83/ESPHome_ButtonPlus
## url: https://gitea.furb.it/kennyboy55/ESPHome_ButtonPlusV2
## files: [<other files>,'package/cards/music.yaml']
##
## display:
@@ -27,57 +27,75 @@
image:
- file: mdi:music-box-outline
type: GRAYSCALE
id: icon_music_album
resize: 20x20
- file: mdi:account-music
type: GRAYSCALE
id: icon_music_artist
resize: 20x20
- file: mdi:music-note
type: GRAYSCALE
id: icon_music_songtitle
resize: 20x20
- file: mdi:power-off
type: GRAYSCALE
id: icon_music_off
resize: 75x75
- file: mdi:power-on
type: GRAYSCALE
id: icon_music_on
resize: 75x75
- file: mdi:power
type: GRAYSCALE
id: icon_music_power
resize: 75x75
- file: mdi:timer-sand-empty
type: GRAYSCALE
id: icon_music_idle
resize: 75x75
- file: mdi:play-pause
type: GRAYSCALE
id: icon_music_playpause
resize: 75x75
- file: mdi:play-box-outline
type: GRAYSCALE
id: icon_music_play_box
resize: 75x75
- file: mdi:pause-box-outline
type: GRAYSCALE
id: icon_music_pause_box
resize: 75x75
- file: mdi:power
type: GRAYSCALE
id: icon_music_standby
resize: 75x75
- file: mdi:download-box-outline
type: GRAYSCALE
id: icon_music_buffering
resize: 75x75
- file: mdi:volume-plus
type: GRAYSCALE
id: icon_music_vol_up
resize: 75x75
- file: mdi:volume-minus
type: GRAYSCALE
id: icon_music_vol_down
resize: 75x75
- file: mdi:skip-forward
type: GRAYSCALE
id: icon_music_next
resize: 75x75
- file: mdi:skip-backward
type: GRAYSCALE
id: icon_music_prev
resize: 75x75
- file: mdi:play
type: GRAYSCALE
id: icon_music_play
resize: 75x75
- file: mdi:pause
type: GRAYSCALE
id: icon_music_pause
resize: 75x75
@@ -92,25 +110,25 @@ color:
text_sensor:
- platform: homeassistant
id: music_album
entity_id: ${music_card_enity_id}
entity_id: ${music_card_entity_id}
attribute: media_album_name
on_value:
- delay: 500ms
- script.execute: update_main
- platform: homeassistant
id: music_artist
entity_id: ${music_card_enity_id}
entity_id: ${music_card_entity_id}
attribute: media_artist
- platform: homeassistant
id: music_title
entity_id: ${music_card_enity_id}
entity_id: ${music_card_entity_id}
attribute: media_title
on_value:
- delay: 500ms
- script.execute: update_main
- platform: homeassistant
id: music_playerstate
entity_id: ${music_card_enity_id}
entity_id: ${music_card_entity_id}
on_value:
- script.execute: update_all
@@ -176,34 +194,34 @@ script:
- homeassistant.action:
action: media_player.volume_up
data:
entity_id: ${music_card_enity_id}
entity_id: ${music_card_entity_id}
- id: music_card_vol_down
then:
- homeassistant.action:
action: media_player.volume_down
data:
entity_id: ${music_card_enity_id}
entity_id: ${music_card_entity_id}
- id: music_card_next
then:
- homeassistant.action:
action: media_player.media_next_track
data:
entity_id: ${music_card_enity_id}
entity_id: ${music_card_entity_id}
- id: music_card_prev
then:
- homeassistant.action:
action: media_player.media_previous_track
data:
entity_id: ${music_card_enity_id}
entity_id: ${music_card_entity_id}
- id: music_card_playpause
then:
- homeassistant.action:
action: media_player.media_play_pause
data:
entity_id: ${music_card_enity_id}
entity_id: ${music_card_entity_id}
- id: music_card_power
then:
- homeassistant.action:
action: media_player.toggle
data:
entity_id: ${music_card_enity_id}
entity_id: ${music_card_entity_id}