14 Commits
32 changed files with 1735 additions and 3059 deletions
+23 -1
View File
@@ -1 +1,23 @@
.vscode/settings.json
# === .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

-35
View File
@@ -1,35 +0,0 @@
# ESPHome Button+ Integration
*work in progress*
## Initial firmware load
### Required
- Button+ Base Module
- 1x Button+ Main Display
- 1x Button+ BAR module
- USB-C cable
- ESPHome installed on Home-Assistant
- [Getting started guide by @balk77](./GettingStarted.md)
- [Official ESPhome guide](https://esphome.io/guides/getting_started_hassio.html)
- Chrome/Edge or a [WebSerial compatible Browser](https://webserial.io/)
### Steps
1. Go to ESPHome in Home Assistant.
2. Click on the bottom right on "Add Device"
3. Click "Continue"
4. Give the device a name you can remember (e.g. "Button+ Kitchen") and click "Next"
5. Click "Skip This Step"
6. Select the "ESP32-S3"
7. Click "Skip"
8. On the ESPHome main screen find the device you've just created, on the device card click "Edit"
9. Find in the links below the initial script for your setup. Copy
- [1 BAR 1 Display](../Examples/1BAR_1Display_Basic.yaml)
- [2 BAR 1 Display](../Examples/2BAR_1Display_Basic.yaml)
- [3 BAR 1 Display](../Examples/3BAR_1Display_Basic.yaml)
10. Klik rechts boven in op "Save" en vervolgens op "Install"
11. Kies "Plug into this computer" en volg de instructies. Als je Firefox gebruikt kun je ook "Manual Download" kiezen en de image met ESP tool laden
Na de initiële upload met USB kun je de Button+ met de Wireless optie laden.
-15
View File
@@ -1,15 +0,0 @@
# 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
3. Create a file secrets.yaml and add the following
```
wifi_ssid: "YOUR_SSID"
wifi_password: "YOUR_PASSWORD"
```
5. modify the yml file to your liking (optional)
6. Compile (Install, manual download) and download in modern format
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
+27
View File
@@ -0,0 +1,27 @@
# 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 [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"
wifi_password: "YOUR_PASSWORD"
```
5. modify the yml file to your liking (optional)
6. Compile (Install, manual download) and download in modern format
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": {}
}
-183
View File
@@ -1,183 +0,0 @@
packages:
remote_package_files:
url: https://github.com/dixi83/ESPHome_ButtonPlus
files:
- 'package/1bar_1display.yaml'
ref: main
refresh: 1d
substitutions:
nr_of_pages: '1'
globals:
- id: !extend page_names
initial_value: '{"Page 1"}'
- id: !extend active_page_name
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: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;
}
-264
View File
@@ -1,264 +0,0 @@
packages:
remote_package_files:
url: https://github.com/dixi83/ESPHome_ButtonPlus
files:
- 'package/2bar_1display.yaml'
ref: main
refresh: 1d
substitutions:
nr_of_pages: '1'
globals:
- id: !extend page_names
initial_value: '{"Page 1"}'
- id: !extend active_page_name
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: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");
}
-343
View File
@@ -1,343 +0,0 @@
packages:
remote_package_files:
url: https://github.com/dixi83/ESPHome_ButtonPlus
files:
- 'package/3bar_1display.yaml'
ref: main
refresh: 1d
substitutions:
nr_of_pages: '1'
globals:
- id: !extend page_names
initial_value: '{"Page 1"}'
- id: !extend active_page_name
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: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;
}
-638
View File
@@ -1,638 +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:
nr_of_pages: '3'
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 page_names
initial_value: '{"page 2","Music","Page 3"}'
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 1"
- "release page 1"
- "long press page 1"
- "press page 2"
- "release page 2"
- "long press page 2"
- "press page 3"
- "release page 3"
- "long press page 3"
- platform: template
name: BAR1 button Right event
id: bar1_btn_right_event
event_types:
- "press page 1"
- "release page 1"
- "long press page 1"
- "press page 2"
- "release page 2"
- "long press page 2"
- "press page 3"
- "release page 3"
- "long press page 3"
- platform: template
name: BAR2 button Left event
id: bar2_btn_left_event
event_types:
- "press page 1"
- "release page 1"
- "long press page 1"
- "press page 2"
- "release page 2"
- "long press page 2"
- "press page 3"
- "release page 3"
- "long press page 3"
- platform: template
name: BAR2 button Right event
id: bar2_btn_right_event
event_types:
- "press page 1"
- "release page 1"
- "long press page 1"
- "press page 2"
- "release page 2"
- "long press page 2"
- "press page 3"
- "release page 3"
- "long press page 3"
- platform: template
name: BAR3 button Left event
id: bar3_btn_left_event
event_types:
- "press page 1"
- "release page 1"
- "long press page 1"
- "press page 2"
- "release page 2"
- "long press page 2"
- "press page 3"
- "release page 3"
- "long press page 3"
- platform: template
name: BAR3 button Right event
id: bar3_btn_right_event
event_types:
- "press page 1"
- "release page 1"
- "long press page 1"
- "press page 2"
- "release page 2"
- "long press page 2"
- "press page 3"
- "release page 3"
- "long press page 3"
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 1:
id(bar1_btn_left_event).trigger("press page 1");
break;
case 2:
id(bar1_btn_left_event).trigger("press page 2");
id(music_card_vol_down).execute();
break;
case 3:
id(bar1_btn_left_event).trigger("press page 3");
break;
}
on_release:
lambda: |-
switch (id(actual_page)){
case 1:
id(bar1_btn_left_event).trigger("release page 1");
break;
case 2:
id(bar1_btn_left_event).trigger("release page 2");
break;
case 3:
id(bar1_btn_left_event).trigger("release page 3");
break;
}
on_multi_click:
- timing:
- ON for at least 2s
then:
- lambda: |-
switch (id(actual_page)){
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;
case 3:
id(bar1_btn_left_event).trigger("long press page 3");
break;
}
- id: !extend bar1_btn_right
on_click:
lambda: |-
switch (id(actual_page)){
case 1:
id(bar1_btn_right_event).trigger("press page 1");
break;
case 2:
id(bar1_btn_right_event).trigger("press page 2");
id(music_card_vol_up).execute();
break;
case 3:
id(bar1_btn_right_event).trigger("press page 3");
break;
}
on_release:
lambda: |-
switch (id(actual_page)){
case 1:
id(bar1_btn_right_event).trigger("release page 1");
break;
case 2:
id(bar1_btn_right_event).trigger("release page 2");
break;
case 3:
id(bar1_btn_right_event).trigger("release page 3");
break;
}
on_multi_click:
- timing:
- ON for at least 2s
then:
- lambda: |-
switch (id(actual_page)){
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;
case 3:
id(bar1_btn_right_event).trigger("long press page 3");
break;
}
- id: !extend bar2_btn_left
on_click:
lambda: |-
switch (id(actual_page)){
case 1:
id(bar2_btn_left_event).trigger("press page 1");
break;
case 2:
id(bar2_btn_left_event).trigger("press page 2");
id(music_card_prev).execute();
break;
case 3:
id(bar2_btn_left_event).trigger("press page 3");
break;
}
on_release:
lambda: |-
switch (id(actual_page)){
case 1:
id(bar2_btn_left_event).trigger("release page 1");
break;
case 2:
id(bar2_btn_left_event).trigger("release page 2");
break;
case 3:
id(bar2_btn_left_event).trigger("release page 3");
break;
}
on_multi_click:
- timing:
- ON for at least 2s
then:
- lambda: |-
switch (id(actual_page)){
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;
case 3:
id(bar2_btn_left_event).trigger("long press page 3");
break;
}
- id: !extend bar2_btn_right
on_click:
lambda: |-
switch (id(actual_page)){
case 1:
id(bar2_btn_right_event).trigger("press page 1");
break;
case 2:
id(bar2_btn_right_event).trigger("press page 2");
id(music_card_next).execute();
break;
case 3:
id(bar2_btn_right_event).trigger("press page 3");
break;
}
on_release:
lambda: |-
switch (id(actual_page)){
case 1:
id(bar2_btn_right_event).trigger("release page 1");
break;
case 2:
id(bar2_btn_right_event).trigger("release page 2");
break;
case 3:
id(bar2_btn_right_event).trigger("release page 3");
break;
}
on_multi_click:
- timing:
- ON for at least 2s
then:
- lambda: |-
switch (id(actual_page)){
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;
case 3:
id(bar2_btn_right_event).trigger("long press page 3");
break;
}
- id: !extend bar3_btn_left
on_click:
lambda: |-
switch (id(actual_page)){
case 1:
id(bar3_btn_left_event).trigger("press page 1");
break;
case 2:
id(bar3_btn_left_event).trigger("press page 2");
id(music_card_power).execute();
break;
case 3:
id(bar3_btn_left_event).trigger("press page 3");
break;
}
on_release:
lambda: |-
switch (id(actual_page)){
case 1:
id(bar3_btn_left_event).trigger("release page 1");
break;
case 2:
id(bar3_btn_left_event).trigger("release page 2");
break;
case 3:
id(bar3_btn_left_event).trigger("release page 3");
break;
}
on_multi_click:
- timing:
- ON for at least 2s
then:
- lambda: |-
switch (id(actual_page)){
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;
case 3:
id(bar3_btn_left_event).trigger("long press page 3");
break;
}
- id: !extend bar3_btn_right
on_click:
lambda: |-
switch (id(actual_page)){
case 1:
id(bar3_btn_right_event).trigger("press page 1");
break;
case 2:
id(bar3_btn_right_event).trigger("press page 2");
id(music_card_playpause).execute();
break;
case 3:
id(bar3_btn_right_event).trigger("press page 3");
break;
}
on_release:
lambda: |-
switch (id(actual_page)){
case 1:
id(bar3_btn_right_event).trigger("release page 1");
break;
case 2:
id(bar3_btn_right_event).trigger("release page 2");
break;
case 3:
id(bar3_btn_right_event).trigger("release page 3");
break;
}
on_multi_click:
- timing:
- ON for at least 2s
then:
lambda: |-
switch (id(actual_page)){
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;
case 3:
id(bar3_btn_right_event).trigger("long press page 3");
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 1:
id(containers).execute();
// Example display item
id(displayitem).execute(160,2,"External","size_3_label","orange",id(outside_temperature).state,1,"","size_3_value","white","°C","size_3_unit","grey","TOP_LEFT");
break;
case 2:
id(music_card).execute(20, 125);
break;
case 3:
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 1:
id(bar_display).image(80, 0, id(lightbulb), ImageAlign::TOP_CENTER, id(red));
break;
case 2:
id(music_card_bar).execute("volume_down");
break;
case 3:
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 1:
id(bar_display).image(80, 0, id(lightbulb), ImageAlign::TOP_CENTER, id(red));
break;
case 2:
id(music_card_bar).execute("volume_up");
break;
case 3:
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 1:
id(bar_display).image(80, 0, id(lightbulb), ImageAlign::TOP_CENTER, id(red));
break;
case 2:
id(music_card_bar).execute("prev");
break;
case 3:
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 1:
id(bar_display).image(80, 0, id(lightbulb), ImageAlign::TOP_CENTER, id(red));
break;
case 2:
id(music_card_bar).execute("next");
break;
case 3:
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 1:
id(bar_display).image(80, 0, id(lightbulb), ImageAlign::TOP_CENTER, id(red));
break;
case 2:
id(music_card_bar).execute("power");
break;
case 3:
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 1:
id(bar_display).image(80, 0, id(lightbulb), ImageAlign::TOP_CENTER, id(red));
break;
case 2:
id(music_card_bar).execute("playpause");
break;
case 3:
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
+15 -51
View File
@@ -1,68 +1,32 @@
# 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 stuff work with ESPHome. Here in this repo you'll find the [results](./Components) of my journey.
The ESPHome integration adds more flexibility 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
-->
## Documentation
The documentation of the Button+ ESPHome integration can be found in the [Docs directory](./Docs/DOCS.md)
## Useful Links
## Usefull Links
* Display Items: https://esphome.io/components/display/
* Fonts: https://esphome.io/components/font#display-fonts
* Icon's and images: https://esphome.io/components/image#display-image
## Changes:
## Documentation
### NEXT: 0.2.0
#### NEW:
- added [display item card](./package/cards/display_item.md) (thanks @balk77)
- menu can now be controlled from HA and has page naming (thanks @balk77)
- the [music card](./package/cards/music.md) now changes the play/pause icon depending on the players state (thanks @balk77)
#### BREAKING:
- global `nr_of_pages` is now replaced by `nr_of_pages` ***substitution***, if you have compile errors please see the basic examples
- paging is now indexed by 1 > first page has number 1. Page 0>1, 1>2 etc.
### 0.1.1
Serialized the mini displays, by toggling the CS pins we need only 1 display instance now.
### 0.1.0
Created a Package
### Before 0.1.0
versions with first prove of concepts
## Status
All the features the Button+ has to offer should work with the [`3pages_TotalExample.yaml`](./Examples/3pages_TotalExample.yml). Please refer the ESPHome documentation for more information on how to build your own menu or ... (?)
The documentation is work in progress.. Again If you want to help... Pull requests are welcome!
## TODO:
- [ ] Documentation
- [x] initial firmware load
- [ ] links to the card 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()
-168
View File
@@ -1,168 +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
color_palette: 8BIT
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
-65
View File
@@ -1,65 +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
- script.execute: update_bar1_left
- script.execute: update_bar1_right
-240
View File
@@ -1,240 +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
color_palette: 8BIT
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
-109
View File
@@ -1,109 +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
- script.execute: update_bar1_left
- script.execute: update_bar1_right
- script.execute: update_bar2_left
- script.execute: update_bar2_right
+3 -3
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:
@@ -277,9 +278,8 @@ display:
invert_colors: false
color_order: rgb
update_interval: 30s
color_palette: 8BIT
dc_pin:
number: GPIO37
number: GPIO7
allow_other_uses: true
cs_pin:
mcp23xxx: base_J0
@@ -301,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
+6 -6
View File
@@ -154,15 +154,15 @@ script:
- id: update_all
then:
- component.update: main_display
- delay: 5ms
- delay: 0.1s
- script.execute: update_bar1_left
- delay: 5ms
- delay: 0.1s
- script.execute: update_bar1_right
- delay: 5ms
- delay: 0.1s
- script.execute: update_bar2_left
- delay: 5ms
- delay: 0.1s
- script.execute: update_bar2_right
- delay: 5ms
- delay: 0.1s
- script.execute: update_bar3_left
- delay: 5ms
- delay: 0.1s
- script.execute: update_bar3_right
+104 -30
View File
@@ -10,31 +10,46 @@
esp32:
flash_size: 16MB
psram:
mode: octal
speed: 80MHz
interval:
- interval: 1min
then:
- logger.log: "ESPHome for Button+ v0.2.0_dev"
- 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: ${nr_of_pages}
- id: actual_page
type: int
initial_value: '1'
initial_value: '0'
- id: page_names
type: std::vector<std::string>
initial_value: '{"Page 1"}'
initial_value: ${page_names}
- id: active_page_name
type: std::string
initial_value: ""
@@ -55,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
@@ -87,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
@@ -96,22 +151,25 @@ time:
id: datetime
number:
# Creates a number entity in Home Assistant
## Creates a number entity in Home Assistant
# It sets the menu of the Button Plus
- platform: template
name: "Menu Selector"
id: menu_selector
id: menuselector
optimistic: true
min_value: 1
max_value: ${nr_of_pages}
min_value: 0
max_value: ${nr_of_pages | int - 1}
step: 1
on_value:
then:
- globals.set:
id: actual_page
value: !lambda 'return x;'
- script.execute: update_page_naming
- script.execute: update_all
- lambda: |-
if(id(menuselector).state >= id(nr_of_pages)) {
id(actual_page) = id(nr_of_pages)-1;
} else {
id(actual_page) = id(menuselector).state;
}
- logger.log: "Changing page"
- script.execute: set_page
script:
######################
@@ -119,19 +177,35 @@ script:
######################
- id: next_page
then:
- number.increment:
id: menu_selector
- lambda: |-
id(actual_page) = id(actual_page) + 1;
if(id(actual_page) >= id(nr_of_pages)) {
id(actual_page) = 0;
}
- script.execute: update_page
- id: prev_page
then:
- number.decrement:
id: menu_selector
- id: update_page_naming
- lambda: |-
id(actual_page) = id(actual_page) - 1;
if(id(actual_page) < 0) {
id(actual_page) = id(nr_of_pages) - 1;
}
- script.execute: update_page
- id: set_page
then:
- lambda: |-
id(active_page_name) = id(page_names)[id(actual_page) - 1];
id(active_page_name) = id(page_names)[id(actual_page)];
- sensor.template.publish:
id: active_page_id_ha
state: !lambda 'return id(actual_page);'
- 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: set_page
-38
View File
@@ -1,38 +0,0 @@
# ESPHome Button+ - Music card
## Usage
* Add the music card file to the package files
```
package:
remote_package_files:
url: https://github.com/dixi83/ESPHome_ButtonPlus
files: [<other files>,'package/cards/display_item.yaml']
```
* Add the display item card to the (main) display `lambda` and give it the position and [parameters](#parameters) you want
It requires fonts and colors to be defined, this can be called as lambda, for instance:
```
//id(display_item).execute(x, y, label, labelfont, labelcolor, valuenum, decimals, valuestr, valuefont, valuecolor, unit, unitfont, unitcolor, align)
id(display_item).execute(160, 2, "External", "size_3_label", "orange", id(outside_temperature).state, 1, "", "size_3_value", "white", "°C", "size_3_unit", "grey", "TOP_LEFT");
```
This will position a block with label "External" in size 3 and color orange, below that a value with 1 decimal, in white, size 3. Next to that unit "°C", size 3 in grey, anchored at x,y = 160,2 top left corner
## Parameters:
```
x: int x of anchor point
y: int y of anchor point
label: string label, to be shown above the value
labelfont: string any of size_1_value, size_2_unit, size_3_label and everything in between
labelcolor: string any of white, orange, red, green or grey
valuenum: float value to display (numerical)
decimals: int rounding of value to display
valuestr: string if left empty it will select display the value float
valuefont: string any of size_1_value, size_2_unit, size_3_label and everything in between
valuecolor: string any of white, orange, red, green or grey
unit: string displayed unit
unitfont: string any of size_1_value, size_2_unit, size_3_label and everything in between
unitcolor: string any of white, orange, red, green or grey
align: string TOP_LEFT, TOP_CENTER or TOP_RIGHT
```
-210
View File
@@ -1,210 +0,0 @@
#####################################################
##
## FILENAME: cards/display_item.yaml
## NAME: Display item constructor
## DESCRIPTION: creates display items like
## the original firmware
## PARAMETERS:
## x: int x of anchor point
## y: int y of anchor point
## label: string label, to be shown above the value
## labelfont: string any of size_1_value, size_2_unit, size_3_label and everything in between
## labelcolor: string any of white, orange, red, green or grey
## valuenum: float value to display (numerical)
## decimals: int rounding of value to display
## valuestr: string if left empty it will select display the value float
## valuefont: string any of size_1_value, size_2_unit, size_3_label and everything in between
## valuecolor: string any of white, orange, red, green or grey
## unit: string displayed unit
## unitfont: string any of size_1_value, size_2_unit, size_3_label and everything in between
## unitcolor: string any of white, orange, red, green or grey
## align: string TOP_LEFT, TOP_CENTER or TOP_RIGHT
## USAGE:
## package:
## remote_package_files:
## url: https://github.com/dixi83/ESPHome_ButtonPlus
## files: [<other files>,'package/cards/display_item.yaml']
##
## display:
## - id: !extend main_display
## lambda: |-
## id(display_item).execute(160,2,"External","size_3_label","orange",id(outside_temperature).state,1,"","size_3_value","white","°C","size_3_unit","grey","TOP_LEFT");
##
#####################################################
font:
- file: "https://github.com/web-fonts/ttf/raw/refs/heads/master/bpg-ingiri-arial-webfont.ttf"
id: display_item_size_1_label
size: 10
bpp: 4
- file: "https://github.com/web-fonts/ttf/raw/refs/heads/master/bpg-ingiri-arial-webfont.ttf"
id: display_item_size_2_label
size: 12
bpp: 4
- file: "https://github.com/web-fonts/ttf/raw/refs/heads/master/bpg-ingiri-arial-webfont.ttf"
id: display_item_size_3_label
size: 14
bpp: 4
- file: "https://github.com/web-fonts/ttf/raw/refs/heads/master/bpg-ingiri-arial-webfont.ttf"
id: display_item_size_1_value
size: 15
bpp: 4
- file: "https://github.com/web-fonts/ttf/raw/refs/heads/master/bpg-ingiri-arial-webfont.ttf"
id: display_item_size_2_value
size: 20
bpp: 4
- file: "https://github.com/web-fonts/ttf/raw/refs/heads/master/bpg-ingiri-arial-webfont.ttf"
id: display_item_size_3_value
size: 20
bpp: 4
- file: "https://github.com/web-fonts/ttf/raw/refs/heads/master/bpg-ingiri-arial-webfont.ttf"
id: display_item_size_1_unit
size: 15
bpp: 4
- file: "https://github.com/web-fonts/ttf/raw/refs/heads/master/bpg-ingiri-arial-webfont.ttf"
id: display_item_size_2_unit
size: 20
bpp: 4
- file: "https://github.com/web-fonts/ttf/raw/refs/heads/master/bpg-ingiri-arial-webfont.ttf"
id: display_item_size_3_unit
size: 20
bpp: 4
color:
- id: display_item_red
hex: FF0000
- id: display_item_blue
hex: 0000FF
- id: display_item_yellow
hex: FFFF00
- id: display_item_green
hex: 00FF00
- id: display_item_orange
hex: FF7F00
- id: display_item_white
hex: FFFFFF
- id: display_item_grey
hex: A6A6A6
script:
- id: display_item
parameters:
x: int # x of anchor point
y: int # y of anchor point
label: string # label, to be shown above the value
labelfont: string # any of size_1_value, size_2_unit, size_3_label and everything in between
labelcolor: string # any of white, orange, red, green or grey
valuenum: float # value to display (numerical)
decimals: int # rounding of value to display
valuestr: string # if left empty it will select display the value float
valuefont: string # any of size_1_value, size_2_unit, size_3_label and everything in between
valuecolor: string # any of white, orange, red, green or grey
unit: string # displayed unit
unitfont: string # any of size_1_value, size_2_unit, size_3_label and everything in between
unitcolor: string # any of white, orange, red, green or grey
align: string # TOP_LEFT, TOP_CENTER or TOP_RIGHT
then:
lambda: |-
// Helper to get the font object from its ID string
// This lambda function maps font ID strings to their corresponding font objects.
// If the provided font_id matches a known font, it returns the appropriate font object.
// Otherwise, it returns nullptr.
auto get_font = [](const std::string &font_id) -> esphome::font::Font* {
if (font_id == "size_1_value") return id(display_item_size_1_value);
if (font_id == "size_1_label") return id(display_item_size_1_label);
if (font_id == "size_1_unit") return id(display_item_size_1_unit);
if (font_id == "size_2_value") return id(display_item_size_2_value);
if (font_id == "size_2_label") return id(display_item_size_2_label);
if (font_id == "size_2_unit") return id(display_item_size_2_unit);
if (font_id == "size_3_value") return id(display_item_size_3_value);
if (font_id == "size_3_label") return id(display_item_size_3_label);
if (font_id == "size_3_unit") return id(display_item_size_3_unit);
return nullptr; // Return nullptr if the font ID doesn't match any known font.
};
auto get_color = [](const std::string &color_id) -> esphome::Color {
if (color_id == "white") return id(display_item_white);
if (color_id == "blue") return id(display_item_blue);
if (color_id == "orange") return id(display_item_orange);
if (color_id == "red") return id(display_item_red);
if (color_id == "green") return id(display_item_green);
if (color_id == "grey") return id(display_item_grey);
return esphome::Color::WHITE; // Default color if no match
};
esphome::Color labelcolor_script = get_color(labelcolor);
esphome::Color valuecolor_script = get_color(valuecolor);
esphome::Color unitcolor_script = get_color(unitcolor);
// Resolve font objects based on the provided font IDs.
// These objects will later be used to measure text dimensions and render text on the display.
auto *labelfont_obj = get_font(labelfont);
auto *valuefont_obj = get_font(valuefont);
auto *unitfont_obj = get_font(unitfont);
// Format the numeric value as a string with the specified number of decimals.
// snprintf safely formats the float into a fixed-width character buffer.
char value_str[40];
// Check if valuestr is not empty
if (valuestr.empty()) { // Check if valuestr has any content
snprintf(value_str, sizeof(value_str), "%.*f", decimals, valuenum);
} else {
// If valuestr is empty, copy a default or alternative string into value_str
strncpy(value_str, valuestr.c_str(), sizeof(value_str)); // Example default value
value_str[sizeof(value_str) - 1] = '\0'; // Ensure null-termination
}
// Helper function to calculate text dimensions.
// This lambda takes a font object and a text string as input,
// and it calculates the text's width and height (returned via reference parameters).
auto calculate_text_dimensions = [](esphome::font::Font *font, const char *text, int &width, int &baseline) {
int x_offset = 0, height = 0;
font->measure(text, &width, &x_offset, &baseline, &height);
};
// Calculate the dimensions of the label text.
int label_width = 0, label_height = 0;
calculate_text_dimensions(labelfont_obj, label.c_str(), label_width, label_height);
// Calculate the dimensions of the value text.
int value_width = 0, value_height = 0;
calculate_text_dimensions(valuefont_obj, value_str, value_width, value_height);
// Calculate the dimensions of the unit text.
int unit_width = 0, unit_height = 0;
calculate_text_dimensions(unitfont_obj, unit.c_str(), unit_width, unit_height);
if (align == "TOP_RIGHT" ){
// Render the label on the display at the specified position (x, y).
id(main_display).printf(x, y, labelfont_obj, labelcolor_script, TextAlign::TOP_RIGHT, "%s", label.c_str());
// Render the value below the label with a vertical offset equal to the label's height.
id(main_display).printf(x - unit_width, y - 2 + label_height , valuefont_obj, valuecolor_script, TextAlign::TOP_RIGHT, "%s", value_str);
// Render the unit text to the right of the value text with appropriate offsets.
id(main_display).printf(x, y - 2 + label_height + value_height - unit_height, unitfont_obj, unitcolor_script, TextAlign::TOP_RIGHT, "%s", unit.c_str());
} else if (align == "TOP_CENTER" ){
// Calculate the maximum width of the display item, being the label or value+unit
// subtract half of that from the anchor point and position based on top left corner of the text fields
int offset = (std::max(label_width, value_width + unit_width) + 1) / 2;
// Render the label on the display at the specified position (x, y).
id(main_display).printf(x - offset, y, labelfont_obj, labelcolor_script, TextAlign::TOP_LEFT, "%s", label.c_str());
// Render the value below the label with a vertical offset equal to the label's height.
id(main_display).printf(x - offset, y - 2 + label_height , valuefont_obj, valuecolor_script, TextAlign::TOP_LEFT, "%s", value_str);
// Render the unit text to the right of the value text with appropriate offsets.
id(main_display).printf(x - offset + value_width, y -2+ label_height + value_height - unit_height, unitfont_obj, unitcolor_script, TextAlign::TOP_LEFT, "%s", unit.c_str());
} else {
// Render the label on the display at the specified position (x, y).
id(main_display).printf(x, y, labelfont_obj, labelcolor_script, TextAlign::TOP_LEFT, "%s", label.c_str());
// Render the value below the label with a vertical offset equal to the label's height.
id(main_display).printf(x, y - 2 + label_height , valuefont_obj, valuecolor_script, TextAlign::TOP_LEFT, "%s", value_str);
// Render the unit text to the right of the value text with appropriate offsets.
id(main_display).printf(x + value_width, y - 2 + label_height + value_height - unit_height , unitfont_obj, unitcolor_script, TextAlign::TOP_LEFT, "%s", unit.c_str());
}
+2 -2
View File
@@ -2,13 +2,13 @@
## Usage
* Add the music card file to the package files and supply the `entity_id`
* Add the music card file to youre files and supply the `entity_id`
```
substitutions:
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
+38 -32
View File
@@ -6,90 +6,96 @@
## 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:
## - id: !extend main_display
## lambda: |-
## id(music_card).execute(20, 125);
## pages:
## - id: player_page
## lambda: |-
## id(all_pages).execute();
## // insert music card:
## // >> id(music_card).execute(x, y);
## id(music_card).execute(20, 125);
## - id: !extend bar1_left
##
#####################################################
image:
- file: mdi:music-box-outline
type: binary
type: GRAYSCALE
id: icon_music_album
resize: 20x20
- file: mdi:account-music
type: binary
type: GRAYSCALE
id: icon_music_artist
resize: 20x20
- file: mdi:music-note
type: binary
type: GRAYSCALE
id: icon_music_songtitle
resize: 20x20
- file: mdi:power-off
type: binary
type: GRAYSCALE
id: icon_music_off
resize: 75x75
- file: mdi:power-on
type: binary
type: GRAYSCALE
id: icon_music_on
resize: 75x75
- file: mdi:power
type: binary
type: GRAYSCALE
id: icon_music_power
resize: 75x75
- file: mdi:timer-sand-empty
type: binary
type: GRAYSCALE
id: icon_music_idle
resize: 75x75
- file: mdi:play-pause
type: binary
type: GRAYSCALE
id: icon_music_playpause
resize: 75x75
- file: mdi:play-box-outline
type: binary
type: GRAYSCALE
id: icon_music_play_box
resize: 75x75
- file: mdi:pause-box-outline
type: binary
type: GRAYSCALE
id: icon_music_pause_box
resize: 75x75
- file: mdi:power
type: binary
type: GRAYSCALE
id: icon_music_standby
resize: 75x75
- file: mdi:download-box-outline
type: binary
type: GRAYSCALE
id: icon_music_buffering
resize: 75x75
- file: mdi:volume-plus
type: binary
type: GRAYSCALE
id: icon_music_vol_up
resize: 75x75
- file: mdi:volume-minus
type: binary
type: GRAYSCALE
id: icon_music_vol_down
resize: 75x75
- file: mdi:skip-forward
type: binary
type: GRAYSCALE
id: icon_music_next
resize: 75x75
- file: mdi:skip-backward
type: binary
type: GRAYSCALE
id: icon_music_prev
resize: 75x75
- file: mdi:play
type: binary
type: GRAYSCALE
id: icon_music_play
resize: 75x75
- file: mdi:pause
type: binary
type: GRAYSCALE
id: icon_music_pause
resize: 75x75
@@ -104,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
@@ -188,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}