Compare commits

...

175 Commits

Author SHA1 Message Date
76cecb268a ESPHome/TX-Ultimate-Easy-ESPHome_addon_ble_proxy.yaml gelöscht
Some checks failed
Validate YAML (secondary files) / Validate YAML (push) Has been cancelled
Version and Tags / version-and-tag (push) Has been cancelled
Validate ESPHome / Code scan (YAML) (push) Has been cancelled
Validate ESPHome / 🏗️ Arduino Core (latest) (push) Has been cancelled
Validate ESPHome / 🏗️ Arduino Core (dev) (push) Has been cancelled
Validate ESPHome / 📦 Arduino Basic (latest) (push) Has been cancelled
Validate ESPHome / 📦 Arduino Basic (dev) (push) Has been cancelled
Validate ESPHome / 🔌 Arduino HW Relays (latest) (push) Has been cancelled
Validate ESPHome / 🔌 Arduino HW Speaker (latest) (push) Has been cancelled
Validate ESPHome / 🔌 Arduino HW Vibration (latest) (push) Has been cancelled
Validate ESPHome / ⚡ Arduino BLE Proxy (latest) (push) Has been cancelled
Validate ESPHome / ⚡ Arduino BLE Proxy (dev) (push) Has been cancelled
Validate ESPHome / 🏗️ IDF Core (latest) (push) Has been cancelled
Validate ESPHome / 🏗️ IDF Core (dev) (push) Has been cancelled
Validate ESPHome / 📦 IDF Basic (latest) (push) Has been cancelled
Validate ESPHome / 📦 IDF Basic (dev) (push) Has been cancelled
Validate ESPHome / 🔌 IDF HW Relays (latest) (push) Has been cancelled
Validate ESPHome / 🔌 IDF HW Speaker (latest) (push) Has been cancelled
Validate ESPHome / 🔌 IDF HW Vibration (latest) (push) Has been cancelled
Validate ESPHome / ⚡ IDF BLE Proxy (latest) (push) Has been cancelled
Validate ESPHome / ⚡ IDF BLE Proxy (dev) (push) Has been cancelled
Validate ESPHome / 📊 Arduino Framework (push) Has been cancelled
Validate ESPHome / 📊 ESP-IDF Framework (push) Has been cancelled
Validate ESPHome / 📊 ESPHome (Latest) (push) Has been cancelled
Validate ESPHome / 📊 ESPHome (Dev) (push) Has been cancelled
2025-10-16 19:15:05 +00:00
69f3985d59 ESPHome/TX-Ultimate-Easy-ESPHome_core_common.yaml aktualisiert
Some checks failed
Validate ESPHome / Code scan (YAML) (push) Has been cancelled
Validate ESPHome / 🏗️ Arduino Core (latest) (push) Has been cancelled
Validate ESPHome / 🏗️ Arduino Core (dev) (push) Has been cancelled
Validate ESPHome / 📦 Arduino Basic (latest) (push) Has been cancelled
Validate ESPHome / 📦 Arduino Basic (dev) (push) Has been cancelled
Validate ESPHome / 🔌 Arduino HW Relays (latest) (push) Has been cancelled
Validate ESPHome / 🔌 Arduino HW Speaker (latest) (push) Has been cancelled
Validate ESPHome / 🔌 Arduino HW Vibration (latest) (push) Has been cancelled
Validate YAML (secondary files) / Validate YAML (push) Has been cancelled
Version and Tags / version-and-tag (push) Has been cancelled
Validate ESPHome / ⚡ Arduino BLE Proxy (latest) (push) Has been cancelled
Validate ESPHome / ⚡ Arduino BLE Proxy (dev) (push) Has been cancelled
Validate ESPHome / 🏗️ IDF Core (latest) (push) Has been cancelled
Validate ESPHome / 🏗️ IDF Core (dev) (push) Has been cancelled
Validate ESPHome / 📦 IDF Basic (latest) (push) Has been cancelled
Validate ESPHome / 📦 IDF Basic (dev) (push) Has been cancelled
Validate ESPHome / 🔌 IDF HW Relays (latest) (push) Has been cancelled
Validate ESPHome / 🔌 IDF HW Speaker (latest) (push) Has been cancelled
Validate ESPHome / 🔌 IDF HW Vibration (latest) (push) Has been cancelled
Validate ESPHome / ⚡ IDF BLE Proxy (latest) (push) Has been cancelled
Validate ESPHome / ⚡ IDF BLE Proxy (dev) (push) Has been cancelled
Validate ESPHome / 📊 Arduino Framework (push) Has been cancelled
Validate ESPHome / 📊 ESP-IDF Framework (push) Has been cancelled
Validate ESPHome / 📊 ESPHome (Latest) (push) Has been cancelled
Validate ESPHome / 📊 ESPHome (Dev) (push) Has been cancelled
2025-10-16 19:14:48 +00:00
1a73150368 ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml aktualisiert
Some checks failed
Validate ESPHome / Code scan (YAML) (push) Has been cancelled
Validate ESPHome / 🏗️ Arduino Core (latest) (push) Has been cancelled
Validate ESPHome / 🏗️ Arduino Core (dev) (push) Has been cancelled
Validate ESPHome / 📦 Arduino Basic (latest) (push) Has been cancelled
Validate ESPHome / 📦 Arduino Basic (dev) (push) Has been cancelled
Validate ESPHome / 🔌 Arduino HW Relays (latest) (push) Has been cancelled
Validate ESPHome / 🔌 Arduino HW Speaker (latest) (push) Has been cancelled
Validate ESPHome / 🔌 Arduino HW Vibration (latest) (push) Has been cancelled
Validate ESPHome / ⚡ Arduino BLE Proxy (latest) (push) Has been cancelled
Validate ESPHome / ⚡ Arduino BLE Proxy (dev) (push) Has been cancelled
Validate ESPHome / 🏗️ IDF Core (latest) (push) Has been cancelled
Validate ESPHome / 🏗️ IDF Core (dev) (push) Has been cancelled
Validate ESPHome / 📦 IDF Basic (latest) (push) Has been cancelled
Validate ESPHome / 📦 IDF Basic (dev) (push) Has been cancelled
Validate ESPHome / 🔌 IDF HW Relays (latest) (push) Has been cancelled
Validate ESPHome / 🔌 IDF HW Speaker (latest) (push) Has been cancelled
Validate ESPHome / 🔌 IDF HW Vibration (latest) (push) Has been cancelled
Validate ESPHome / ⚡ IDF BLE Proxy (latest) (push) Has been cancelled
Validate ESPHome / ⚡ IDF BLE Proxy (dev) (push) Has been cancelled
Validate ESPHome / 📊 Arduino Framework (push) Has been cancelled
Validate ESPHome / 📊 ESP-IDF Framework (push) Has been cancelled
Validate ESPHome / 📊 ESPHome (Latest) (push) Has been cancelled
Validate ESPHome / 📊 ESPHome (Dev) (push) Has been cancelled
Validate YAML (secondary files) / Validate YAML (push) Has been cancelled
Version and Tags / version-and-tag (push) Has been cancelled
2025-10-16 18:12:20 +00:00
GitHub Actions
cd39fee788 Bump version to 2025.10.1 [skip-versioning] 2025-10-03 06:07:29 +00:00
Edward Firmo
d9f361b404 Merge pull request #120 from edwardtfn/nvs-optimization
Resolve configuration loss by optimizing storage usage
2025-10-03 08:07:18 +02:00
Edward Firmo
6ffa73b321 Update .github/workflows/validate_esphome.yml
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2025-10-03 07:40:20 +02:00
Edward Firmo
e2b4fa4186 Do not turn light on when restoring state 2025-09-30 09:44:06 +02:00
Edward Firmo
63c98fcc68 Remove reference to PR #10195 2025-09-18 19:48:32 +02:00
Edward Firmo
8943dc7ea9 Req v2025.8.0 2025-09-18 06:20:14 +02:00
Edward Firmo
15c7ddc4b4 Fix dev job gating for IDF core (dev) 2025-08-12 00:08:53 +02:00
Edward Firmo
22a99017fa Update .github/workflows/validate_esphome.yml
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2025-08-12 00:04:21 +02:00
Edward Firmo
67d1c2cdce Call to restore_from_nvs 2025-08-12 00:00:33 +02:00
Edward Firmo
65d08194aa nvs optimization for hw leds 2025-08-11 23:55:32 +02:00
Edward Firmo
0690ae14d8 Test core under dev 2025-08-10 23:42:22 +02:00
Edward Firmo
0b430a7ce0 Lint 2025-08-10 23:36:08 +02:00
Edward Firmo
03e0004e8c Update lights when mode changes 2025-08-10 23:29:25 +02:00
Edward Firmo
6d2de0d32b Guard against invalid gang_count when restoring index 2025-08-10 23:12:15 +02:00
Edward Firmo
8af692e82d Introduces bs_boot_completed to know the boot status
To know when the boot was completed
2025-08-10 22:59:29 +02:00
Edward Firmo
fc135875c5 Use new switch.on_state 2025-08-09 14:46:14 +02:00
Edward Firmo
aae3eaa204 Validate group_index and add symmetric logging in restore_dual 2025-08-08 14:54:56 +02:00
Edward Firmo
fe745c0132 Update TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml 2025-08-08 14:52:40 +02:00
Edward Firmo
5757714b31 Guard against invalid group_index 2025-08-08 14:05:12 +02:00
Edward Firmo
6520b8050e Fixes "Dereference without null-check may crash on bad caller" 2025-08-08 14:02:40 +02:00
Edward Firmo
a10edb3d06 Fix "Hex literal exceeds 64-bit capacity" 2025-08-08 14:00:43 +02:00
Edward Firmo
1df58e162f Set min_version to ESPHome 2025.8.0 2025-08-08 07:37:52 +02:00
Edward Firmo
d905b69ad2 Requires ESPHome v2025.8.0 2025-08-06 23:49:27 +02:00
Edward Firmo
462b131cb6 Fixes "Wrong selector referenced – US light-mode writes EU value" 2025-08-06 23:08:00 +02:00
Edward Firmo
cf8590b460 Use switch.control from pr#10105 (esphome) 2025-08-06 23:00:41 +02:00
Edward Firmo
eca54c01ed Move "Relay n light mode" to standard_hw_relays 2025-08-06 19:47:27 +02:00
Edward Firmo
0d0de08e19 Ensure brightness is limited to 100
As suggested by coderabbitai.
2025-08-06 19:21:32 +02:00
Edward Firmo
3557c847b2 Remove TAG from .h 2025-08-06 19:12:43 +02:00
Edward Firmo
dbbbc4040d Move tag to .cpp 2025-08-06 19:06:59 +02:00
Edward Firmo
1bb444e008 Fix component tag 2025-08-06 16:45:57 +02:00
Edward Firmo
00d70efc3b Remove duplicated files 2025-08-06 16:44:31 +02:00
Edward Firmo
3c53b21fbe Lint 2025-08-06 16:34:52 +02:00
Edward Firmo
0fb4b5fe09 Fix typo 2025-08-06 16:32:13 +02:00
Edward Firmo
4634b650d9 Optimize nvs even more with dual light attributes saving 2025-08-06 16:30:40 +02:00
Edward Firmo
6a1a5d5ae2 Extend nvs optimization to relay's lights 2025-08-06 14:27:35 +02:00
Edward Firmo
271aa74f99 Do not update lights when the action is disabled
Solves #109
2025-08-06 13:43:36 +02:00
Edward Firmo
ce54db0f6d Set version for test 2025-08-05 21:36:48 +02:00
Edward Firmo
93120a8b51 Fix log reference 2025-08-05 21:27:02 +02:00
Edward Firmo
91f24db3b1 Clarify touch support
Solves #98
2025-08-05 21:15:35 +02:00
Edward Firmo
fa2e8f3b62 Update dependencies 2025-08-05 20:01:32 +02:00
Edward Firmo
c9a85504f1 Initial nvs optimization for relay lights 2025-08-05 19:46:12 +02:00
Edward Firmo
33bbad6adb Lint 2025-08-05 18:07:39 +02:00
Edward Firmo
bda597f748 nvs optimization - relays (except lights) 2025-08-05 17:51:18 +02:00
Edward Firmo
1552aaec6c Lint 2025-08-05 11:39:41 +02:00
Edward Firmo
250b22562d Simplifies tags
It should be limited to 15ish chars
2025-08-05 11:36:09 +02:00
Edward Firmo
243a18bdf2 Fix 4-bit mask 2025-08-05 11:08:16 +02:00
Edward Firmo
07de172188 Claude's changes for CodeRabbit suggestion 2025-08-05 10:57:08 +02:00
Edward Firmo
c1f1b5a7e7 Standardize log tag - hw leds 2025-08-05 09:01:36 +02:00
Edward Firmo
1d8b4fee1a Standardize log tag 2025-08-05 08:54:45 +02:00
Edward Firmo
6d16d7d645 restore_from_nvs for core_hw_buttons 2025-08-05 08:11:50 +02:00
Edward Firmo
42ae160217 Introduce restore_from_nvs on core.common 2025-08-04 23:50:06 +02:00
Edward Firmo
ecfc64d2f3 add PR information to tag descriptions in versioning workflow
Enhance the versioning workflow to include PR title and description in tag messages.

Changes:
- Add PR information extraction step using GitHub API
- Format tag messages with version, PR title, and description
- Apply structured messages to stable, latest, and version tags
- Handle fallback cases for manual dispatches and API errors
- Use temporary file approach for reliable multiline message handling

Tag descriptions now follow the format:
`# v{version} - {PR title}`
This improves release traceability by providing clear context
for what changes are included in each tagged version.
2025-08-04 21:29:29 +02:00
GitHub Actions
db83050d7b Bump version to 2025.8.2 [skip-versioning] 2025-08-02 22:10:08 +00:00
Edward Firmo
d7dcc05506 Merge pull request #119 from edwardtfn/stable_tag_update
Return last position on swipe
2025-08-03 00:09:59 +02:00
Edward Firmo
b6dc915469 use main also for advanced 2025-08-02 23:55:52 +02:00
Edward Firmo
dcc68f7077 Revert defaults 2025-08-02 23:53:13 +02:00
Edward Firmo
23d28a32a3 Improved readability 2025-08-02 23:50:42 +02:00
Edward Firmo
ba8f19060f Declare variable used in the loop 2025-08-02 23:40:01 +02:00
Edward Firmo
c36af8a220 Return last position on swipe
As per #110
2025-08-02 23:26:49 +02:00
Edward Firmo
6fad9d7074 Update stable tag automatically
To make it easier to release.
I will point users to `main` instead.
2025-08-02 22:58:11 +02:00
GitHub Actions
f79ed28364 Bump version to 2025.8.1 [skip-versioning] 2025-08-01 08:22:09 +00:00
Edward Firmo
11807ce892 Merge pull request #112 from edwardtfn/idf-as-default
Set ESP-IDF as default framework
2025-08-01 10:22:00 +02:00
Edward Firmo
526b257ad6 Use github.event.inputs.skip_dev 2025-08-01 09:28:42 +02:00
Edward Firmo
0a95a7f8d0 Fix typo on std::string 2025-08-01 09:10:58 +02:00
Edward Firmo
6687c5c6a5 Fix other references to gang_count_plural_suffix 2025-08-01 09:01:37 +02:00
Edward Firmo
ad92799035 Remove gang_count_plural_suffix and set audio channel to mono 2025-08-01 08:51:33 +02:00
Edward Firmo
8e4bbda412 Fix totals 2025-07-31 13:29:34 +02:00
Edward Firmo
8eaa60b5c0 Update totals 2025-07-31 13:14:11 +02:00
Edward Firmo
334932c6c8 Completely remove media player specific tests 2025-07-31 11:24:33 +02:00
Edward Firmo
0480f2ddf3 Media player is already part of standard package 2025-07-31 11:22:29 +02:00
Edward Firmo
720cf3ab36 Ensure tests call the right media player code 2025-07-31 11:18:40 +02:00
Edward Firmo
9edd43079b Use globals for persistence 2025-07-31 10:58:22 +02:00
Edward Firmo
a67f3dfd4a Removed initial value for volume control 2025-07-31 10:34:03 +02:00
Edward Firmo
796eb7620c Remember Media Player volume 2025-07-31 10:26:37 +02:00
Edward Firmo
e53334ac18 Clarify support to bluetooth_proxy 2025-07-31 07:21:48 +02:00
GitHub Actions
7d2080cd8d Bump version to 2025.7.3 [skip-versioning] 2025-07-31 03:20:55 +00:00
Edward Firmo
efd44fab02 Merge pull request #115 from MorghusDragon/correctly-looping-rainbow-effect
Fix rainbow effect loop by setting correct width per model.
2025-07-31 05:20:44 +02:00
Edward Firmo
8422168837 Remove effects from smaller segments 2025-07-30 19:34:56 +02:00
Edward Firmo
0b7f83ebca Add anchor per model 2025-07-30 19:15:39 +02:00
Edward Firmo
5aecb343f6 Return full audio settings 2025-07-30 19:00:54 +02:00
Edward Firmo
a4340da836 Lint 2025-07-30 07:31:17 +02:00
Edward Firmo
912607d1ec Refine speaker settings for esp-idf
Solves #95
2025-07-30 07:28:05 +02:00
Edward Firmo
ba041a9d50 Set audio channel to mono 2025-07-29 21:19:21 +02:00
Edward Firmo
b99a6b8c54 Fail-fast - Test basic before hardware 2025-07-29 16:06:11 +02:00
Edward Firmo
29b292bf45 Fix dependencies 2025-07-29 16:00:40 +02:00
Edward Firmo
4c0a83851e Test basic on dev 2025-07-29 15:54:34 +02:00
Edward Firmo
9663f295d5 Lint 2025-07-29 15:44:22 +02:00
Edward Firmo
4707506c74 Fix grouping 2025-07-29 15:40:49 +02:00
Edward Firmo
5b77f44ae2 Lint 2025-07-29 15:33:54 +02:00
Edward Firmo
efd4eaa6bb Detailed summary 2025-07-29 15:26:04 +02:00
Edward Firmo
bc0b775df2 A bit more reorg 2025-07-29 15:21:48 +02:00
Edward Firmo
ddfd4b3220 Add environment names 2025-07-29 15:12:35 +02:00
Edward Firmo
54ac959abb Org test back on platform-centric 2025-07-29 15:07:06 +02:00
Edward Firmo
9fdfd52ad4 Resolve conflict with PR#116 2025-07-29 14:58:48 +02:00
Edward Firmo
e326f03502 Merge branch 'main' into idf-as-default 2025-07-29 14:55:53 +02:00
GitHub Actions
754ad9cffe Bump version to 2025.7.2 [skip-versioning] 2025-07-29 12:54:52 +00:00
Edward Firmo
637f47583a Merge pull request #116 from MorghusDragon/fix-incorrect-multitouch-trigger-when-swiping
Fix incorrect triggering of multitouch event when swiping
2025-07-29 14:54:39 +02:00
Edward Firmo
bd86371453 Lint 2025-07-29 14:48:06 +02:00
Edward Firmo
54c9119a63 Test basic first 2025-07-29 14:45:52 +02:00
Morghus
2d1ef49ca9 Fix incorrect triggering of multitouch event when swiping 2025-07-29 20:58:44 +12:00
Morghus
1190802ccd Extract base light effect configuration so it can be customized per model. 2025-07-29 20:51:32 +12:00
Edward Firmo
2cc8199593 No limits on the core tests 2025-07-29 10:46:41 +02:00
Edward Firmo
669a78a427 Reorg tests 2025-07-29 10:39:06 +02:00
Edward Firmo
78d820d91e Remove BLE proxy addon 2025-07-29 10:35:11 +02:00
Edward Firmo
a8277e5b88 Fail-fast tests 2025-07-29 10:33:56 +02:00
Edward Firmo
458273206d Test idf first 2025-07-29 10:26:03 +02:00
Edward Firmo
982c64c1c1 Use latest (main) for all actions 2025-07-29 10:22:11 +02:00
Edward Firmo
66d1b27c0d Run tests on every push 2025-07-29 10:15:41 +02:00
Edward Firmo
f9da912777 Test Media player under IDF and unify ESPHome tests 2025-07-29 10:13:18 +02:00
Edward Firmo
6d663323e5 Simplify tests in a matrix
To make it easier to maintain
2025-07-29 10:06:46 +02:00
Edward Firmo
122cfff8db Fix compiler key name 2025-07-29 09:50:19 +02:00
Edward Firmo
70a054616c Remove old code 2025-07-29 09:39:49 +02:00
Edward Firmo
5957e277b7 Remove mapping on !lambda calls 2025-07-29 09:31:35 +02:00
Edward Firmo
7e070617ae Rebuild homeassistant.event calls
To support ESPHome 2025.8
2025-07-29 09:28:57 +02:00
Edward Firmo
50a11f61ff Removed allow_other_uses for pin 26 2025-07-29 09:20:05 +02:00
Edward Firmo
97b193d7c3 Add notice about migration to ESP-IDF 2025-07-29 09:15:49 +02:00
Edward Firmo
644ba8e711 Add test against ESPHome Dev 2025-07-29 09:05:29 +02:00
Edward Firmo
36b28a2475 Make media player dependent on speaker 2025-07-29 09:03:45 +02:00
Edward Firmo
1bd51f304d Deprecate BLE proxy addon package
This kind of solves #9

Replace TX-Ultimate-Easy-ESPHome_addon_ble_proxy.yaml with compilation
error to force migration to direct bluetooth_proxy configuration.

BREAKING CHANGE: Configurations including this package will fail to
compile. Users must remove the package reference and add bluetooth_proxy
directly to their YAML.

Before:
  packages:
    addon_bluetooth_proxy: !include TX-Ultimate-Easy-ESPHome_addon_ble_proxy.yaml

After:
  bluetooth_proxy:
2025-07-29 09:00:38 +02:00
Edward Firmo
680042a428 Replaces i2s_audio.media_player by i2s_audio.speaker.media_player 2025-07-29 08:44:46 +02:00
Edward Firmo
0a3ceb97f5 No special settings for IDF 2025-07-14 13:54:07 +02:00
Edward Firmo
daaffdb6ca Set ESP-IDF as default framework 2025-07-14 13:51:21 +02:00
GitHub Actions
ffdb3a22b5 Bump version to 2025.7.1 [skip-versioning] 2025-07-09 22:42:27 +00:00
Edward Firmo
b426bdd5dc Merge pull request #111 from edwardtfn/remove-duplicated-names
Support to ESPHome 2025.7.0
2025-07-10 00:42:19 +02:00
Edward Firmo
3cdd9f87af Standardize std::to_string 2025-07-10 00:29:34 +02:00
Edward Firmo
ddb26cd833 Test with recommended IDF only 2025-07-10 00:13:56 +02:00
Edward Firmo
5ea4f2015b Support to ESPHome 2025.7.0
- Resolve duplicated names
- Rebuilt HA event calls
2025-07-09 23:36:54 +02:00
GitHub Actions
ddb5e0f783 Bump version to 2025.5.1 [skip-versioning] 2025-05-14 19:19:50 +00:00
Edward Firmo
ab078f597a Merge pull request #106 from edwardtfn/dev
Remove enforcing psram mode to octal
2025-05-14 21:19:39 +02:00
Edward Firmo
5eb42082db Remove rmt_channel
Not supported with IDF 5
2025-05-14 21:07:07 +02:00
Edward Firmo
b1a978b3f3 Remove rmt_channel
Not supported with IDF 5
2025-05-14 20:57:58 +02:00
Edward Firmo
4ec44e611f Merge branch 'main' into dev 2025-05-02 18:30:35 +02:00
Edward Firmo
4d7895a2e9 Remove enforcing psram mode to octal
Solves #105
2025-05-02 18:29:07 +02:00
GitHub Actions
be76ebd603 Bump version to 2025.3.1 [skip-versioning] 2025-03-21 19:08:53 +00:00
Edward Firmo
49a0f088e7 Merge pull request #101 from edwardtfn/edwardtfn-patch-1
Remove reference to tj-actions
2025-03-21 20:08:45 +01:00
Edward Firmo
ace945a4ea Update validate_yamllint.yml 2025-03-15 18:23:25 +01:00
Edward Firmo
e944cd2c21 Remove reference to tj-actions
Security reasons
2025-03-15 18:19:32 +01:00
GitHub Actions
1cf6f60c78 Bump version to 2025.1.4 [skip-versioning] 2025-01-07 07:49:22 +00:00
Edward Firmo
58ba5e0136 Merge pull request #92 from edwardtfn/fix-name-CORE-is-not-defined
Fix missing `add_idf_sdkconfig_option`
2025-01-07 08:49:12 +01:00
Edward Firmo
b0b19fee45 Fix missing add_idf_sdkconfig_option 2025-01-07 08:48:38 +01:00
GitHub Actions
fe222ffe32 Bump version to 2025.1.3 [skip-versioning] 2025-01-07 07:17:53 +00:00
Edward Firmo
bb09d7f7f7 Merge pull request #91 from edwardtfn/fix-name-CORE-is-not-defined
Fix `name 'CORE' is not defined`
2025-01-07 08:17:42 +01:00
Edward Firmo
b6e3f41952 Fix name 'CORE' is not defined 2025-01-07 08:15:27 +01:00
GitHub Actions
ba3fc89032 Bump version to 2025.1.2 [skip-versioning] 2025-01-06 16:01:26 +00:00
Edward Firmo
fcbc92aaec Merge pull request #88 from edwardtfn/revert-to-media-player-as-default
Revert to media player and neopixelbus as default
2025-01-06 17:01:15 +01:00
GitHub Actions
a59f667e7d Bump version to 2025.1.1 [skip-versioning] 2025-01-06 16:00:59 +00:00
Edward Firmo
ff3bf05ed1 Merge pull request #90 from Matthijs33/patch-1
Fix value for multi_touch event publishing
2025-01-06 17:00:46 +01:00
Edward Firmo
8a4009dd7e Move sdkconfig_options to component 2025-01-05 12:58:45 +01:00
Edward Firmo
6bdf6e97b7 Move sdkconfig to component 2025-01-05 12:51:22 +01:00
Edward Firmo
acb602c4ca Lint 2025-01-05 12:44:48 +01:00
Edward Firmo
9bae016573 Lint 2025-01-05 12:43:08 +01:00
Edward Firmo
e47726dc47 Advanced settings with granular package selection
Resolves #86
2025-01-05 12:40:52 +01:00
Edward Firmo
5988681132 Include common 2025-01-05 11:02:46 +01:00
Edward Firmo
5d80ce1117 Do not remove attributes twice 2025-01-05 01:34:18 +01:00
Edward Firmo
036e62b277 Remove check for framework type 2025-01-05 01:06:52 +01:00
Edward Firmo
56c613a70f Remove sw_speaker_amplifier when the media_player is removed 2025-01-05 00:54:48 +01:00
Edward Firmo
12fd2d3747 Lint 2025-01-05 00:38:30 +01:00
Edward Firmo
81f0420987 Lint 2025-01-05 00:37:05 +01:00
Edward Firmo
e89adca5cb Remove reference to esphome_fw_version 2025-01-05 00:34:40 +01:00
Edward Firmo
a64c45b37b Media Player is not compatible with IDF 2025-01-04 23:58:05 +01:00
Edward Firmo
95291c0381 Remove non-essential packages 2025-01-04 23:54:35 +01:00
Edward Firmo
39cf0f2b10 Do not remove media player from core 2025-01-04 22:58:09 +01:00
Edward Firmo
8d03105caf Reorg tests 2025-01-04 22:54:08 +01:00
Edward Firmo
e911482deb Lint 2025-01-04 22:39:31 +01:00
Edward Firmo
ef4f412223 Split extensions 2025-01-04 22:38:19 +01:00
Edward Firmo
9d64b099a4 Lint 2025-01-04 22:34:38 +01:00
Edward Firmo
037801e961 Revert to media player and neopixelbus 2025-01-04 22:31:14 +01:00
Edward Firmo
79b57304c7 Point users to stable tag. 2025-01-03 22:53:46 +01:00
Matthijs33
48ddfbf8b0 Update TX-Ultimate-Easy-ESPHome_core_hw_touch.yaml
fixes #74
Value was "OFF" instead of "ON"
2025-01-01 14:32:17 +01:00
GitHub Actions
2e43160acb Bump version to 2024.12.27 [skip-versioning] 2024-12-28 00:11:36 +00:00
Edward Firmo
8656d0c4ca Merge pull request #81 from skykingjwc/patch-1
Update README.md
2024-12-28 01:11:26 +01:00
Jordan W. Cobb
133d571874 Update README.md
Corrected path to TX-Ultimate-Easy-ESPHome.yaml
2024-12-27 17:39:05 -05:00
51 changed files with 3074 additions and 1562 deletions

View File

@@ -1,12 +1,36 @@
--- ---
name: Validate ESPHome (Latest) name: Validate ESPHome
permissions: permissions:
contents: read contents: read
pull-requests: read pull-requests: read
on: # yamllint disable-line rule:truthy on: # yamllint disable-line rule:truthy
push:
paths:
- "**/*.yml"
- "**/*.yaml"
- "**/*.h"
- "**/*.cpp"
- "**/*.c"
- "**/*.py"
pull_request:
paths:
- "**/*.yml"
- "**/*.yaml"
- "**/*.h"
- "**/*.cpp"
- "**/*.c"
- "**/*.py"
workflow_dispatch: workflow_dispatch:
inputs:
skip_dev:
description: 'Skip dev testing (test only on latest)'
required: false
default: false
type: boolean
concurrency: concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
@@ -15,250 +39,416 @@ concurrency:
jobs: jobs:
code_scan: code_scan:
name: Code scan (YAML) name: Code scan (YAML)
runs-on: "ubuntu-latest" runs-on: ubuntu-latest
steps: steps:
- name: Checkout Code - name: Checkout Code
uses: actions/checkout@main uses: actions/checkout@main
- name: Setup Python - name: Setup Python
uses: actions/setup-python@main uses: actions/setup-python@main
with: with:
python-version: '3.8' python-version: '3.11'
- name: Install Yamllint - name: Install Yamllint
run: pip install yamllint run: pip install yamllint
- name: Validate YAML files - name: Validate YAML files
run: find . -name "*.yaml" -exec yamllint -c ./.rules/yamllint.yml {} + run: find . -name "*.yaml" -exec yamllint -c ./.rules/yamllint.yml {} +
# Arduino Framework - Foundation (latest)
build_ard_core: build_ard_core:
name: Core (Arduino) name: "🏗️ Arduino Core (latest)"
needs: code_scan needs: code_scan
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@main - uses: actions/checkout@main
- name: Build Core (Arduino) - name: Build Arduino Core - latest
uses: esphome/build-action@main uses: esphome/build-action@main
with: with:
yaml-file: ".test/esphome_ard_core.yaml" yaml-file: ".test/esphome_ard_core.yaml"
version: latest version: latest
build_idf_core: build_ard_core_dev:
name: Core (IDF) name: "🏗️ Arduino Core (dev)"
needs: code_scan needs: code_scan
if: ${{ success() && (github.event_name != 'workflow_dispatch' || github.event.inputs.skip_dev != 'true') }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@main - uses: actions/checkout@main
- name: Build Core (IDF) - name: Build Arduino Core - dev
uses: esphome/build-action@main uses: esphome/build-action@main
with: with:
yaml-file: ".test/esphome_idf_core.yaml" yaml-file: ".test/esphome_ard_core.yaml"
version: latest version: dev
build_idf53_core:
name: Core (IDF v5.3)
needs: code_scan
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@main
- name: Build Core (IDF v5.3)
uses: esphome/build-action@main
with:
yaml-file: ".test/esphome_idf53_core.yaml"
version: latest
build_ard_basic: build_ard_basic:
name: Basic (Arduino) name: "📦 Arduino Basic (latest)"
needs: build_ard_core needs: build_ard_core
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@main - uses: actions/checkout@main
- name: Build Basic (Arduino) - name: Build Arduino Basic - latest
uses: esphome/build-action@main uses: esphome/build-action@main
with: with:
yaml-file: ".test/esphome_ard_basic.yaml" yaml-file: ".test/esphome_ard_basic.yaml"
version: latest version: latest
build_idf_basic: build_ard_basic_dev:
name: Basic (IDF) name: "📦 Arduino Basic (dev)"
needs: build_idf_core needs: build_ard_core_dev
if: ${{ success() && (github.event_name != 'workflow_dispatch' || github.event.inputs.skip_dev != 'true') }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@main - uses: actions/checkout@main
- name: Build Basic (IDF) - name: Build Arduino Basic - dev
uses: esphome/build-action@main uses: esphome/build-action@main
with: with:
yaml-file: ".test/esphome_idf_basic.yaml" yaml-file: ".test/esphome_ard_basic.yaml"
version: latest version: dev
build_idf53_basic:
name: Basic (IDF v5.3)
needs: build_idf53_core
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@main
- name: Build Basic (IDF v5.3)
uses: esphome/build-action@main
with:
yaml-file: ".test/esphome_idf53_basic.yaml"
version: latest
build_ard_hw_relays: build_ard_hw_relays:
name: HW Relays (Arduino) name: "🔌 Arduino HW Relays (latest)"
needs: build_ard_core needs: build_ard_basic
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@main - uses: actions/checkout@main
- name: Build HW Relays (Arduino) - name: Build Arduino HW Relays - latest
uses: esphome/build-action@main uses: esphome/build-action@main
with: with:
yaml-file: ".test/esphome_ard_hw_relays.yaml" yaml-file: ".test/esphome_ard_hw_relays.yaml"
version: latest version: latest
build_idf_hw_relays:
name: HW Relays (IDF)
needs: build_idf_core
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@main
- name: Build HW Relays (IDF)
uses: esphome/build-action@main
with:
yaml-file: ".test/esphome_idf_hw_relays.yaml"
version: latest
build_idf53_hw_relays:
name: HW Relays (IDF v5.3)
needs: build_idf53_core
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@main
- name: Build HW Relays (IDF v5.3)
uses: esphome/build-action@main
with:
yaml-file: ".test/esphome_idf53_hw_relays.yaml"
version: latest
build_ard_hw_speaker: build_ard_hw_speaker:
name: HW Speaker (Arduino) name: "🔌 Arduino HW Speaker (latest)"
needs: build_ard_core needs: build_ard_basic
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@main - uses: actions/checkout@main
- name: Build HW Speaker (Arduino) - name: Build Arduino HW Speaker - latest
uses: esphome/build-action@main uses: esphome/build-action@main
with: with:
yaml-file: ".test/esphome_ard_hw_speaker.yaml" yaml-file: ".test/esphome_ard_hw_speaker.yaml"
version: latest version: latest
build_idf_hw_speaker:
name: HW Speaker (IDF)
needs: build_idf_core
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@main
- name: Build HW Speaker (IDF)
uses: esphome/build-action@main
with:
yaml-file: ".test/esphome_idf_hw_speaker.yaml"
version: latest
build_idf53_hw_speaker:
name: HW Speaker (IDF v5.3)
needs: build_idf53_core
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@main
- name: Build HW Speaker (IDF v5.3)
uses: esphome/build-action@main
with:
yaml-file: ".test/esphome_idf53_hw_speaker.yaml"
version: latest
build_ard_hw_vibration: build_ard_hw_vibration:
name: HW Vibration (Arduino) name: "🔌 Arduino HW Vibration (latest)"
needs: build_ard_core needs: build_ard_basic
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@main - uses: actions/checkout@main
- name: Build HW Vibration (Arduino) - name: Build Arduino HW Vibration - latest
uses: esphome/build-action@main uses: esphome/build-action@main
with: with:
yaml-file: ".test/esphome_ard_hw_vibration.yaml" yaml-file: ".test/esphome_ard_hw_vibration.yaml"
version: latest version: latest
build_idf_hw_vibration: build_ard_ble_proxy_latest:
name: HW Vibration (IDF) name: "⚡ Arduino BLE Proxy (latest)"
needs: build_idf_core
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@main
- name: Build HW Vibration (IDF)
uses: esphome/build-action@main
with:
yaml-file: ".test/esphome_idf_hw_vibration.yaml"
version: latest
build_idf53_hw_vibration:
name: HW Vibration (IDF v5.3)
needs: build_idf53_core
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@main
- name: Build HW Vibration (IDF v5.3)
uses: esphome/build-action@main
with:
yaml-file: ".test/esphome_idf53_hw_vibration.yaml"
version: latest
build_ard_ble_proxy:
name: Bluetooth Proxy (Ard)
needs: build_ard_basic needs: build_ard_basic
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@main - uses: actions/checkout@main
- name: Build BLE Proxy (Ard) - name: Build Arduino BLE Proxy - latest
uses: esphome/build-action@main uses: esphome/build-action@main
with: with:
yaml-file: ".test/esphome_ard_ble_proxy.yaml" yaml-file: ".test/esphome_ard_ble_proxy.yaml"
version: latest version: latest
build_idf_ble_proxy: build_ard_ble_proxy_dev:
name: Bluetooth Proxy (IDF) name: "⚡ Arduino BLE Proxy (dev)"
needs: build_ard_basic_dev
if: ${{ success() && (github.event_name != 'workflow_dispatch' || github.event.inputs.skip_dev != 'true') }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@main
- name: Build Arduino BLE Proxy - dev
uses: esphome/build-action@main
with:
yaml-file: ".test/esphome_ard_ble_proxy.yaml"
version: dev
# IDF Framework - Foundation (latest)
build_idf_core:
name: "🏗️ IDF Core (latest)"
needs: code_scan
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@main
- name: Build IDF Core - latest
uses: esphome/build-action@main
with:
yaml-file: ".test/esphome_idf_core.yaml"
version: latest
build_idf_core_dev:
name: "🏗️ IDF Core (dev)"
needs: code_scan
if: ${{ success() && (github.event_name != 'workflow_dispatch' || github.event.inputs.skip_dev != 'true') }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@main
- name: Build IDF Core - dev
uses: esphome/build-action@main
with:
yaml-file: ".test/esphome_idf_core.yaml"
version: dev
build_idf_basic:
name: "📦 IDF Basic (latest)"
needs: build_idf_core
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@main
- name: Build IDF Basic - latest
uses: esphome/build-action@main
with:
yaml-file: ".test/esphome_idf_basic.yaml"
version: latest
build_idf_basic_dev:
name: "📦 IDF Basic (dev)"
needs: build_idf_core_dev
if: ${{ success() && (github.event_name != 'workflow_dispatch' || github.event.inputs.skip_dev != 'true') }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@main
- name: Build IDF Basic - dev
uses: esphome/build-action@main
with:
yaml-file: ".test/esphome_idf_basic.yaml"
version: dev
build_idf_hw_relays:
name: "🔌 IDF HW Relays (latest)"
needs: build_idf_basic needs: build_idf_basic
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@main - uses: actions/checkout@main
- name: Build BLE Proxy (IDF) - name: Build IDF HW Relays - latest
uses: esphome/build-action@main
with:
yaml-file: ".test/esphome_idf_hw_relays.yaml"
version: latest
build_idf_hw_speaker:
name: "🔌 IDF HW Speaker (latest)"
needs: build_idf_basic
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@main
- name: Build IDF HW Speaker - latest
uses: esphome/build-action@main
with:
yaml-file: ".test/esphome_idf_hw_speaker.yaml"
version: latest
build_idf_hw_vibration:
name: "🔌 IDF HW Vibration (latest)"
needs: build_idf_basic
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@main
- name: Build IDF HW Vibration - latest
uses: esphome/build-action@main
with:
yaml-file: ".test/esphome_idf_hw_vibration.yaml"
version: latest
build_idf_ble_proxy_latest:
name: "⚡ IDF BLE Proxy (latest)"
needs: build_idf_basic
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@main
- name: Build IDF BLE Proxy - latest
uses: esphome/build-action@main uses: esphome/build-action@main
with: with:
yaml-file: ".test/esphome_idf_ble_proxy.yaml" yaml-file: ".test/esphome_idf_ble_proxy.yaml"
version: latest version: latest
build_idf53_ble_proxy: build_idf_ble_proxy_dev:
name: Bluetooth Proxy (IDF v5.3) name: "⚡ IDF BLE Proxy (dev)"
needs: build_idf53_basic needs: build_idf_basic_dev
if: ${{ success() && (github.event_name != 'workflow_dispatch' || github.event.inputs.skip_dev != 'true') }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@main - uses: actions/checkout@main
- name: Build BLE Proxy (IDF v5.3) - name: Build IDF BLE Proxy - dev
uses: esphome/build-action@main uses: esphome/build-action@main
with: with:
yaml-file: ".test/esphome_idf53_ble_proxy.yaml" yaml-file: ".test/esphome_idf_ble_proxy.yaml"
version: latest version: dev
build_ard_media_player: # Summary Jobs - Grouped by framework and environment
name: Media Player (Ard) summary_arduino:
name: "📊 Arduino Framework"
needs: needs:
- build_ard_core
- build_ard_core_dev
- build_ard_basic
- build_ard_basic_dev
- build_ard_hw_relays
- build_ard_hw_speaker
- build_ard_hw_vibration
- build_ard_ble_proxy_latest
- build_ard_ble_proxy_dev
if: always()
runs-on: ubuntu-latest
steps:
- name: Arduino Framework Results
run: |
SUCCESS=0
TOTAL=0
# Count latest builds (always run)
[ "${{ needs.build_ard_core.result }}" == "success" ] && SUCCESS=$((SUCCESS + 1))
[ "${{ needs.build_ard_basic.result }}" == "success" ] && SUCCESS=$((SUCCESS + 1))
[ "${{ needs.build_ard_hw_relays.result }}" == "success" ] && SUCCESS=$((SUCCESS + 1))
[ "${{ needs.build_ard_hw_speaker.result }}" == "success" ] && SUCCESS=$((SUCCESS + 1))
[ "${{ needs.build_ard_hw_vibration.result }}" == "success" ] && SUCCESS=$((SUCCESS + 1))
[ "${{ needs.build_ard_ble_proxy_latest.result }}" == "success" ] && SUCCESS=$((SUCCESS + 1))
TOTAL=$((TOTAL + 6))
# Count dev builds (if not skipped)
if [ "${{ github.event.inputs.skip_dev }}" != "true" ]; then
[ "${{ needs.build_ard_core_dev.result }}" == "success" ] && SUCCESS=$((SUCCESS + 1))
[ "${{ needs.build_ard_basic_dev.result }}" == "success" ] && SUCCESS=$((SUCCESS + 1))
[ "${{ needs.build_ard_ble_proxy_dev.result }}" == "success" ] && SUCCESS=$((SUCCESS + 1))
TOTAL=$((TOTAL + 3))
fi
echo "🤖 Arduino Framework: $SUCCESS/$TOTAL builds passed"
if [ "$SUCCESS" -eq "$TOTAL" ]; then
echo "✅ All Arduino builds successful!"
else
echo "❌ Some Arduino builds failed"
exit 1
fi
summary_idf:
name: "📊 ESP-IDF Framework"
needs:
- build_idf_core
- build_idf_core_dev
- build_idf_basic
- build_idf_basic_dev
- build_idf_hw_relays
- build_idf_hw_speaker
- build_idf_hw_vibration
- build_idf_ble_proxy_latest
- build_idf_ble_proxy_dev
if: always()
runs-on: ubuntu-latest
steps:
- name: ESP-IDF Framework Results
run: |
SUCCESS=0
TOTAL=0
# Count latest builds (always run)
[ "${{ needs.build_idf_core.result }}" == "success" ] && SUCCESS=$((SUCCESS + 1))
[ "${{ needs.build_idf_basic.result }}" == "success" ] && SUCCESS=$((SUCCESS + 1))
[ "${{ needs.build_idf_hw_relays.result }}" == "success" ] && SUCCESS=$((SUCCESS + 1))
[ "${{ needs.build_idf_hw_speaker.result }}" == "success" ] && SUCCESS=$((SUCCESS + 1))
[ "${{ needs.build_idf_hw_vibration.result }}" == "success" ] && SUCCESS=$((SUCCESS + 1))
[ "${{ needs.build_idf_ble_proxy_latest.result }}" == "success" ] && SUCCESS=$((SUCCESS + 1))
TOTAL=$((TOTAL + 6))
# Count dev builds (if not skipped)
if [ "${{ github.event.inputs.skip_dev }}" != "true" ]; then
[ "${{ needs.build_idf_core_dev.result }}" == "success" ] && SUCCESS=$((SUCCESS + 1))
[ "${{ needs.build_idf_basic_dev.result }}" == "success" ] && SUCCESS=$((SUCCESS + 1))
[ "${{ needs.build_idf_ble_proxy_dev.result }}" == "success" ] && SUCCESS=$((SUCCESS + 1))
TOTAL=$((TOTAL + 3))
fi
echo "🔧 ESP-IDF Framework: $SUCCESS/$TOTAL builds passed"
if [ "$SUCCESS" -eq "$TOTAL" ]; then
echo "✅ All ESP-IDF builds successful!"
else
echo "❌ Some ESP-IDF builds failed"
exit 1
fi
summary_latest:
name: "📊 ESPHome (Latest)"
needs:
- build_ard_core
- build_ard_basic - build_ard_basic
- build_ard_hw_relays - build_ard_hw_relays
- build_ard_hw_speaker
- build_ard_hw_vibration - build_ard_hw_vibration
- build_ard_ble_proxy_latest
- build_idf_core
- build_idf_basic
- build_idf_hw_relays
- build_idf_hw_speaker
- build_idf_hw_vibration
- build_idf_ble_proxy_latest
if: always()
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@main - name: Latest ESPHome Results
- name: Build Media Player (Ard) run: |
uses: esphome/build-action@main SUCCESS=0
with: TOTAL=12
yaml-file: ".test/esphome_ard_media_player.yaml"
version: latest # Arduino Latest builds
[ "${{ needs.build_ard_core.result }}" == "success" ] && SUCCESS=$((SUCCESS + 1))
[ "${{ needs.build_ard_basic.result }}" == "success" ] && SUCCESS=$((SUCCESS + 1))
[ "${{ needs.build_ard_hw_relays.result }}" == "success" ] && SUCCESS=$((SUCCESS + 1))
[ "${{ needs.build_ard_hw_speaker.result }}" == "success" ] && SUCCESS=$((SUCCESS + 1))
[ "${{ needs.build_ard_hw_vibration.result }}" == "success" ] && SUCCESS=$((SUCCESS + 1))
[ "${{ needs.build_ard_ble_proxy_latest.result }}" == "success" ] && SUCCESS=$((SUCCESS + 1))
# ESP-IDF Latest builds
[ "${{ needs.build_idf_core.result }}" == "success" ] && SUCCESS=$((SUCCESS + 1))
[ "${{ needs.build_idf_basic.result }}" == "success" ] && SUCCESS=$((SUCCESS + 1))
[ "${{ needs.build_idf_hw_relays.result }}" == "success" ] && SUCCESS=$((SUCCESS + 1))
[ "${{ needs.build_idf_hw_speaker.result }}" == "success" ] && SUCCESS=$((SUCCESS + 1))
[ "${{ needs.build_idf_hw_vibration.result }}" == "success" ] && SUCCESS=$((SUCCESS + 1))
[ "${{ needs.build_idf_ble_proxy_latest.result }}" == "success" ] && SUCCESS=$((SUCCESS + 1))
echo "📋 ESPHome (Latest): $SUCCESS/$TOTAL builds passed"
if [ "$SUCCESS" -eq "$TOTAL" ]; then
echo "✅ All builds on latest ESPHome successful!"
else
echo "❌ Some builds on latest ESPHome failed"
exit 1
fi
summary_dev:
name: "📊 ESPHome (Dev)"
needs:
- build_ard_core_dev
- build_ard_basic_dev
- build_ard_ble_proxy_dev
- build_idf_core_dev
- build_idf_basic_dev
- build_idf_ble_proxy_dev
if: ${{ always() && (github.event_name != 'workflow_dispatch' || github.event.inputs.skip_dev != 'true') }}
runs-on: ubuntu-latest
steps:
- name: Dev ESPHome Results
run: |
SUCCESS=0
TOTAL=6
# Arduino Dev builds
[ "${{ needs.build_ard_core_dev.result }}" == "success" ] && SUCCESS=$((SUCCESS + 1))
[ "${{ needs.build_ard_basic_dev.result }}" == "success" ] && SUCCESS=$((SUCCESS + 1))
[ "${{ needs.build_ard_ble_proxy_dev.result }}" == "success" ] && SUCCESS=$((SUCCESS + 1))
# ESP-IDF Dev builds
[ "${{ needs.build_idf_core_dev.result }}" == "success" ] && SUCCESS=$((SUCCESS + 1))
[ "${{ needs.build_idf_basic_dev.result }}" == "success" ] && SUCCESS=$((SUCCESS + 1))
[ "${{ needs.build_idf_ble_proxy_dev.result }}" == "success" ] && SUCCESS=$((SUCCESS + 1))
echo "🚀 ESPHome (Dev): $SUCCESS/$TOTAL builds passed"
if [ "$SUCCESS" -eq "$TOTAL" ]; then
echo "✅ All builds on dev ESPHome successful!"
else
echo "❌ Some builds on dev ESPHome failed"
exit 1
fi
... ...

View File

@@ -1,270 +0,0 @@
---
name: Validate ESPHome (Beta)
permissions:
contents: read
pull-requests: read
on: # yamllint disable-line rule:truthy
push:
paths:
- "**/*.{yml,yaml,h,cpp,c,py}" # Match any file with the extensions in any directory
pull_request:
paths:
- "**/*.{yml,yaml,h,cpp,c,py}" # Match any file with the extensions in any directory
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
code_scan:
name: Code scan (YAML)
runs-on: "ubuntu-latest"
steps:
- name: Checkout Code
uses: actions/checkout@main
- name: Setup Python
uses: actions/setup-python@main
with:
python-version: '3.8'
- name: Install Yamllint
run: pip install yamllint
- name: Validate YAML files
run: find . -name "*.yaml" -exec yamllint -c ./.rules/yamllint.yml {} +
build_ard_core:
name: Core (Arduino)
needs: code_scan
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@main
- name: Build Core (Arduino)
uses: esphome/build-action@main
with:
yaml-file: ".test/esphome_ard_core.yaml"
version: beta
build_idf_core:
name: Core (IDF)
needs: code_scan
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@main
- name: Build Core (IDF)
uses: esphome/build-action@main
with:
yaml-file: ".test/esphome_idf_core.yaml"
version: beta
build_idf53_core:
name: Core (IDF v5.3)
needs: code_scan
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@main
- name: Build Core (IDF v5.3)
uses: esphome/build-action@main
with:
yaml-file: ".test/esphome_idf53_core.yaml"
version: beta
build_ard_basic:
name: Basic (Arduino)
needs: build_ard_core
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@main
- name: Build Basic (Arduino)
uses: esphome/build-action@main
with:
yaml-file: ".test/esphome_ard_basic.yaml"
version: beta
build_idf_basic:
name: Basic (IDF)
needs: build_idf_core
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@main
- name: Build Basic (IDF)
uses: esphome/build-action@main
with:
yaml-file: ".test/esphome_idf_basic.yaml"
version: beta
build_idf53_basic:
name: Basic (IDF v5.3)
needs: build_idf53_core
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@main
- name: Build Basic (IDF v5.3)
uses: esphome/build-action@main
with:
yaml-file: ".test/esphome_idf53_basic.yaml"
version: beta
build_ard_hw_relays:
name: HW Relays (Arduino)
needs: build_ard_core
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@main
- name: Build HW Relays (Arduino)
uses: esphome/build-action@main
with:
yaml-file: ".test/esphome_ard_hw_relays.yaml"
version: beta
build_idf_hw_relays:
name: HW Relays (IDF)
needs: build_idf_core
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@main
- name: Build HW Relays (IDF)
uses: esphome/build-action@main
with:
yaml-file: ".test/esphome_idf_hw_relays.yaml"
version: beta
build_idf53_hw_relays:
name: HW Relays (IDF v5.3)
needs: build_idf53_core
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@main
- name: Build HW Relays (IDF v5.3)
uses: esphome/build-action@main
with:
yaml-file: ".test/esphome_idf53_hw_relays.yaml"
version: beta
build_ard_hw_speaker:
name: HW Speaker (Arduino)
needs: build_ard_core
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@main
- name: Build HW Speaker (Arduino)
uses: esphome/build-action@main
with:
yaml-file: ".test/esphome_ard_hw_speaker.yaml"
version: beta
build_idf_hw_speaker:
name: HW Speaker (IDF)
needs: build_idf_core
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@main
- name: Build HW Speaker (IDF)
uses: esphome/build-action@main
with:
yaml-file: ".test/esphome_idf_hw_speaker.yaml"
version: beta
build_idf53_hw_speaker:
name: HW Speaker (IDF v5.3)
needs: build_idf53_core
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@main
- name: Build HW Speaker (IDF v5.3)
uses: esphome/build-action@main
with:
yaml-file: ".test/esphome_idf53_hw_speaker.yaml"
version: beta
build_ard_hw_vibration:
name: HW Vibration (Arduino)
needs: build_ard_core
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@main
- name: Build HW Vibration (Arduino)
uses: esphome/build-action@main
with:
yaml-file: ".test/esphome_ard_hw_vibration.yaml"
version: beta
build_idf_hw_vibration:
name: HW Vibration (IDF)
needs: build_idf_core
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@main
- name: Build HW Vibration (IDF)
uses: esphome/build-action@main
with:
yaml-file: ".test/esphome_idf_hw_vibration.yaml"
version: beta
build_idf53_hw_vibration:
name: HW Vibration (IDF v5.3)
needs: build_idf53_core
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@main
- name: Build HW Vibration (IDF v5.3)
uses: esphome/build-action@main
with:
yaml-file: ".test/esphome_idf53_hw_vibration.yaml"
version: beta
build_ard_ble_proxy:
name: Bluetooth Proxy (Ard)
needs: build_ard_basic
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@main
- name: Build BLE Proxy (Ard)
uses: esphome/build-action@main
with:
yaml-file: ".test/esphome_ard_ble_proxy.yaml"
version: beta
build_idf_ble_proxy:
name: Bluetooth Proxy (IDF)
needs: build_idf_basic
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@main
- name: Build BLE Proxy (IDF)
uses: esphome/build-action@main
with:
yaml-file: ".test/esphome_idf_ble_proxy.yaml"
version: beta
build_idf53_ble_proxy:
name: Bluetooth Proxy (IDF v5.3)
needs: build_idf53_basic
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@main
- name: Build BLE Proxy (IDF v5.3)
uses: esphome/build-action@main
with:
yaml-file: ".test/esphome_idf53_ble_proxy.yaml"
version: beta
build_ard_media_player:
name: Media Player (Ard)
needs:
- build_ard_basic
- build_ard_hw_relays
- build_ard_hw_vibration
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@main
- name: Build Media Player (Ard)
uses: esphome/build-action@main
with:
yaml-file: ".test/esphome_ard_media_player.yaml"
version: beta
...

View File

@@ -17,23 +17,28 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Check out code - name: Check out code
uses: actions/checkout@main uses: actions/checkout@v4
with: with:
fetch-depth: '0' fetch-depth: 0 # Ensures full commit history for diff
# https://github.com/marketplace/actions/markdownlint-cli2-action
- name: Identify changed files - name: Identify changed files
uses: tj-actions/changed-files@v41
id: changed-files id: changed-files
with: run: |
files: '**/*.md' BASE=${{ github.event.before }}
separator: "," HEAD=${{ github.sha }}
# https://github.com/marketplace/actions/markdownlint-cli2-action FILES=$(git diff --name-only $BASE $HEAD -- '**/*.md' | tr '\n' ',')
echo "changed_files=$FILES" >> $GITHUB_ENV
if [[ -n "$FILES" ]]; then
echo "any_changed=true" >> $GITHUB_ENV
else
echo "any_changed=false" >> $GITHUB_ENV
fi
- name: Markdown Lint - name: Markdown Lint
uses: DavidAnson/markdownlint-cli2-action@v14 uses: DavidAnson/markdownlint-cli2-action@v14
if: steps.changed-files.outputs.any_changed == 'true' if: env.any_changed == 'true'
with: with:
globs: ${{ steps.changed-files.outputs.all_changed_files }} globs: ${{ env.changed_files }}
separator: "," separator: ","
config: '.rules/.markdownlint.jsonc' config: '.rules/.markdownlint.jsonc'
fix: true fix: true
@@ -43,15 +48,27 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Check out code - name: Check out code
uses: actions/checkout@main uses: actions/checkout@v4
with: with:
fetch-depth: '0' fetch-depth: 0
- name: Identify changed files
id: changed-files
run: |
BASE=${{ github.event.before }}
HEAD=${{ github.sha }}
FILES=$(git diff --name-only $BASE $HEAD -- '**/*.md' | tr '\n' ',')
echo "changed_files=$FILES" >> $GITHUB_ENV
if [[ -n "$FILES" ]]; then
echo "any_changed=true" >> $GITHUB_ENV
else
echo "any_changed=false" >> $GITHUB_ENV
fi
# https://github.com/gaurav-nelson/github-action-markdown-link-check
- name: Markdown links - name: Markdown links
uses: gaurav-nelson/github-action-markdown-link-check@v1 uses: gaurav-nelson/github-action-markdown-link-check@v1
if: env.any_changed == 'true'
with: with:
# yamllint disable-line rule:truthy
check-modified-files-only: yes check-modified-files-only: yes
config-file: '.rules/mlc_config.json' config-file: '.rules/mlc_config.json'
base-branch: 'main' base-branch: 'main'

View File

@@ -19,24 +19,29 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Check out code - name: Check out code
uses: actions/checkout@main uses: actions/checkout@v4
with: with:
fetch-depth: '0' fetch-depth: 0 # Ensures full commit history for diff
- name: Identify changed files - name: Identify changed files
uses: tj-actions/changed-files@v41
id: changed-files id: changed-files
with: run: |
files: '**/*.y*ml' BASE=${{ github.event.before }}
separator: "," HEAD=${{ github.sha }}
FILES=$(git diff --name-only $BASE $HEAD -- '**/*.yml' '**/*.yaml' | tr '\n' ',')
echo "changed_files=$FILES" >> $GITHUB_ENV
if [[ -n "$FILES" ]]; then
echo "any_changed=true" >> $GITHUB_ENV
else
echo "any_changed=false" >> $GITHUB_ENV
fi
- name: Validate YAML - name: Validate YAML
if: steps.changed-files.outputs.any_changed == 'true' if: env.any_changed == 'true'
run: | run: |
IFS=',' read -ra FILES <<< "${{ steps.changed-files.outputs.all_changed_files }}" IFS=',' read -ra FILES <<< "${{ env.changed_files }}"
for file in "${FILES[@]}"; do for file in "${FILES[@]}"; do
if [[ "$file" =~ ^*\.yaml$ ]] || \ if [[ "$file" =~ ^.*\.yaml$ ]] || [[ "$file" =~ ^ESPHome/.*\.yaml$ ]]; then
[[ "$file" =~ ^ESPHome/*\.yaml$ ]]; then
echo "Skipping $file" echo "Skipping $file"
continue continue
fi fi

View File

@@ -4,7 +4,6 @@
# 2. Manual dispatch - allows independent control over stable and latest tags # 2. Manual dispatch - allows independent control over stable and latest tags
--- ---
name: Version and Tags name: Version and Tags
on: # yamllint disable-line rule:truthy on: # yamllint disable-line rule:truthy
@@ -42,11 +41,84 @@ jobs:
git config user.name "GitHub Actions" git config user.name "GitHub Actions"
git config user.email "actions@github.com" git config user.email "actions@github.com"
- name: Get PR information
id: pr_info
if: github.event_name == 'push'
uses: actions/github-script@v7
with:
script: |
try {
// Use GitHub's reliable API to find PRs associated with this commit
const { data: associatedPRs } = await github.rest.repos.listPullRequestsAssociatedWithCommit({
owner: context.repo.owner,
repo: context.repo.repo,
commit_sha: context.sha
});
if (associatedPRs.length > 0) {
// Get the most recent merged PR
const pr = associatedPRs.find(pr => pr.state === 'closed' && pr.merged_at) || associatedPRs[0];
return {
title: pr.title,
body: pr.body || 'No description provided',
number: pr.number,
found: true
};
} else {
// Fallback for direct pushes or when no PR is found
const commit = await github.rest.repos.getCommit({
owner: context.repo.owner,
repo: context.repo.repo,
ref: context.sha
});
return {
title: 'Direct push to main',
body: commit.data.commit.message,
number: null,
found: false
};
}
} catch (error) {
console.log('Could not get PR info:', error.message);
return {
title: 'Version update',
body: 'Automated version bump',
number: null,
found: false
};
}
- name: Bump version - name: Bump version
run: | run: |
chmod +x ./versioning/bump_version.sh chmod +x ./versioning/bump_version.sh
./versioning/bump_version.sh ./versioning/bump_version.sh
- name: Create tag message
id: tag_message
run: |
NEW_VERSION=$(cat ./versioning/VERSION)
if [ "${{ github.event_name }}" == "push" ] && [ "${{ steps.pr_info.outcome }}" == "success" ]; then
# Use PR information directly in heredoc to avoid quoting issues
cat > tag_message.txt << EOF
# v${NEW_VERSION} - ${{ fromJson(steps.pr_info.outputs.result).title }}
${{ fromJson(steps.pr_info.outputs.result).body }}
EOF
else
# Manual dispatch or PR info unavailable - simpler message
cat > tag_message.txt << EOF
# v${NEW_VERSION} - Manual tag update
Tag updated via manual workflow dispatch.
EOF
fi
# Store the message for use in subsequent steps
echo "TAG_MESSAGE_FILE=tag_message.txt" >> $GITHUB_ENV
- name: Push Changes and Tags - name: Push Changes and Tags
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -73,10 +145,10 @@ jobs:
echo "Backing up current stable tag ($OLD_STABLE)" echo "Backing up current stable tag ($OLD_STABLE)"
fi fi
# Update tag # Update tag with detailed message
NEW_VERSION=$(cat ./versioning/VERSION) NEW_VERSION=$(cat ./versioning/VERSION)
echo "Updating stable tag to $NEW_VERSION" echo "Updating stable tag to $NEW_VERSION"
git tag -fa stable -m "Update stable tag" git tag -fa stable -F "$TAG_MESSAGE_FILE"
git push origin stable --force git push origin stable --force
- name: Update latest tag - name: Update latest tag
@@ -98,9 +170,15 @@ jobs:
echo "Backing up current latest tag ($OLD_LATEST)" echo "Backing up current latest tag ($OLD_LATEST)"
fi fi
# Update tag # Update tag with detailed message
NEW_VERSION=$(cat ./versioning/VERSION) NEW_VERSION=$(cat ./versioning/VERSION)
echo "Updating latest tag to $NEW_VERSION" echo "Updating latest tag to $NEW_VERSION"
git tag -fa latest -m "Update latest tag" git tag -fa latest -F "$TAG_MESSAGE_FILE"
git push origin latest --force git push origin latest --force
- name: Cleanup
if: always()
run: |
# Clean up temporary files
[ -f tag_message.txt ] && rm tag_message.txt || true
... ...

5
.test/common.yaml Normal file
View File

@@ -0,0 +1,5 @@
---
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
...

8
.test/common_ard.yaml Normal file
View File

@@ -0,0 +1,8 @@
---
packages:
common: !include common.yaml
esp32:
framework:
type: arduino
...

8
.test/common_idf.yaml Normal file
View File

@@ -0,0 +1,8 @@
---
packages:
common: !include common.yaml
esp32:
framework:
type: esp-idf
...

View File

@@ -1,13 +1,6 @@
--- ---
packages: packages:
common_arduino: !include common_ard.yaml
core_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml core_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml
standard_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_standard.yaml standard_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_standard.yaml
esp32:
framework:
type: arduino
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
... ...

View File

@@ -1,14 +1,8 @@
--- ---
packages: packages:
common_arduino: !include common_ard.yaml
core_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml core_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml
standard_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_standard.yaml standard_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_standard.yaml
addon_bluetooth_proxy: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_addon_ble_proxy.yaml
esp32: bluetooth_proxy:
framework:
type: arduino
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
... ...

View File

@@ -1,12 +1,5 @@
--- ---
packages: packages:
common_arduino: !include common_ard.yaml
core_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml core_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml
esp32:
framework:
type: arduino
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
... ...

View File

@@ -1,13 +1,6 @@
--- ---
packages: packages:
common_arduino: !include common_ard.yaml
core_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml core_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml
standard_hw_relays_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml standard_hw_relays_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml
esp32:
framework:
type: arduino
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
... ...

View File

@@ -1,13 +1,6 @@
--- ---
packages: packages:
common_arduino: !include common_ard.yaml
core_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml core_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml
standard_hw_speaker_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_speaker.yaml standard_hw_speaker_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_speaker.yaml
esp32:
framework:
type: arduino
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
... ...

View File

@@ -1,13 +1,6 @@
--- ---
packages: packages:
common_arduino: !include common_ard.yaml
core_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml core_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml
standard_hw_vibration_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_vibration.yaml standard_hw_vibration_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_vibration.yaml
esp32:
framework:
type: arduino
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
... ...

View File

@@ -1,15 +0,0 @@
---
packages:
core_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml
standard_hw_relays_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml
standard_hw_vibration_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_vibration.yaml
addon_media_player: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_addon_media_player.yaml
esp32:
framework:
type: arduino
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
...

View File

@@ -1,15 +0,0 @@
---
packages:
core_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml
standard_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_standard.yaml
esp32:
framework:
type: esp-idf
version: 5.3.1
platform_version: 6.9.0
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
...

View File

@@ -1,16 +0,0 @@
---
packages:
core_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml
standard_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_standard.yaml
addon_bluetooth_proxy: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_addon_ble_proxy.yaml
esp32:
framework:
type: esp-idf
version: 5.3.1
platform_version: 6.9.0
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
...

View File

@@ -1,14 +0,0 @@
---
packages:
core_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml
esp32:
framework:
type: esp-idf
version: 5.3.1
platform_version: 6.9.0
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
...

View File

@@ -1,15 +0,0 @@
---
packages:
core_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml
standard_hw_relays_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml
esp32:
framework:
type: esp-idf
version: 5.3.1
platform_version: 6.9.0
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
...

View File

@@ -1,15 +0,0 @@
---
packages:
core_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml
standard_hw_speaker_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_speaker.yaml
esp32:
framework:
type: esp-idf
version: 5.3.1
platform_version: 6.9.0
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
...

View File

@@ -1,15 +0,0 @@
---
packages:
core_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml
standard_hw_vibration_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_vibration.yaml
esp32:
framework:
type: esp-idf
version: 5.3.1
platform_version: 6.9.0
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
...

View File

@@ -1,13 +1,6 @@
--- ---
packages: packages:
common_idf: !include common_idf.yaml
core_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml core_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml
standard_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_standard.yaml standard_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_standard.yaml
esp32:
framework:
type: esp-idf
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
... ...

View File

@@ -1,14 +1,8 @@
--- ---
packages: packages:
common: !include common.yaml
core_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml core_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml
standard_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_standard.yaml standard_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_standard.yaml
addon_bluetooth_proxy: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_addon_ble_proxy.yaml
esp32: bluetooth_proxy:
framework:
type: esp-idf
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
... ...

View File

@@ -1,12 +1,5 @@
--- ---
packages: packages:
common_idf: !include common_idf.yaml
core_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml core_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml
esp32:
framework:
type: esp-idf
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
... ...

View File

@@ -1,13 +1,6 @@
--- ---
packages: packages:
common_idf: !include common_idf.yaml
core_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml core_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml
standard_hw_relays_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml standard_hw_relays_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml
esp32:
framework:
type: esp-idf
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
... ...

View File

@@ -1,13 +1,6 @@
--- ---
packages: packages:
common_idf: !include common_idf.yaml
core_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml core_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml
standard_hw_speaker_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_speaker.yaml standard_hw_speaker_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_speaker.yaml
esp32:
framework:
type: esp-idf
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
... ...

View File

@@ -1,13 +1,6 @@
--- ---
packages: packages:
common_idf: !include common_idf.yaml
core_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml core_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml
standard_hw_vibration_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_vibration.yaml standard_hw_vibration_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_vibration.yaml
esp32:
framework:
type: esp-idf
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
... ...

View File

@@ -2,7 +2,7 @@
##### TX Ultimate Easy for ESPHome ##### ##### TX Ultimate Easy for ESPHome #####
##### Repository: https://github.com/edwardtfn/TX-Ultimate-Easy ##### ##### Repository: https://github.com/edwardtfn/TX-Ultimate-Easy #####
#################################################################################################### ####################################################################################################
##### Purpose: ESPHome Core - Hardware - Media player ##### ##### Purpose: ESPHome Basic (includes core plus non-essential, but recommended packages) #####
#################################################################################################### ####################################################################################################
##### Author: edwardtfn - https://github.com/edwardtfn - https://buymeacoffee.com/edwardfirmo ##### ##### Author: edwardtfn - https://github.com/edwardtfn - https://buymeacoffee.com/edwardfirmo #####
#################################################################################################### ####################################################################################################
@@ -11,19 +11,9 @@
##### - For normal system use, modifications to this file are NOT required. ##### ##### - For normal system use, modifications to this file are NOT required. #####
#################################################################################################### ####################################################################################################
--- ---
i2s_audio: packages:
id: if_i2s_audio # yamllint disable rule:colons
i2s_bclk_pin: GPIO2 core: !include TX-Ultimate-Easy-ESPHome_core.yaml
i2s_lrclk_pin: GPIO4 standard: !include TX-Ultimate-Easy-ESPHome_standard.yaml
# yamllint enable rule:colons
media_player:
- id: mp_media_player
name: Media Player
internal: false
platform: i2s_audio
i2s_dout_pin: GPIO15
i2s_audio_id: if_i2s_audio
i2s_comm_fmt: msb # Experimental - It was 'lsb' until v2024.12.12
dac_type: external
mode: mono
... ...

View File

@@ -1,33 +0,0 @@
####################################################################################################
##### TX Ultimate Easy for ESPHome #####
##### Repository: https://github.com/edwardtfn/TX-Ultimate-Easy #####
####################################################################################################
##### Purpose: ESPHome Core - Add-on - BLE Proxy #####
####################################################################################################
##### Author: edwardtfn - https://github.com/edwardtfn - https://buymeacoffee.com/edwardfirmo #####
####################################################################################################
##### NOTE: #####
##### - Make changes ONLY if absolutely necessary and you have the required knowledge. #####
##### - For normal system use, modifications to this file are NOT required. #####
####################################################################################################
---
bluetooth_proxy:
esphome:
platformio_options:
build_flags:
- -D TX_ULTIMATE_EASY_ADDON_BLE_PROXY
script:
- id: !extend dump_config_list_packages
then:
- script.wait: dump_config
- lambda: |-
// Check for requirements
#if !defined(TX_ULTIMATE_EASY_CORE)
#error "The package TX-Ultimate-Easy-ESPHome_core.yaml is required."
#endif
// Identify itself
ESP_LOGCONFIG(ESPHOME_PROJECT_NAME, " - Add-on - BLE Proxy");
...

View File

@@ -1,68 +0,0 @@
####################################################################################################
##### TX Ultimate Easy for ESPHome #####
##### Repository: https://github.com/edwardtfn/TX-Ultimate-Easy #####
####################################################################################################
##### Purpose: ESPHome - Add-on - Media player #####
####################################################################################################
##### Author: edwardtfn - https://github.com/edwardtfn - https://buymeacoffee.com/edwardfirmo #####
####################################################################################################
##### NOTE: #####
##### - Make changes ONLY if absolutely necessary and you have the required knowledge. #####
##### - For normal system use, modifications to this file are NOT required. #####
####################################################################################################
---
packages:
standard_hw_audio: !include TX-Ultimate-Easy-ESPHome_standard_hw_audio.yaml
esp32:
framework:
type: arduino
esphome:
platformio_options:
build_flags:
- -D TX_ULTIMATE_EASY_ADDON_MEDIA_PLAYER
media_player:
- id: mp_media_player
name: Media Player
internal: false
platform: i2s_audio
i2s_dout_pin: GPIO15
i2s_audio_id: if_i2s_audio
i2s_comm_fmt: msb
dac_type: external
mode: mono
mute_pin:
number: GPIO26
inverted: true
allow_other_uses: true
script:
- id: !extend dump_config_list_packages
then:
- script.wait: dump_config
- lambda: |-
// Check for requirements
#if !defined(TX_ULTIMATE_EASY_CORE)
#error "The package TX-Ultimate-Easy-ESPHome_core.yaml is required."
#endif
#if !defined(TX_ULTIMATE_EASY_STANDARD_HW_AUDIO)
#error "The package TX-Ultimate-Easy-ESPHome_standard_hw_audio.yaml is required."
#endif
// Identify itself
ESP_LOGCONFIG(ESPHOME_PROJECT_NAME, " - Add-on - Media Player");
switch:
- id: sw_speaker_amplifier
name: Speaker - Amplifier
platform: gpio
pin:
number: GPIO26
inverted: true
allow_other_uses: true
restore_mode: RESTORE_DEFAULT_ON
internal: true
entity_category: config
...

View File

@@ -11,312 +11,11 @@
##### - For normal system use, modifications to this file are NOT required. ##### ##### - For normal system use, modifications to this file are NOT required. #####
#################################################################################################### ####################################################################################################
--- ---
substitutions:
name: tx-ultimate-easy
friendly_name: TX Ultimate Easy
<<: !include ../versioning/VERSION_YAML
TX_MODEL_FORMAT_EU_TEXT: "EU (Square, T5-xC-86)"
TX_MODEL_FORMAT_US_TEXT: "US (Rectangle, T5-xC-120)"
TX_MODEL_1_GANG_TEXT: "1 Gang"
TX_MODEL_2_GANG_TEXT: "2 Gang"
TX_MODEL_3_GANG_TEXT: "3 Gang"
TX_MODEL_4_GANG_TEXT: "4 Gang"
DUMP_CONFIG_CALLER_DELAY: 10s # Delay to dump config after requested
packages: packages:
# yamllint disable rule:colons # yamllint disable rule:colons
core_common: !include TX-Ultimate-Easy-ESPHome_core_common.yaml
core_hw_buttons: !include TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml core_hw_buttons: !include TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml
core_hw_leds: !include TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml core_hw_leds: !include TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml
core_hw_touch: !include TX-Ultimate-Easy-ESPHome_core_hw_touch.yaml core_hw_touch: !include TX-Ultimate-Easy-ESPHome_core_hw_touch.yaml
# yamllint enable rule:colons # yamllint enable rule:colons
api:
id: api_server
on_client_connected:
then:
- script.execute: dump_config_caller
- script.execute: publish_device_info
binary_sensor:
- id: bs_pending_restart
name: Pending Restart Status
internal: false
disabled_by_default: false
platform: template
entity_category: diagnostic
device_class: problem
on_state:
then:
- lambda: |-
if (x)
ESP_LOGW("core", "Pending restart: YES");
else
ESP_LOGCONFIG("core", "Pending restart: No");
- script.execute: dump_config_caller
esp32:
board: esp32dev # adafruit_feather_esp32_v2 # esp32dev
flash_size: 8MB
framework:
type: esp-idf
esphome:
name: ${name}
friendly_name: ${friendly_name}
comment: TX Ultimate Easy
project:
name: "edwardtfn.tx_ultimate_easy"
version: ${version}
platformio_options:
build_flags:
- -D TX_ULTIMATE_EASY_CORE
on_boot:
- priority: 700
then:
- script.execute: boot_initialize
- priority: 600 # This is where most sensors are set up.
then:
- script.execute: boot_sequence
- priority: -100 # At this priority, pretty much everything should already be initialized.
then:
- script.execute: boot_done
globals:
- id: is_us_model
type: bool
restore_value: true
initial_value: 'false'
- id: gang_count
type: uint8_t
restore_value: true
initial_value: '0'
- id: gang_count_plural_suffix
type: std::string
restore_value: true
max_restore_data_length: 3
# initial_value: "''"
logger:
level: DEBUG
ota:
platform: esphome
psram:
mode: octal
speed: 80MHz
script:
- id: api_send_ha_event_boot
mode: queued
parameters:
type: string
then:
- homeassistant.event:
event: esphome.tx_ultimate_easy
data:
device_name: !lambda return tx_device_name->state.c_str();
firmware: ${version}
domain: boot
type: !lambda return type.c_str();
- id: boot_done
mode: restart
then:
- script.execute:
id: api_send_ha_event_boot
type: done
- id: boot_initialize
mode: restart
then:
# Extended by:
# - HW Buttons
# - HW Relays
# - HW Touch
- script.execute:
id: api_send_ha_event_boot
type: start
- wait_until:
condition:
- lambda: return sl_tx_model_format->active_index().has_value();
- lambda: return sl_tx_model_gang->active_index().has_value();
- lambda: |-
id(is_us_model) = (sl_tx_model_format->state == "${TX_MODEL_FORMAT_US_TEXT}");
id(gang_count) = sl_tx_model_gang->active_index().value() + 1;
if (id(gang_count) < 1 || id(gang_count) > 4) {
ESP_LOGE("core_hw_leds", "Invalid number of gangs: %" PRIu8, id(gang_count));
}
id(gang_count_plural_suffix) = id(gang_count) > 1 ? "s" : "";
- id: boot_sequence
mode: restart
then:
- script.execute: publish_device_info
- binary_sensor.template.publish:
id: bs_pending_restart
state: false
- id: dump_config
mode: restart
then:
# Extended by all modules
- lambda: |-
// Device identification
ESP_LOGCONFIG("core", "Device friendly name: ${friendly_name}");
ESP_LOGCONFIG("core", "Device name: ${name}");
ESP_LOGCONFIG("core", "Device name (HA): %s", tx_device_name->state.c_str());
ESP_LOGCONFIG("core", "Device hostname: %s", App.get_name().c_str());
dump_config_versions->execute();
// Framework detection
#ifdef USE_ARDUINO
ESP_LOGCONFIG("core", "Framework: Arduino");
#elif defined(USE_ESP_IDF)
ESP_LOGCONFIG("core", "Framework: ESP-IDF");
#else
ESP_LOGW("core", "Framework: UNKNOWN");
#endif
// Model configuration
ESP_LOGCONFIG("core", "Model format (selected): %s", sl_tx_model_format->state.c_str());
ESP_LOGCONFIG("core", "Model format (detected): %s", id(is_us_model) ? "US" : "EU");
ESP_LOGCONFIG("core", "Gangs (selected): %s", sl_tx_model_gang->state.c_str());
ESP_LOGCONFIG("core", "Gangs (detected): %" PRIu8 "-Gang%s", id(gang_count), id(gang_count_plural_suffix).c_str());
// System state
if (bs_pending_restart->state)
ESP_LOGW("core", "Pending restart: YES");
else
ESP_LOGCONFIG("core", "Pending restart: No");
- id: dump_config_caller
mode: restart
then:
- delay: ${DUMP_CONFIG_CALLER_DELAY}
- script.execute: dump_config
- script.wait: dump_config
- script.execute: dump_config_list_packages
- id: dump_config_list_packages
mode: restart
then:
- script.wait: dump_config
- lambda: |-
ESP_LOGCONFIG(ESPHOME_PROJECT_NAME, "Installed packages:");
// Identify itself
ESP_LOGCONFIG(ESPHOME_PROJECT_NAME, " - Core");
- id: dump_config_versions
mode: restart
then:
- lambda: |-
// Version information
ESP_LOGCONFIG("core", "TX Ultimate firmware version: ${version}");
- id: publish_device_info
mode: restart
then:
- lambda: |-
tx_fw_version->publish_state("${version}");
tx_device_name->publish_state(App.get_name().c_str());
select:
- id: sl_tx_model_format
name: Model (Format)
platform: template
options:
- "${TX_MODEL_FORMAT_EU_TEXT}"
- "${TX_MODEL_FORMAT_US_TEXT}"
initial_option: "${TX_MODEL_FORMAT_EU_TEXT}"
optimistic: true
restore_value: true
internal: false
entity_category: config
disabled_by_default: false
icon: mdi:tablet-cellphone
on_value:
then:
- binary_sensor.template.publish:
id: bs_pending_restart
state: true
- lambda: |-
id(is_us_model) = (x == "${TX_MODEL_FORMAT_US_TEXT}");
ESP_LOGI("core", "New model selected: %s", id(is_us_model) ? "US" : "EU");
- id: sl_tx_model_gang
name: Model (Gang)
platform: template
options:
- "${TX_MODEL_1_GANG_TEXT}"
- "${TX_MODEL_2_GANG_TEXT}"
- "${TX_MODEL_3_GANG_TEXT}"
- "${TX_MODEL_4_GANG_TEXT}"
initial_option: "${TX_MODEL_1_GANG_TEXT}"
optimistic: true
restore_value: true
internal: false
entity_category: config
disabled_by_default: false
icon: mdi:dip-switch
on_value:
then:
- binary_sensor.template.publish:
id: bs_pending_restart
state: true
- lambda: |-
id(gang_count) = static_cast<uint8_t>(i) + 1;
ESP_LOGI("core", "New model selected: %" PRIu8 "-%s", id(gang_count), id(gang_count) > 1 ? "Gangs" : "Gang");
tx_ultimate->set_gang_count(id(gang_count));
text_sensor:
- id: tx_fw_version
name: TX Ultimate Easy Firmware Version
platform: template
entity_category: diagnostic
icon: mdi:tag-text-outline
internal: false
update_interval: never
lambda: |-
return {"${version}"};
- id: tx_device_name
name: Device Name
platform: template
icon: mdi:identifier
entity_category: diagnostic
internal: false
disabled_by_default: false
update_interval: never
lambda: |-
return {"${name}"};
filters:
- lambda: |-
const std::string raw_name = x;
std::string result;
bool last_was_underscore = false;
for (const char& c : raw_name) {
if (isalnum(c)) {
result += tolower(c); // Add alphanumeric characters as lowercase
last_was_underscore = false;
} else if (!last_was_underscore) { // Replace non-alphanumeric with '_' but avoid consecutive '_'
result += '_';
last_was_underscore = true;
}
}
return result;
wifi:
id: wifi_component
... ...

View File

@@ -0,0 +1,378 @@
####################################################################################################
##### TX Ultimate Easy for ESPHome #####
##### Repository: https://github.com/edwardtfn/TX-Ultimate-Easy #####
####################################################################################################
##### Purpose: ESPHome Core - Common #####
####################################################################################################
##### Author: edwardtfn - https://github.com/edwardtfn - https://buymeacoffee.com/edwardfirmo #####
####################################################################################################
##### NOTE: #####
##### - Make changes ONLY if absolutely necessary and you have the required knowledge. #####
##### - For normal system use, modifications to this file are NOT required. #####
####################################################################################################
---
substitutions:
name: tx-ultimate-easy
friendly_name: TX Ultimate Easy
<<: !include ../versioning/VERSION_YAML
EVENT_NAME: esphome.tx_ultimate_easy
TX_MODEL_FORMAT_EU_TEXT: "EU (Square, T5-xC-86)"
TX_MODEL_FORMAT_US_TEXT: "US (Rectangle, T5-xC-120)"
TX_MODEL_1_GANG_TEXT: "1 Gang"
TX_MODEL_2_GANG_TEXT: "2 Gang"
TX_MODEL_3_GANG_TEXT: "3 Gang"
TX_MODEL_4_GANG_TEXT: "4 Gang"
DUMP_CONFIG_CALLER_DELAY: 5s # Delay to dump config after requested
TAG_CORE_COMMON: core.common
api:
id: api_server
on_client_connected:
then:
- script.execute: dump_config_caller
- script.execute: publish_device_info
binary_sensor:
- id: bs_pending_restart
name: Pending Restart Status
internal: false
disabled_by_default: false
platform: template
entity_category: diagnostic
device_class: problem
on_state:
then:
- script.execute: dump_config_caller
- id: bs_boot_completed
name: Boot Completed
internal: false
disabled_by_default: false
platform: template
entity_category: diagnostic
device_class: connectivity
icon: mdi:rocket-launch
on_state:
then:
- script.execute: dump_config_caller
button:
- id: bt_restart
name: Restart
internal: false
platform: restart
esp32:
board: esp32dev
flash_size: 8MB
framework:
type: esp-idf
esphome:
name: ${name}
friendly_name: ${friendly_name}
comment: TX Ultimate Easy
min_version: 2025.8.0
project:
name: "edwardtfn.tx_ultimate_easy"
version: ${version}
platformio_options:
build_flags:
- -D TX_ULTIMATE_EASY_CORE_COMMON
on_boot:
- priority: 1000 # Very early in boot process
then:
- binary_sensor.template.publish:
id: bs_boot_completed
state: OFF # yamllint disable-line rule:truthy
- priority: 750
then:
- script.execute: restore_from_nvs
- priority: 700
then:
- script.execute: boot_initialize
- priority: 600 # This is where most sensors are set up.
then:
- script.execute: boot_sequence
- priority: -100 # At this priority, pretty much everything should already be initialized.
then:
- script.execute: boot_done
external_components:
- source:
type: git
url: https://git.andrejka.at/oneil1987/TX-Ultimate-Easy
ref: v${version}
refresh: 1h
components:
- tx_ultimate_easy
globals:
- id: gang_count
type: uint8_t
restore_value: true
initial_value: '0'
- id: is_us_model
type: bool
restore_value: true
initial_value: 'false'
logger:
level: DEBUG
ota:
platform: esphome
psram:
script:
- id: api_send_ha_event_boot
mode: queued
parameters:
type: string
then:
- lambda: |-
// Send event to Home Assistant
esphome::api::CustomAPIDevice ha_event;
ha_event.fire_homeassistant_event("${EVENT_NAME}", {
{"device_name", id(tx_device_name).state.c_str()},
{"firmware", "${version}"},
{"domain", "boot"},
{"type", type.c_str()}
});
- id: boot_done
mode: restart
then:
- script.wait: restore_from_nvs
- script.wait: boot_initialize
- script.wait: boot_sequence
- script.execute:
id: api_send_ha_event_boot
type: done
- script.wait: api_send_ha_event_boot
- binary_sensor.template.publish:
id: bs_boot_completed
state: ON # yamllint disable-line rule:truthy
- id: boot_initialize
mode: restart
then:
# Extended by:
# - HW Buttons
# - HW Relays
# - HW Touch
- script.execute:
id: api_send_ha_event_boot
type: start
- wait_until:
condition:
- lambda: return sl_tx_model_format->active_index().has_value();
- lambda: return sl_tx_model_gang->active_index().has_value();
- lambda: |-
id(is_us_model) = (sl_tx_model_format->state == "${TX_MODEL_FORMAT_US_TEXT}");
id(gang_count) = sl_tx_model_gang->active_index().value() + 1;
if (id(gang_count) < 1 || id(gang_count) > 4) {
ESP_LOGE("${TAG_CORE_COMMON}", "Invalid number of gangs: %" PRIu8, id(gang_count));
}
- id: boot_sequence
mode: restart
then:
- script.execute: publish_device_info
- binary_sensor.template.publish:
id: bs_pending_restart
state: false
- id: dump_config
mode: restart
then:
# Extended by all modules
- lambda: |-
// Device identification
ESP_LOGCONFIG("${TAG_CORE_COMMON}", "Device friendly name: ${friendly_name}");
ESP_LOGCONFIG("${TAG_CORE_COMMON}", "Device name: ${name}");
ESP_LOGCONFIG("${TAG_CORE_COMMON}", "Device name (HA): %s", tx_device_name->state.c_str());
ESP_LOGCONFIG("${TAG_CORE_COMMON}", "Device hostname: %s", App.get_name().c_str());
dump_config_versions->execute();
// Framework detection
#ifdef USE_ARDUINO
ESP_LOGCONFIG("${TAG_CORE_COMMON}", "Framework: Arduino");
#elif defined(USE_ESP_IDF)
ESP_LOGCONFIG("${TAG_CORE_COMMON}", "Framework: ESP-IDF");
#else
ESP_LOGW("${TAG_CORE_COMMON}", "Framework: UNKNOWN");
#endif
// Model configuration
ESP_LOGCONFIG("${TAG_CORE_COMMON}", "Model format (selected): %s",
sl_tx_model_format->state.c_str());
ESP_LOGCONFIG("${TAG_CORE_COMMON}", "Model format (detected): %s",
id(is_us_model) ? "US" : "EU");
ESP_LOGCONFIG("${TAG_CORE_COMMON}", "Gangs (selected): %s",
sl_tx_model_gang->state.c_str());
ESP_LOGCONFIG("${TAG_CORE_COMMON}", "Gangs (detected): %" PRIu8 "-Gang%s",
id(gang_count), id(gang_count) > 1 ? "s" : "");
// Boot completion status
if (bs_boot_completed->state)
ESP_LOGCONFIG("${TAG_CORE_COMMON}", "Boot completed: Yes");
else
ESP_LOGW("${TAG_CORE_COMMON}", "Boot completed: NO");
// System state
if (bs_pending_restart->state)
ESP_LOGW("${TAG_CORE_COMMON}", "Pending restart: YES");
else
ESP_LOGCONFIG("${TAG_CORE_COMMON}", "Pending restart: No");
- id: dump_config_caller
mode: restart
then:
- delay: ${DUMP_CONFIG_CALLER_DELAY}
- script.execute: dump_config
- script.wait: dump_config
- script.execute: dump_config_list_packages
- id: dump_config_list_packages
mode: restart
then:
- script.wait: dump_config
- lambda: |-
ESP_LOGCONFIG(ESPHOME_PROJECT_NAME, "Installed packages:");
// Identify itself
ESP_LOGCONFIG(ESPHOME_PROJECT_NAME, " - Core");
- id: dump_config_versions
mode: restart
then:
- lambda: |-
// Version information
ESP_LOGCONFIG("${TAG_CORE_COMMON}", "TX Ultimate firmware version: ${version}");
// ESPHome builder information
ESP_LOGCONFIG("${TAG_CORE_COMMON}", "ESPHome builder: " ESPHOME_VERSION);
ESP_LOGCONFIG("${TAG_CORE_COMMON}", "ESPHome build timestamp: %s", App.get_compilation_time().c_str());
- id: publish_device_info
mode: restart
then:
- lambda: |-
tx_fw_version->publish_state("${version}");
tx_device_name->publish_state(App.get_name().c_str());
- id: restore_from_nvs
mode: single
then:
- select.set_index:
id: sl_tx_model_format
index: !lambda if (id(is_us_model)) return 1; else return 0;
- select.set_index:
id: sl_tx_model_gang
index: !lambda |-
if (id(gang_count) < 1 || id(gang_count) > 4) return 0; // default to 1-Gang
return static_cast<uint8_t>(id(gang_count)) - 1;
select:
- id: sl_tx_model_format
name: Model (Format)
platform: template
options:
- "${TX_MODEL_FORMAT_EU_TEXT}"
- "${TX_MODEL_FORMAT_US_TEXT}"
initial_option: "${TX_MODEL_FORMAT_EU_TEXT}"
optimistic: true
restore_value: false
internal: false
entity_category: config
disabled_by_default: false
icon: mdi:tablet-cellphone
on_value:
then:
- binary_sensor.template.publish:
id: bs_pending_restart
state: true
- lambda: |-
id(is_us_model) = (x == "${TX_MODEL_FORMAT_US_TEXT}");
- id: sl_tx_model_gang
name: Model (Gang)
platform: template
options:
- "${TX_MODEL_1_GANG_TEXT}"
- "${TX_MODEL_2_GANG_TEXT}"
- "${TX_MODEL_3_GANG_TEXT}"
- "${TX_MODEL_4_GANG_TEXT}"
initial_option: "${TX_MODEL_1_GANG_TEXT}"
optimistic: true
restore_value: false
internal: false
entity_category: config
disabled_by_default: false
icon: mdi:dip-switch
on_value:
then:
- binary_sensor.template.publish:
id: bs_pending_restart
state: true
- lambda: |-
id(gang_count) = static_cast<uint8_t>(i) + 1;
tx_ultimate->set_gang_count(id(gang_count));
text_sensor:
- id: tx_fw_version
name: TX Ultimate Easy Firmware Version
platform: template
entity_category: diagnostic
icon: mdi:tag-text-outline
internal: false
update_interval: never
lambda: |-
return {"${version}"};
- id: tx_device_name
name: Device Name
platform: template
icon: mdi:identifier
entity_category: diagnostic
internal: false
disabled_by_default: false
update_interval: never
lambda: |-
return {"${name}"};
filters:
- lambda: |-
const std::string raw_name = x;
std::string result;
bool last_was_underscore = false;
for (const char& c : raw_name) {
if (isalnum(c)) {
result += tolower(c); // Add alphanumeric characters as lowercase
last_was_underscore = false;
} else if (!last_was_underscore) { // Replace non-alphanumeric with '_' but avoid consecutive '_'
result += '_';
last_was_underscore = true;
}
}
return result;
tx_ultimate_easy:
id: tx_ultimate
wifi:
id: wifi_component
...

View File

@@ -36,6 +36,8 @@ substitutions:
BUTTON_3_ID: '3' BUTTON_3_ID: '3'
BUTTON_4_ID: '4' BUTTON_4_ID: '4'
TAG_CORE_HW_BUTTONS: core.hw.buttons
binary_sensor: binary_sensor:
- &binary_sensor_button_base - &binary_sensor_button_base
id: bs_button_2 id: bs_button_2
@@ -63,6 +65,14 @@ esphome:
- -D TX_ULTIMATE_EASY_CORE_HW_BUTTONS - -D TX_ULTIMATE_EASY_CORE_HW_BUTTONS
globals: globals:
# Packed button actions - replaces 4 separate selects
- id: button_actions_packed
type: uint16_t
restore_value: true
initial_value: '4369' # Binary: 0001000100010001 = all buttons set to option 1 (default action)
# Bit layout: [button4(4)][button3(4)][button2(4)][button1(4)]
# Each button uses 4 bits: 0=None, 1=Toggle, 2=Failsafe, 3-15=Reserved for future options
- id: button_press_button - id: button_press_button
type: uint8_t type: uint8_t
restore_value: false restore_value: false
@@ -112,19 +122,20 @@ script:
action: string action: string
count: uint8_t count: uint8_t
then: then:
- homeassistant.event:
event: esphome.tx_ultimate_easy
data:
device_name: !lambda return tx_device_name->state.c_str();
firmware: ${version}
domain: touch
type: button
action: !lambda return action.c_str();
button_id: !lambda return button;
count: !lambda return count;
position: !lambda return id(button_press_position);
- lambda: |- - lambda: |-
ESP_LOGI("core_hw_buttons", "Button %" PRIu8 " action: '%s'", button, action.c_str()); // Send event to Home Assistant
esphome::api::CustomAPIDevice ha_event;
ha_event.fire_homeassistant_event("${EVENT_NAME}", {
{"device_name", id(tx_device_name).state.c_str()},
{"firmware", "${version}"},
{"domain", "touch"},
{"type", "button"},
{"action", action.c_str()},
{"button_id", std::to_string(button)},
{"count", std::to_string(count)},
{"position", std::to_string(id(button_press_position))}
});
ESP_LOGI("${TAG_CORE_HW_BUTTONS}", "Button %" PRIu8 " action: '%s'", button, action.c_str());
if (action == "click") { if (action == "click") {
switch (button) { switch (button) {
case ${BUTTON_1_ID}: case ${BUTTON_1_ID}:
@@ -208,38 +219,50 @@ script:
- id: !extend dump_config - id: !extend dump_config
then: then:
- lambda: |- - lambda: |-
std::string gang_count_plural_suffix = id(gang_count) > 1 ? "s" : "";
// Button's actions // Button's actions
ESP_LOGCONFIG("core_hw_buttons", "Button%s action%s:", ESP_LOGCONFIG("${TAG_CORE_HW_BUTTONS}", "Button%s action%s:",
id(gang_count_plural_suffix).c_str(), gang_count_plural_suffix.c_str(),
id(gang_count_plural_suffix).c_str()); gang_count_plural_suffix.c_str());
ESP_LOGCONFIG("core_hw_buttons", " Relay 1: %s", sl_button_1_action->has_state() ESP_LOGCONFIG("${TAG_CORE_HW_BUTTONS}", " Relay 1: %s", sl_button_1_action->has_state() ?
? sl_button_1_action->state.c_str() sl_button_1_action->state.c_str() : "Unknown");
: "Unknown");
if (id(gang_count) >= 2) if (id(gang_count) >= 2)
ESP_LOGCONFIG("core_hw_buttons", " Relay 2: %s", sl_button_2_action->has_state() ESP_LOGCONFIG("${TAG_CORE_HW_BUTTONS}", " Relay 2: %s", sl_button_2_action->has_state() ?
? sl_button_2_action->state.c_str() sl_button_2_action->state.c_str() : "Unknown");
: "Unknown");
if (id(gang_count) >= 3) if (id(gang_count) >= 3)
ESP_LOGCONFIG("core_hw_buttons", " Relay 3: %s", sl_button_3_action->has_state() ESP_LOGCONFIG("${TAG_CORE_HW_BUTTONS}", " Relay 3: %s", sl_button_3_action->has_state() ?
? sl_button_3_action->state.c_str() sl_button_3_action->state.c_str() : "Unknown");
: "Unknown");
if (id(gang_count) >= 4) if (id(gang_count) >= 4)
ESP_LOGCONFIG("core_hw_buttons", " Relay 4: %s", sl_button_4_action->has_state() ESP_LOGCONFIG("${TAG_CORE_HW_BUTTONS}", " Relay 4: %s", sl_button_4_action->has_state() ?
? sl_button_4_action->state.c_str() sl_button_4_action->state.c_str() : "Unknown");
: "Unknown");
- id: !extend dump_config_list_packages - id: !extend dump_config_list_packages
then: then:
- script.wait: dump_config - script.wait: dump_config
- lambda: |- - lambda: |-
// Check for requirements // Check for requirements
#if !defined(TX_ULTIMATE_EASY_CORE) #if !defined(TX_ULTIMATE_EASY_CORE_COMMON)
#error "The package TX-Ultimate-Easy-ESPHome_core.yaml is required." #error "The package TX-Ultimate-Easy-ESPHome_core_common.yaml is required."
#endif #endif
// Identify itself // Identify itself
ESP_LOGCONFIG(ESPHOME_PROJECT_NAME, " - Core - Hardware - Buttons"); ESP_LOGCONFIG(ESPHOME_PROJECT_NAME, " - Core - Hardware - Buttons");
- id: !extend restore_from_nvs
then:
- select.set_index:
id: sl_button_1_action
index: !lambda return std::min((uint8_t)(id(button_actions_packed) & 0x0F), (uint8_t)2);
- select.set_index:
id: sl_button_2_action
index: !lambda return std::min((uint8_t)((id(button_actions_packed) >> 4) & 0x0F), (uint8_t)2);
- select.set_index:
id: sl_button_3_action
index: !lambda return std::min((uint8_t)((id(button_actions_packed) >> 8) & 0x0F), (uint8_t)2);
- select.set_index:
id: sl_button_4_action
index: !lambda return std::min((uint8_t)((id(button_actions_packed) >> 12) & 0x0F), (uint8_t)2);
- id: !extend touch_on_multi_touch_release - id: !extend touch_on_multi_touch_release
then: then:
- script.execute: buttons_release - script.execute: buttons_release
@@ -292,19 +315,20 @@ script:
id(button_press_start_time) < current_time) { id(button_press_start_time) < current_time) {
uint32_t press_duration = current_time - id(button_press_start_time); uint32_t press_duration = current_time - id(button_press_start_time);
// Handle overflow (optional, since it's unlikely to happen here) // Handle overflow (optional, since it's unlikely to happen here)
ESP_LOGI("core_hw_buttons", "Button press duration: %" PRIu32 " ms", press_duration); ESP_LOGI("${TAG_CORE_HW_BUTTONS}", "Button press duration: %" PRIu32 " ms", press_duration);
if (press_duration < ${BUTTON_CLICK_MIN_LENGTH}) { if (press_duration < ${BUTTON_CLICK_MIN_LENGTH}) {
ESP_LOGW("core_hw_buttons", "Ignoring button press (too short)"); ESP_LOGW("${TAG_CORE_HW_BUTTONS}", "Ignoring button press (too short)");
} else if (press_duration >= ${BUTTON_CLICK_MIN_LENGTH} and } else if (press_duration >= ${BUTTON_CLICK_MIN_LENGTH} and
press_duration <= ${BUTTON_CLICK_MAX_LENGTH}) { // Short/normal click press_duration <= ${BUTTON_CLICK_MAX_LENGTH}) { // Short/normal click
button_click_event->execute(id(button_press_button), id(click_counter)); button_click_event->execute(id(button_press_button), id(click_counter));
} else if (press_duration >= ${BUTTON_LONG_PRESS_DELAY} and press_duration <= ${BUTTON_PRESS_TIMEOUT}) { } else if (press_duration >= ${BUTTON_LONG_PRESS_DELAY} and press_duration <= ${BUTTON_PRESS_TIMEOUT}) {
button_action->execute(id(button_press_button), "long_press", 1); button_action->execute(id(button_press_button), "long_press", 1);
} else if (press_duration > ${BUTTON_PRESS_TIMEOUT}) { // Timeout or invalid } else if (press_duration > ${BUTTON_PRESS_TIMEOUT}) { // Timeout or invalid
ESP_LOGW("core_hw_buttons", "Button press cancelled or timed out after ${BUTTON_PRESS_TIMEOUT} ms"); ESP_LOGW("${TAG_CORE_HW_BUTTONS}",
"Button press cancelled or timed out after ${BUTTON_PRESS_TIMEOUT} ms");
} }
} else { } else {
ESP_LOGW("core_hw_buttons", "Press event timestamp not recorded yet"); ESP_LOGW("${TAG_CORE_HW_BUTTONS}", "Press event timestamp not recorded yet");
} }
id(button_press_start_time) = 0; id(button_press_start_time) = 0;
@@ -327,11 +351,16 @@ select:
- "${BUTTON_1_ACTION_FAILSAFE_TEXT}" - "${BUTTON_1_ACTION_FAILSAFE_TEXT}"
initial_option: "${BUTTON_1_ACTION_TEXT}" initial_option: "${BUTTON_1_ACTION_TEXT}"
optimistic: true optimistic: true
restore_value: true restore_value: false
internal: true internal: true
entity_category: config entity_category: config
disabled_by_default: false disabled_by_default: false
icon: mdi:dip-switch icon: mdi:dip-switch
on_value:
then:
- lambda: |-
id(button_actions_packed) = (id(button_actions_packed) & ~(0x0F << 0)) |
((id(sl_button_1_action).active_index().value_or(1) & 0x0F) << 0);
- id: sl_button_2_action - id: sl_button_2_action
name: Button 2 action name: Button 2 action
@@ -342,6 +371,11 @@ select:
- "${BUTTON_2_ACTION_FAILSAFE_TEXT}" - "${BUTTON_2_ACTION_FAILSAFE_TEXT}"
initial_option: "${BUTTON_2_ACTION_TEXT}" initial_option: "${BUTTON_2_ACTION_TEXT}"
<<: *button_select_action_base <<: *button_select_action_base
on_value:
then:
- lambda: |-
id(button_actions_packed) = (id(button_actions_packed) & ~(0x0F << 4)) |
((id(sl_button_2_action).active_index().value_or(1) & 0x0F) << 4);
- id: sl_button_3_action - id: sl_button_3_action
name: Button 3 action name: Button 3 action
@@ -352,6 +386,11 @@ select:
- "${BUTTON_3_ACTION_FAILSAFE_TEXT}" - "${BUTTON_3_ACTION_FAILSAFE_TEXT}"
initial_option: "${BUTTON_3_ACTION_TEXT}" initial_option: "${BUTTON_3_ACTION_TEXT}"
<<: *button_select_action_base <<: *button_select_action_base
on_value:
then:
- lambda: |-
id(button_actions_packed) = (id(button_actions_packed) & ~(0x0F << 8)) |
((id(sl_button_3_action).active_index().value_or(1) & 0x0F) << 8);
- id: sl_button_4_action - id: sl_button_4_action
name: Button 4 action name: Button 4 action
@@ -362,4 +401,9 @@ select:
- "${BUTTON_4_ACTION_FAILSAFE_TEXT}" - "${BUTTON_4_ACTION_FAILSAFE_TEXT}"
initial_option: "${BUTTON_4_ACTION_TEXT}" initial_option: "${BUTTON_4_ACTION_TEXT}"
<<: *button_select_action_base <<: *button_select_action_base
on_value:
then:
- lambda: |-
id(button_actions_packed) = (id(button_actions_packed) & ~(0x0F << 12)) |
((id(sl_button_4_action).active_index().value_or(1) & 0x0F) << 12);
... ...

View File

@@ -25,9 +25,14 @@ substitutions:
LIGHT_TRANSITION_TURN_OFF: '0' # Transition time in msec LIGHT_TRANSITION_TURN_OFF: '0' # Transition time in msec
LIGHT_BRIGHTNESS_TURN_ON: '0' # Brightness (1 to 100%, 0 to not set it) LIGHT_BRIGHTNESS_TURN_ON: '0' # Brightness (1 to 100%, 0 to not set it)
LIGHT_FULL_RESTORE_MODE: RESTORE_DEFAULT_OFF LIGHT_FULL_RESTORE_MODE: ALWAYS_OFF
LIGHT_SIDES_RESTORE_MODE: RESTORE_DEFAULT_OFF LIGHT_SIDES_RESTORE_MODE: ALWAYS_OFF
LIGHT_INDIVIDUAL_RESTORE_MODE: RESTORE_DEFAULT_OFF LIGHT_INDIVIDUAL_RESTORE_MODE: ALWAYS_OFF
LIGHT_ATTRS_DEFAULT: '0x64FFFFFF' # brightness=100, RGB=255,255,255
LIGHT_DUAL_ATTRS_DEFAULT: '0x64FFFFFF64FFFFFF' # Both lights: brightness=100, RGB=255,255,255
TAG_CORE_HW_LEDS: core.hw.leds
esphome: esphome:
platformio_options: platformio_options:
@@ -35,32 +40,61 @@ esphome:
- -D TX_ULTIMATE_EASY_CORE_HW_LEDS - -D TX_ULTIMATE_EASY_CORE_HW_LEDS
globals: globals:
- id: gb_lights_1 # light_full with effect support
type: std::vector<light::LightState*> - id: led_full_attrs
restore_value: false type: uint32_t
- id: gb_lights_2 restore_value: true
type: std::vector<light::LightState*> initial_value: '${LIGHT_ATTRS_DEFAULT}'
restore_value: false # Basic attributes: brightness + RGB
# For light_full effects
- id: led_full_effect_packed
type: uint32_t
restore_value: true
initial_value: '0x00000000'
# Stores: effect_index(16 bits) + effect_speed(8 bits) + reserved(8 bits)
# 0 = no effect/solid color, 1-10 = effect indices
- id: led_main_packed_attrs
type: uint64_t
restore_value: true
initial_value: '${LIGHT_DUAL_ATTRS_DEFAULT}'
# light_eu(group1) + light_us(group2) - the main effect lights
- id: led_main_packed_effect
type: uint32_t
restore_value: true
initial_value: '0x00000000'
# Shared between light_eu and light_us
# Main user-facing LED lights only
- id: led_sides_packed_attrs
type: uint64_t
restore_value: true
initial_value: '${LIGHT_DUAL_ATTRS_DEFAULT}'
# EU: bottom(group1) + top(group2) OR US: left(group1) + right(group2)
light: light:
# These lights are controlling the LEDs on the device # These lights are controlling the LEDs on the device
- id: light_full - id: light_full
name: Light - All name: Light - All
internal: true internal: true
platform: esp32_rmt_led_strip
rgb_order: GRB
rmt_channel: 1
chipset: ws2812
num_leds: 32 num_leds: 32
pin: GPIO13 pin: GPIO13
restore_mode: ${LIGHT_FULL_RESTORE_MODE} restore_mode: ${LIGHT_FULL_RESTORE_MODE}
platform: esp32_rmt_led_strip
rgb_order: GRB
chipset: ws2812
max_refresh_rate: 33ms max_refresh_rate: 33ms
use_psram: true use_psram: false
# platform: neopixelbus
# type: GRB
# variant: WS2811
# Those lights are available based on the model selections # Those lights are available based on the model selections
- &light_partition_with_effects - &light_partition_with_effects_eu
id: light_eu id: light_eu
name: Light name: Light EU
platform: partition platform: partition
default_transition_length: ${default_transition_length} default_transition_length: ${default_transition_length}
internal: true internal: true
@@ -68,8 +102,76 @@ light:
effects: effects:
- addressable_rainbow: - addressable_rainbow:
name: "Rainbow" name: "Rainbow"
width: 28
- addressable_rainbow: - addressable_rainbow:
name: "Rainbow - Fast" name: "Rainbow - Fast"
width: 28
speed: 50
- pulse:
name: "Pulse"
transition_length: 1s
update_interval: 1s
- pulse:
name: "Pulse - Slow"
transition_length: 2s
update_interval: 2s
- addressable_scan:
name: "Scan"
move_interval: 100ms
scan_width: 3
- addressable_twinkle:
name: "Twinkle"
twinkle_probability: 5%
progress_interval: 4ms
- addressable_random_twinkle:
name: "Random Twinkle"
twinkle_probability: 20%
progress_interval: 32ms
- addressable_fireworks:
name: "Fireworks"
update_interval: 32ms
spark_probability: 10%
use_random_color: false
fade_out_rate: 120
- addressable_flicker:
name: "Flicker"
update_interval: 16ms
intensity: 5%
- addressable_nachtlicht:
name: "Nachtlicht"
intensity: 25%
- random:
name: "Random"
segments:
- id: light_full
from: 0
to: 27
on_state:
then:
- script.execute:
id: light_attributes_save_dual
light_ptr: !lambda return id(light_eu);
attr_global_ptr: !lambda return &id(led_main_packed_attrs);
group_index: 1
- script.execute:
id: light_effect_save
light_ptr: !lambda return id(light_eu);
effect_global_ptr: !lambda return &id(led_main_packed_effect);
- &light_partition_with_effects_us
id: light_us
name: Light US
platform: partition
default_transition_length: ${default_transition_length}
internal: true
restore_mode: ${LIGHT_SIDES_RESTORE_MODE}
effects:
- addressable_rainbow:
name: "Rainbow"
width: 32
- addressable_rainbow:
name: "Rainbow - Fast"
width: 32
speed: 50 speed: 50
- pulse: - pulse:
name: "Pulse" name: "Pulse"
@@ -103,40 +205,59 @@ light:
intensity: 5% intensity: 5%
- random: - random:
name: "Random" name: "Random"
segments:
- id: light_full
from: 0
to: 27
- id: light_us
name: Light
<<: *light_partition_with_effects
segments: segments:
- id: light_full - id: light_full
from: 0 from: 0
to: 31 to: 31
on_state:
then:
- script.execute:
id: light_attributes_save_dual
light_ptr: !lambda return id(light_us);
attr_global_ptr: !lambda return &id(led_main_packed_attrs);
group_index: 2
- script.execute:
id: light_effect_save
light_ptr: !lambda return id(light_us);
effect_global_ptr: !lambda return &id(led_main_packed_effect);
- &light_partition_with_effects_disabled - &light_partition_no_effects_disabled
id: light_eu_bottom id: light_eu_bottom
name: Light - Bottom name: Light - Bottom EU
platform: partition
disabled_by_default: true disabled_by_default: true
<<: *light_partition_with_effects default_transition_length: ${default_transition_length}
restore_mode: ${LIGHT_SIDES_RESTORE_MODE}
internal: true
segments: segments:
- id: light_full - id: light_full
from: 6 from: 6
to: 12 to: 12
reversed: true reversed: true
on_state:
- script.execute:
id: light_attributes_save_dual
light_ptr: !lambda return id(light_eu_bottom);
attr_global_ptr: !lambda return &id(led_sides_packed_attrs);
group_index: 1
- id: light_eu_left - id: light_eu_left
name: Light - Left name: Light - Left EU
<<: *light_partition_with_effects_disabled <<: *light_partition_no_effects_disabled
segments: segments:
- id: light_full - id: light_full
from: 13 from: 13
to: 19 to: 19
reversed: true reversed: true
on_state:
- script.execute:
id: light_attributes_save_dual
light_ptr: !lambda return id(light_eu_left);
attr_global_ptr: !lambda return &id(led_sides_packed_attrs);
group_index: 1
- id: light_eu_right - id: light_eu_right
name: Light - Right name: Light - Right EU
<<: *light_partition_with_effects_disabled <<: *light_partition_no_effects_disabled
segments: segments:
- id: light_full - id: light_full
from: 27 from: 27
@@ -146,33 +267,57 @@ light:
from: 0 from: 0
to: 5 to: 5
reversed: false reversed: false
on_state:
- script.execute:
id: light_attributes_save_dual
light_ptr: !lambda return id(light_eu_right);
attr_global_ptr: !lambda return &id(led_sides_packed_attrs);
group_index: 2
- id: light_eu_top - id: light_eu_top
name: Light - Top name: Light - Top EU
<<: *light_partition_with_effects_disabled <<: *light_partition_no_effects_disabled
segments: segments:
- id: light_full - id: light_full
from: 20 from: 20
to: 26 to: 26
reversed: false reversed: false
on_state:
- script.execute:
id: light_attributes_save_dual
light_ptr: !lambda return id(light_eu_top);
attr_global_ptr: !lambda return &id(led_sides_packed_attrs);
group_index: 2
- id: light_us_bottom - id: light_us_bottom
name: Light - Bottom name: Light - Bottom US
<<: *light_partition_with_effects_disabled <<: *light_partition_no_effects_disabled
segments: segments:
- id: light_full - id: light_full
from: 9 from: 9
to: 14 to: 14
reversed: true reversed: true
on_state:
- script.execute:
id: light_attributes_save_dual
light_ptr: !lambda return id(light_us_bottom);
attr_global_ptr: !lambda return &id(led_sides_packed_attrs);
group_index: 1
- id: light_us_left - id: light_us_left
name: Light - Left name: Light - Left US
<<: *light_partition_with_effects_disabled <<: *light_partition_no_effects_disabled
segments: segments:
- id: light_full - id: light_full
from: 15 from: 15
to: 24 to: 24
reversed: true reversed: true
on_state:
- script.execute:
id: light_attributes_save_dual
light_ptr: !lambda return id(light_us_left);
attr_global_ptr: !lambda return &id(led_sides_packed_attrs);
group_index: 1
- id: light_us_right - id: light_us_right
name: Light - Right name: Light - Right US
<<: *light_partition_with_effects_disabled <<: *light_partition_no_effects_disabled
segments: segments:
- id: light_full - id: light_full
from: 31 from: 31
@@ -182,14 +327,26 @@ light:
from: 0 from: 0
to: 8 to: 8
reversed: false reversed: false
on_state:
- script.execute:
id: light_attributes_save_dual
light_ptr: !lambda return id(light_us_right);
attr_global_ptr: !lambda return &id(led_sides_packed_attrs);
group_index: 2
- id: light_us_top - id: light_us_top
name: Light - Top name: Light - Top US
<<: *light_partition_with_effects_disabled <<: *light_partition_no_effects_disabled
segments: segments:
- id: light_full - id: light_full
from: 25 from: 25
to: 30 to: 30
reversed: false reversed: false
on_state:
- script.execute:
id: light_attributes_save_dual
light_ptr: !lambda return id(light_us_top);
attr_global_ptr: !lambda return &id(led_sides_packed_attrs);
group_index: 2
- &light_partition_individual_led - &light_partition_individual_led
id: light_00 id: light_00
@@ -446,178 +603,246 @@ script:
- id: !extend dump_config - id: !extend dump_config
then: then:
- lambda: |- - lambda: |-
// Relay's LEDs modes
ESP_LOGCONFIG("core_hw_leds", "Relay%s LEDs mode%s:",
id(gang_count_plural_suffix).c_str(),
id(gang_count_plural_suffix).c_str());
ESP_LOGCONFIG("core_hw_leds", " Relay 1: %s", id(is_us_model)
? sl_relay_1_light_mode_us->state.c_str()
: sl_relay_1_light_mode_eu->state.c_str());
if (id(gang_count) >= 2)
ESP_LOGCONFIG("core_hw_leds", " Relay 2: %s", id(is_us_model)
? sl_relay_2_light_mode_us->state.c_str()
: sl_relay_2_light_mode_eu->state.c_str());
if (id(gang_count) >= 3)
ESP_LOGCONFIG("core_hw_leds", " Relay 3: %s", id(is_us_model)
? sl_relay_3_light_mode_us->state.c_str()
: sl_relay_3_light_mode_eu->state.c_str());
if (id(gang_count) >= 4)
ESP_LOGCONFIG("core_hw_leds", " Relay 4: %s", id(is_us_model)
? sl_relay_4_light_mode_us->state.c_str()
: sl_relay_4_light_mode_eu->state.c_str());
// Transition times // Transition times
ESP_LOGCONFIG("core_hw_leds", "Transition times:"); ESP_LOGCONFIG("${TAG_CORE_HW_LEDS}", "Transition times:");
ESP_LOGCONFIG("core_hw_leds", " Default: ${default_transition_length}"); ESP_LOGCONFIG("${TAG_CORE_HW_LEDS}", " Default: ${default_transition_length}");
if (${LIGHT_TRANSITION_TURN_ON} > 0) if (${LIGHT_TRANSITION_TURN_ON} > 0)
ESP_LOGCONFIG("core_hw_leds", " Turning on: ${LIGHT_TRANSITION_TURN_ON}ms"); ESP_LOGCONFIG("${TAG_CORE_HW_LEDS}", " Turning on: ${LIGHT_TRANSITION_TURN_ON}ms");
else else
ESP_LOGCONFIG("core_hw_leds", " Turning on: Disabled"); ESP_LOGCONFIG("${TAG_CORE_HW_LEDS}", " Turning on: Disabled");
if (${LIGHT_TRANSITION_TURN_OFF} > 0) if (${LIGHT_TRANSITION_TURN_OFF} > 0)
ESP_LOGCONFIG("core_hw_leds", " Turning off: ${LIGHT_TRANSITION_TURN_OFF}ms"); ESP_LOGCONFIG("${TAG_CORE_HW_LEDS}", " Turning off: ${LIGHT_TRANSITION_TURN_OFF}ms");
else else
ESP_LOGCONFIG("core_hw_leds", " Turning off: Disabled"); ESP_LOGCONFIG("${TAG_CORE_HW_LEDS}", " Turning off: Disabled");
// Restore modes // Restore modes
ESP_LOGCONFIG("core_hw_leds", "LED restore modes:"); ESP_LOGCONFIG("${TAG_CORE_HW_LEDS}", "LED restore modes:");
ESP_LOGCONFIG("core_hw_leds", " Light - All: ${LIGHT_FULL_RESTORE_MODE}"); ESP_LOGCONFIG("${TAG_CORE_HW_LEDS}", " Light - All: ${LIGHT_FULL_RESTORE_MODE}");
ESP_LOGCONFIG("core_hw_leds", " Sides: ${LIGHT_SIDES_RESTORE_MODE}"); ESP_LOGCONFIG("${TAG_CORE_HW_LEDS}", " Sides: ${LIGHT_SIDES_RESTORE_MODE}");
ESP_LOGCONFIG("core_hw_leds", " Individual: ${LIGHT_INDIVIDUAL_RESTORE_MODE}"); ESP_LOGCONFIG("${TAG_CORE_HW_LEDS}", " Individual: ${LIGHT_INDIVIDUAL_RESTORE_MODE}");
- id: !extend dump_config_list_packages - id: !extend dump_config_list_packages
then: then:
- script.wait: dump_config - script.wait: dump_config
- lambda: |- - lambda: |-
// Check for requirements // Check for requirements
#if !defined(TX_ULTIMATE_EASY_CORE) #if !defined(TX_ULTIMATE_EASY_CORE_COMMON)
#error "The package TX-Ultimate-Easy-ESPHome_core.yaml is required." #error "The package TX-Ultimate-Easy-ESPHome_core_common.yaml is required."
#endif #endif
// Identify itself // Identify itself
ESP_LOGCONFIG(ESPHOME_PROJECT_NAME, " - Core - Hardware - LEDs"); ESP_LOGCONFIG(ESPHOME_PROJECT_NAME, " - Core - Hardware - LEDs");
- id: light_set_state - id: light_attributes_restore
mode: parallel mode: parallel
parameters: parameters:
light_group: uint8_t light_ptr: light::LightState*
light_index: uint8_t attr_global_ptr: uint32_t*
state: bool
then: then:
- lambda: |- - lambda: |-
static const uint32_t LIGHT_TRANSITION_TURN_ON = ${LIGHT_TRANSITION_TURN_ON}; if (!light_ptr) {
static const uint32_t LIGHT_TRANSITION_TURN_OFF = ${LIGHT_TRANSITION_TURN_OFF}; ESP_LOGE("${TAG_CORE_HW_LEDS}", "Invalid light pointer");
static const uint8_t LIGHT_BRIGHTNESS_TURN_ON = ${LIGHT_BRIGHTNESS_TURN_ON}; return;
}
if (!attr_global_ptr) {
ESP_LOGE("${TAG_CORE_HW_LEDS}", "Invalid attributes pointer");
return;
}
tx_ultimate_easy::LightAttributes attrs = tx_ultimate_easy::unpack_light_attributes(*attr_global_ptr);
auto call = light_ptr->make_call();
call.set_brightness(attrs.brightness / 100.0f);
call.set_rgb(attrs.red / 255.0f, attrs.green / 255.0f, attrs.blue / 255.0f);
call.perform();
ESP_LOGI("${TAG_CORE_HW_LEDS}", "Restored light attributes: brightness=%d%%, RGB=(%d,%d,%d)",
attrs.brightness, attrs.red, attrs.green, attrs.blue);
const std::string light_mode_group_1 = id(is_us_model) - id: light_attributes_restore_dual
? "${LIGHT_MODE_LEFT_TEXT}" mode: parallel
: "${LIGHT_MODE_BOTTOM_TEXT}"; parameters:
const std::string light_mode_group_2 = id(is_us_model) light_ptr: light::LightState*
? "${LIGHT_MODE_RIGHT_TEXT}" attr_global_ptr: uint64_t*
: "${LIGHT_MODE_TOP_TEXT}"; group_index: uint8_t # 1 or 2
then:
- lambda: |-
if (!light_ptr) {
ESP_LOGE("${TAG_CORE_HW_LEDS}", "Invalid light pointer");
return;
}
if (!attr_global_ptr) {
ESP_LOGE("${TAG_CORE_HW_LEDS}", "Invalid attributes pointer");
return;
}
if (group_index != 1 && group_index != 2) {
ESP_LOGE("${TAG_CORE_HW_LEDS}", "Invalid group_index=%d (expected 1 or 2)", group_index);
return;
}
auto attrs_pair = tx_ultimate_easy::unpack_dual_light_attributes(*attr_global_ptr);
tx_ultimate_easy::LightAttributes attrs = (group_index == 1) ? attrs_pair.first : attrs_pair.second;
switch (light_group) { auto call = light_ptr->make_call();
case 1: call.set_brightness(attrs.brightness / 100.0f);
if (light_index >= id(gb_lights_1).size()) { call.set_rgb(attrs.red / 255.0f, attrs.green / 255.0f, attrs.blue / 255.0f);
ESP_LOGE("core_hw_leds", "Invalid set with %s light for Relay %" PRIu8, call.perform();
light_mode_group_1.c_str(), light_index+1); ESP_LOGI("${TAG_CORE_HW_LEDS}", "Restored (group=%d) attributes: brightness=%d%%, RGB=(%d,%d,%d)",
return; group_index, attrs.brightness, attrs.red, attrs.green, attrs.blue);
}
if (state and !id(gb_lights_1)[light_index]->current_values.is_on()) { - id: light_attributes_save
ESP_LOGI("core_hw_leds", "Turn-on %s light for Relay %" PRIu8, mode: parallel
light_mode_group_1.c_str(), light_index+1); parameters:
auto call = id(gb_lights_1)[light_index]->turn_on(); light_ptr: light::LightState*
if (LIGHT_TRANSITION_TURN_ON > 0) attr_global_ptr: uint32_t*
call.set_transition_length(LIGHT_TRANSITION_TURN_ON); // in ms then:
if (LIGHT_BRIGHTNESS_TURN_ON > 0 and LIGHT_BRIGHTNESS_TURN_ON <= 100) - lambda: |-
call.set_brightness(LIGHT_BRIGHTNESS_TURN_ON / 100.0f); if (!light_ptr) {
call.perform(); ESP_LOGE("${TAG_CORE_HW_LEDS}", "Invalid light pointer");
} else if (!state and id(gb_lights_1)[light_index]->current_values.is_on()) { return;
ESP_LOGI("core_hw_leds", "Turn-off %s light for Relay %" PRIu8, }
light_mode_group_1.c_str(), light_index+1); if (!attr_global_ptr) {
auto call = id(gb_lights_1)[light_index]->turn_off(); ESP_LOGE("${TAG_CORE_HW_LEDS}", "Invalid attributes pointer");
if (LIGHT_TRANSITION_TURN_OFF > 0) return;
call.set_transition_length(LIGHT_TRANSITION_TURN_OFF); // in ms }
call.perform(); auto current = light_ptr->current_values;
} tx_ultimate_easy::LightAttributes attrs = {
break; .brightness = uint8_t(current.get_brightness() * 100.0f),
case 2: .red = uint8_t(current.get_red() * 255.0f),
if (light_index >= id(gb_lights_2).size()) { .green = uint8_t(current.get_green() * 255.0f),
ESP_LOGE("core_hw_leds", "Invalid set with %s light for Relay %" PRIu8, .blue = uint8_t(current.get_blue() * 255.0f)
light_mode_group_2.c_str(), light_index+1); };
return;
} *attr_global_ptr = tx_ultimate_easy::pack_light_attributes(attrs);
if (state and !id(gb_lights_2)[light_index]->current_values.is_on()) { ESP_LOGD("${TAG_CORE_HW_LEDS}", "Saved light attributes: brightness=%d%%, RGB=(%d,%d,%d)",
ESP_LOGI("core_hw_leds", "Turn-on %s light for Relay %" PRIu8, attrs.brightness, attrs.red, attrs.green, attrs.blue);
light_mode_group_2.c_str(), light_index+1);
auto call = id(gb_lights_2)[light_index]->turn_on(); - id: light_attributes_save_dual
if (LIGHT_TRANSITION_TURN_ON > 0) mode: queued # To prevent two instances trying to change the same global
call.set_transition_length(LIGHT_TRANSITION_TURN_ON); // in ms parameters:
if (LIGHT_BRIGHTNESS_TURN_ON > 0 and LIGHT_BRIGHTNESS_TURN_ON <= 100) light_ptr: light::LightState*
call.set_brightness(LIGHT_BRIGHTNESS_TURN_ON / 100.0f); attr_global_ptr: uint64_t*
call.perform(); group_index: uint8_t # 1 or 2 (which group within the dual pack)
} else if (!state and id(gb_lights_2)[light_index]->current_values.is_on()) { then:
ESP_LOGI("core_hw_leds", "Turn-off %s light for Relay %" PRIu8, - lambda: |-
light_mode_group_2.c_str(), light_index+1); if (!light_ptr) {
auto call = id(gb_lights_2)[light_index]->turn_off(); ESP_LOGE("${TAG_CORE_HW_LEDS}", "Invalid light pointer");
if (LIGHT_TRANSITION_TURN_OFF > 0) return;
call.set_transition_length(LIGHT_TRANSITION_TURN_OFF); // in ms }
call.perform(); if (!attr_global_ptr) {
} ESP_LOGE("${TAG_CORE_HW_LEDS}", "Invalid attributes pointer");
break; return;
}
if (group_index != 1 && group_index != 2) {
ESP_LOGE("${TAG_CORE_HW_LEDS}", "Invalid group_index=%d (expected 1 or 2)", group_index);
return;
}
auto current = light_ptr->current_values;
tx_ultimate_easy::LightAttributes new_attrs = {
.brightness = uint8_t(current.get_brightness() * 100.0f),
.red = uint8_t(current.get_red() * 255.0f),
.green = uint8_t(current.get_green() * 255.0f),
.blue = uint8_t(current.get_blue() * 255.0f)
};
// Unpack existing dual attributes
auto existing = tx_ultimate_easy::unpack_dual_light_attributes(*attr_global_ptr);
// Update the appropriate group
if (group_index == 1) {
*attr_global_ptr = tx_ultimate_easy::pack_dual_light_attributes(new_attrs, existing.second);
} else {
*attr_global_ptr = tx_ultimate_easy::pack_dual_light_attributes(existing.first, new_attrs);
} }
select: - id: light_effect_save
# EU relay light mode template mode: queued
- &relay_light_mode_eu parameters:
id: sl_relay_1_light_mode_eu light_ptr: light::LightState*
name: Relay 1 light mode effect_global_ptr: uint32_t*
platform: template then:
options: - lambda: |-
- "${LIGHT_MODE_DISABLED_TEXT}" if (!light_ptr || !effect_global_ptr) return;
- "${LIGHT_MODE_BOTTOM_TEXT}"
- "${LIGHT_MODE_TOP_TEXT}"
- "${LIGHT_MODE_BOTH_TEXT}"
initial_option: "${LIGHT_MODE_BOTTOM_TEXT}"
optimistic: true
restore_value: true
internal: true
entity_category: config
disabled_by_default: false
icon: mdi:dots-square
- id: sl_relay_2_light_mode_eu
name: Relay 2 light mode
<<: *relay_light_mode_eu
- id: sl_relay_3_light_mode_eu
name: Relay 3 light mode
<<: *relay_light_mode_eu
- id: sl_relay_4_light_mode_eu
name: Relay 4 light mode
<<: *relay_light_mode_eu
# US relay light mode template uint32_t effect_index = light_ptr->get_current_effect_index();
- &relay_light_mode_us // Store effect index (16 bits) + reserved space for future use
id: sl_relay_1_light_mode_us *effect_global_ptr = effect_index & 0xFFFF;
name: Relay 1 light mode
platform: template ESP_LOGD("${TAG_CORE_HW_LEDS}", "Saved effect index: %d", effect_index);
options:
- "${LIGHT_MODE_DISABLED_TEXT}" - id: light_effect_restore
- "${LIGHT_MODE_LEFT_TEXT}" mode: parallel
- "${LIGHT_MODE_RIGHT_TEXT}" parameters:
- "${LIGHT_MODE_BOTH_TEXT}" light_ptr: light::LightState*
initial_option: "${LIGHT_MODE_RIGHT_TEXT}" effect_global_ptr: uint32_t*
optimistic: true then:
restore_value: true - lambda: |-
internal: true if (!light_ptr || !effect_global_ptr) return;
entity_category: config
disabled_by_default: false uint32_t effect_index = *effect_global_ptr & 0xFFFF;
icon: mdi:dots-square
- id: sl_relay_2_light_mode_us if (effect_index == 0) return; // No effect
name: Relay 2 light mode if (effect_index > light_ptr->get_effect_count()) {
<<: *relay_light_mode_us ESP_LOGW("${TAG_CORE_HW_LEDS}", "Invalid effect index: %d (max: %d)",
- id: sl_relay_3_light_mode_us effect_index, light_ptr->get_effect_count());
name: Relay 3 light mode return;
<<: *relay_light_mode_us }
- id: sl_relay_4_light_mode_us
name: Relay 4 light mode auto call = light_ptr->make_call();
<<: *relay_light_mode_us call.set_effect(effect_index);
call.perform();
ESP_LOGI("${TAG_CORE_HW_LEDS}", "Restored effect: %s (index=%d)",
light_ptr->get_effect_name_by_index(effect_index).c_str(), effect_index);
- id: !extend restore_from_nvs
then:
# LED restoration
- if:
condition:
lambda: return id(is_us_model);
then:
# US model restoration
- script.execute:
id: light_attributes_restore_dual
light_ptr: !lambda return id(light_us);
attr_global_ptr: !lambda return &id(led_main_packed_attrs);
group_index: 2
- script.execute:
id: light_effect_restore
light_ptr: !lambda return id(light_us);
effect_global_ptr: !lambda return &id(led_main_packed_effect);
- script.execute:
id: light_attributes_restore_dual
light_ptr: !lambda return id(light_us_left);
attr_global_ptr: !lambda return &id(led_sides_packed_attrs);
group_index: 1
- script.execute:
id: light_attributes_restore_dual
light_ptr: !lambda return id(light_us_right);
attr_global_ptr: !lambda return &id(led_sides_packed_attrs);
group_index: 2
else:
# EU model restoration
- script.execute:
id: light_attributes_restore_dual
light_ptr: !lambda return id(light_eu);
attr_global_ptr: !lambda return &id(led_main_packed_attrs);
group_index: 1
- script.execute:
id: light_effect_restore
light_ptr: !lambda return id(light_eu);
effect_global_ptr: !lambda return &id(led_main_packed_effect);
- script.execute:
id: light_attributes_restore_dual
light_ptr: !lambda return id(light_eu_bottom);
attr_global_ptr: !lambda return &id(led_sides_packed_attrs);
group_index: 1
- script.execute:
id: light_attributes_restore_dual
light_ptr: !lambda return id(light_eu_top);
attr_global_ptr: !lambda return &id(led_sides_packed_attrs);
group_index: 2
# Light full restoration
- script.execute:
id: light_attributes_restore
light_ptr: !lambda return id(light_full);
attr_global_ptr: !lambda return &id(led_full_attrs);
... ...

View File

@@ -14,6 +14,8 @@
substitutions: substitutions:
TOUCH_POSITION_MAX_VALUE: '10' # Maximum touch position value returned by the touch pad via uart TOUCH_POSITION_MAX_VALUE: '10' # Maximum touch position value returned by the touch pad via uart
TAG_CORE_HW_TOUCH: core.hw.touch
binary_sensor: binary_sensor:
- &bs_button_event_template - &bs_button_event_template
id: bs_button_event_template id: bs_button_event_template
@@ -203,15 +205,6 @@ esphome:
build_flags: build_flags:
- -D TX_ULTIMATE_EASY_CORE_HW_TOUCH - -D TX_ULTIMATE_EASY_CORE_HW_TOUCH
external_components:
- source:
type: git
url: https://github.com/edwardtfn/TX-Ultimate-Easy
ref: v${version}
refresh: 1h
components:
- tx_ultimate_easy
number: number:
- id: nr_touch_duration - id: nr_touch_duration
name: Touch event duration name: Touch event duration
@@ -293,18 +286,18 @@ script:
- lambda: |- - lambda: |-
// Touch panel's state // Touch panel's state
if (sw_touch_panel_power->state) if (sw_touch_panel_power->state)
ESP_LOGCONFIG("core_hw_touch", "Touch panel - Power: On"); ESP_LOGCONFIG("${TAG_CORE_HW_TOUCH}", "Touch panel - Power: On");
else else
ESP_LOGW("core_hw_touch", "Touch panel - Power: OFF"); ESP_LOGW("${TAG_CORE_HW_TOUCH}", "Touch panel - Power: OFF");
ESP_LOGCONFIG("core_hw_touch", "Touch event duration: %.0fms", nr_touch_duration->state); ESP_LOGCONFIG("${TAG_CORE_HW_TOUCH}", "Touch event duration: %.0fms", nr_touch_duration->state);
- id: !extend dump_config_list_packages - id: !extend dump_config_list_packages
then: then:
- script.wait: dump_config - script.wait: dump_config
- lambda: |- - lambda: |-
// Check for requirements // Check for requirements
#if !defined(TX_ULTIMATE_EASY_CORE) #if !defined(TX_ULTIMATE_EASY_CORE_COMMON)
#error "The package TX-Ultimate-Easy-ESPHome_core.yaml is required." #error "The package TX-Ultimate-Easy-ESPHome_core_common.yaml is required."
#endif #endif
// Identify itself // Identify itself
@@ -318,7 +311,7 @@ script:
# - HW Vibration # - HW Vibration
- binary_sensor.template.publish: - binary_sensor.template.publish:
id: bs_multi_touch id: bs_multi_touch
state: OFF # yamllint disable-line rule:truthy state: ON # yamllint disable-line rule:truthy
- id: touch_on_press - id: touch_on_press
mode: restart mode: restart
@@ -373,88 +366,104 @@ switch:
entity_category: config entity_category: config
tx_ultimate_easy: tx_ultimate_easy:
id: tx_ultimate
uart: uart_touch uart: uart_touch
on_long_touch_release: on_long_touch_release:
- lambda: |- - lambda: |-
const uint8_t touch_position = static_cast<uint8_t>(touch.x); const uint8_t touch_position = static_cast<uint8_t>(touch.x);
if (touch_position > ${TOUCH_POSITION_MAX_VALUE}) { // Check for valid range if (touch_position > ${TOUCH_POSITION_MAX_VALUE}) { // Check for valid range
ESP_LOGE("core_hw_touch", "Invalid long-touch position: %" PRIu8, touch_position); ESP_LOGE("${TAG_CORE_HW_TOUCH}", "Invalid long-touch position: %" PRIu8, touch_position);
} else { } else {
ESP_LOGI("core_hw_touch", "Long-touch released at position %" PRIu8, touch_position); ESP_LOGI("${TAG_CORE_HW_TOUCH}", "Long-touch released at position %" PRIu8, touch_position);
} }
on_multi_touch_release: on_multi_touch_release:
- homeassistant.event: then:
event: esphome.tx_ultimate_easy - homeassistant.event:
data: event: ${EVENT_NAME}
device_name: !lambda return tx_device_name->state.c_str(); data:
firmware: ${version} device_name: !lambda return id(tx_device_name).state.c_str();
domain: touch firmware: ${version}
type: multi_touch domain: touch
action: release type: multi_touch
position: !lambda return touch.x; action: release
- lambda: ESP_LOGI("core_hw_touch", "Multi-touch released"); position: !lambda return std::to_string(touch.x);
- script.execute: touch_on_multi_touch_release - lambda: |-
ESP_LOGI("${TAG_CORE_HW_TOUCH}", "Multi-touch released");
touch_on_multi_touch_release->execute();
on_press: on_press:
- lambda: |- then:
const uint8_t position = static_cast<uint8_t>(touch.x); - lambda: |-
if (position > ${TOUCH_POSITION_MAX_VALUE}) { // Check for valid range const uint8_t position = static_cast<uint8_t>(touch.x);
ESP_LOGE("core_hw_touch", "Invalid touch position: %" PRIu8, position); if (position > ${TOUCH_POSITION_MAX_VALUE}) { // Check for valid range
} else { ESP_LOGE("${TAG_CORE_HW_TOUCH}", "Invalid touch position: %" PRIu8, position);
ESP_LOGI("core_hw_touch", "Pressed at position %" PRIu8, position); } else {
touch_on_press->execute(static_cast<uint8_t>(touch.button), position); ESP_LOGI("${TAG_CORE_HW_TOUCH}", "Pressed at position %" PRIu8, position);
} touch_on_press->execute(static_cast<uint8_t>(touch.button), position);
}
on_release: on_release:
- lambda: ESP_LOGI("core_hw_touch", "Released"); then:
- script.execute: touch_on_release - lambda: |-
ESP_LOGI("${TAG_CORE_HW_TOUCH}", "Released");
- script.execute: touch_on_release
on_swipe_left: on_swipe_left:
- homeassistant.event: then:
event: esphome.tx_ultimate_easy - homeassistant.event:
data: event: ${EVENT_NAME}
device_name: !lambda return tx_device_name->state.c_str(); data:
firmware: ${version} device_name: !lambda return id(tx_device_name).state.c_str();
domain: touch firmware: ${version}
type: swipe domain: touch
action: !lambda |- type: swipe
return sl_tx_model_format->state == "${TX_MODEL_FORMAT_EU_TEXT}" ? "left" : "down"; action: !lambda |-
swipe-direction: !lambda |- if (sl_tx_model_format->state == "${TX_MODEL_FORMAT_EU_TEXT}")
return sl_tx_model_format->state == "${TX_MODEL_FORMAT_EU_TEXT}" ? "left" : "down"; return "left";
position: !lambda return touch.x; else
- lambda: |- return "down";
ESP_LOGI("core_hw_touch", "Swipe %s", sl_tx_model_format->state == "${TX_MODEL_FORMAT_EU_TEXT}" swipe-direction: !lambda |-
? "left" if (sl_tx_model_format->state == "${TX_MODEL_FORMAT_EU_TEXT}")
: "down"); return "left";
- script.execute: touch_swipe_left else
return "down";
position: !lambda return std::to_string(touch.x);
- lambda: |-
ESP_LOGI("${TAG_CORE_HW_TOUCH}", "Swipe %s",
(sl_tx_model_format->state == "${TX_MODEL_FORMAT_EU_TEXT}") ? "left" : "down");
touch_swipe_left->execute();
on_swipe_right: on_swipe_right:
- homeassistant.event: then:
event: esphome.tx_ultimate_easy - homeassistant.event:
data: event: ${EVENT_NAME}
device_name: !lambda return tx_device_name->state.c_str(); data:
firmware: ${version} device_name: !lambda return id(tx_device_name).state.c_str();
domain: touch firmware: ${version}
type: swipe domain: touch
action: !lambda |- type: swipe
return sl_tx_model_format->state == "${TX_MODEL_FORMAT_EU_TEXT}" ? "right" : "up"; action: !lambda |-
swipe-direction: !lambda |- if (sl_tx_model_format->state == "${TX_MODEL_FORMAT_EU_TEXT}")
return sl_tx_model_format->state == "${TX_MODEL_FORMAT_EU_TEXT}" ? "right" : "up"; return "right";
position: !lambda return touch.x; else
- lambda: |- return "up";
ESP_LOGI("core_hw_touch", "Swipe %s", sl_tx_model_format->state == "${TX_MODEL_FORMAT_EU_TEXT}" swipe-direction: !lambda |-
? "right" if (sl_tx_model_format->state == "${TX_MODEL_FORMAT_EU_TEXT}")
: "up"); return "right";
- script.execute: touch_swipe_right else
return "up";
position: !lambda return std::to_string(touch.x);
- lambda: |-
ESP_LOGI("${TAG_CORE_HW_TOUCH}", "Swipe %s",
(sl_tx_model_format->state == "${TX_MODEL_FORMAT_EU_TEXT}") ? "right" : "up");
touch_swipe_right->execute();
on_touch_event: on_touch_event:
- lambda: |- - lambda: |-
ESP_LOGD("core_hw_touch", "Touch event:"); ESP_LOGD("${TAG_CORE_HW_TOUCH}", "Touch event:");
ESP_LOGD("core_hw_touch", " State: %i (%s)", touch.state, touch.state_str.c_str()); ESP_LOGD("${TAG_CORE_HW_TOUCH}", " State: %i (%s)", touch.state, touch.state_str.c_str());
ESP_LOGD("core_hw_touch", " Position: %i", touch.x); ESP_LOGD("${TAG_CORE_HW_TOUCH}", " Position: %i", touch.x);
uart: uart:
- id: uart_touch - id: uart_touch

View File

@@ -13,60 +13,8 @@
--- ---
packages: packages:
# yamllint disable rule:colons # yamllint disable rule:colons
core_hw_relays: !include TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml standard_hw_relays: !include TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml
core_hw_speaker: !include TX-Ultimate-Easy-ESPHome_standard_hw_speaker.yaml standard_hw_vibration: !include TX-Ultimate-Easy-ESPHome_standard_hw_vibration.yaml
core_hw_vibration: !include TX-Ultimate-Easy-ESPHome_standard_hw_vibration.yaml standard_media_player: !include TX-Ultimate-Easy-ESPHome_standard_media_player.yaml
# yamllint enable rule:colons # yamllint enable rule:colons
button:
- id: bt_restart
name: Restart
internal: false
platform: restart
captive_portal:
id: ap_captive_portal
esphome:
platformio_options:
build_flags:
- -D TX_ULTIMATE_EASY_STANDARD
script:
- id: !extend dump_config_list_packages
then:
- script.wait: dump_config
- lambda: |-
// Check for requirements
#if !defined(TX_ULTIMATE_EASY_CORE)
#error "The package TX-Ultimate-Easy-ESPHome_core.yaml is required."
#endif
// Identify itself
ESP_LOGCONFIG(ESPHOME_PROJECT_NAME, " - Standard");
- id: !extend dump_config_versions
then:
- lambda: |-
// Version information
ESP_LOGCONFIG("standard", "ESPHome builder: %s", esphome_fw_version->state.c_str());
improv_serial:
id: serial_improv
text_sensor:
- id: esphome_fw_version
name: ESPHome Version
platform: version
entity_category: diagnostic
internal: false
time:
- id: time_source
platform: homeassistant
web_server:
wifi:
ap:
... ...

View File

@@ -11,15 +11,20 @@
##### - For normal system use, modifications to this file are NOT required. ##### ##### - For normal system use, modifications to this file are NOT required. #####
#################################################################################################### ####################################################################################################
--- ---
substitutions:
SWITCH_AUDIO_AMPLIFIER_RESTORE_MODE: RESTORE_DEFAULT_ON
TAG_STD_HW_AUDIO: std.hw.audio
esphome: esphome:
platformio_options: platformio_options:
build_flags: build_flags:
- -D TX_ULTIMATE_EASY_STANDARD_HW_AUDIO - -D TX_ULTIMATE_EASY_STANDARD_HW_AUDIO
i2s_audio: i2s_audio:
id: if_i2s_audio - id: if_i2s_audio
i2s_bclk_pin: GPIO2 i2s_bclk_pin: GPIO2
i2s_lrclk_pin: GPIO4 i2s_lrclk_pin: GPIO4
script: script:
- id: !extend dump_config_list_packages - id: !extend dump_config_list_packages
@@ -27,10 +32,21 @@ script:
- script.wait: dump_config - script.wait: dump_config
- lambda: |- - lambda: |-
// Check for requirements // Check for requirements
#if !defined(TX_ULTIMATE_EASY_CORE) #if !defined(TX_ULTIMATE_EASY_CORE_COMMON)
#error "The package TX-Ultimate-Easy-ESPHome_core.yaml is required." #error "The package TX-Ultimate-Easy-ESPHome_core_common.yaml is required."
#endif #endif
// Identify itself // Identify itself
ESP_LOGCONFIG(ESPHOME_PROJECT_NAME, " - Standard - Hardware - Audio"); ESP_LOGCONFIG(ESPHOME_PROJECT_NAME, " - Standard - Hardware - Audio");
switch:
- id: sw_audio_amplifier
name: Audio Amplifier
platform: gpio
pin:
number: GPIO26
inverted: false
restore_mode: ${SWITCH_AUDIO_AMPLIFIER_RESTORE_MODE}
internal: true
entity_category: config
... ...

File diff suppressed because it is too large Load Diff

View File

@@ -16,6 +16,8 @@ substitutions:
# d=32 (32nd note), o=5 (octave 5), b=100 (tempo 100) # d=32 (32nd note), o=5 (octave 5), b=100 (tempo 100)
tone_volume_change: "scale_up:d=32,o=5,b=100:c,c#,d#,e,f#,g#,a#,b" # Use "none" to disable sound tone_volume_change: "scale_up:d=32,o=5,b=100:c,c#,d#,e,f#,g#,a#,b" # Use "none" to disable sound
TAG_STD_HW_SPEAKER: std.hw.speaker
packages: packages:
standard_hw_audio: !include TX-Ultimate-Easy-ESPHome_standard_hw_audio.yaml standard_hw_audio: !include TX-Ultimate-Easy-ESPHome_standard_hw_audio.yaml
@@ -31,18 +33,18 @@ api:
- lambda: |- - lambda: |-
rtttl_speaker->stop(); rtttl_speaker->stop();
if (speaker_volume->state > 0) { if (speaker_volume->state > 0) {
ESP_LOGI("standard_speaker", "Play tone: '%s'", tone.c_str()); ESP_LOGI("${TAG_STD_HW_SPEAKER}", "Play tone: '%s'", tone.c_str());
rtttl_speaker->play(tone.c_str()); rtttl_speaker->play(tone.c_str());
} }
esphome: esphome:
platformio_options: platformio_options:
build_flags: build_flags:
- -D TX_ULTIMATE_EASY_STANDARD_SPEAKER - -D TX_ULTIMATE_EASY_STANDARD_HW_SPEAKER
number: number:
- id: speaker_volume - id: speaker_volume
name: Sound - Volume name: Speaker Volume
platform: template platform: template
max_value: 100 max_value: 100
min_value: 0 min_value: 0
@@ -76,8 +78,8 @@ script:
- script.wait: dump_config - script.wait: dump_config
- lambda: |- - lambda: |-
// Check for requirements // Check for requirements
#if !defined(TX_ULTIMATE_EASY_CORE) #if !defined(TX_ULTIMATE_EASY_CORE_COMMON)
#error "The package TX-Ultimate-Easy-ESPHome_core.yaml is required." #error "The package TX-Ultimate-Easy-ESPHome_core_common.yaml is required."
#endif #endif
#if !defined(TX_ULTIMATE_EASY_STANDARD_HW_AUDIO) #if !defined(TX_ULTIMATE_EASY_STANDARD_HW_AUDIO)
#error "The package TX-Ultimate-Easy-ESPHome_standard_hw_audio.yaml is required." #error "The package TX-Ultimate-Easy-ESPHome_standard_hw_audio.yaml is required."
@@ -179,4 +181,8 @@ speaker:
i2s_audio_id: if_i2s_audio i2s_audio_id: if_i2s_audio
i2s_comm_fmt: stand_msb i2s_comm_fmt: stand_msb
channel: mono channel: mono
timeout: never
bits_per_sample: 16bit
sample_rate: 48000
buffer_duration: 500ms
... ...

View File

@@ -14,6 +14,8 @@
substitutions: substitutions:
vibration_max_duration: 2s vibration_max_duration: 2s
TAG_STD_HW_VIBRATION: std.hw.vibration
binary_sensor: binary_sensor:
- id: bs_vibrating - id: bs_vibrating
name: Vibrating name: Vibrating
@@ -62,24 +64,24 @@ script:
then: then:
- lambda: |- - lambda: |-
// Configuration // Configuration
ESP_LOGCONFIG("standard_hw_vibration", "Touch - Vibration feedback: %s", ESP_LOGCONFIG("${TAG_STD_HW_VIBRATION}", "Touch - Vibration feedback: %s",
sl_touch_vibration_feedback->state.c_str()); sl_touch_vibration_feedback->state.c_str());
ESP_LOGCONFIG("standard_hw_vibration", "Vibrate duration: %.0fms", nr_vibrating_duration->state); ESP_LOGCONFIG("${TAG_STD_HW_VIBRATION}", "Vibrate duration: %.0fms", nr_vibrating_duration->state);
ESP_LOGCONFIG("standard_hw_vibration", "Vibrate max duration: ${vibration_max_duration}"); ESP_LOGCONFIG("${TAG_STD_HW_VIBRATION}", "Vibrate max duration: ${vibration_max_duration}");
// State // State
if (bs_vibrating->state) if (bs_vibrating->state)
ESP_LOGW("standard_hw_vibration", "Vibrating now: YES"); ESP_LOGW("${TAG_STD_HW_VIBRATION}", "Vibrating now: YES");
else else
ESP_LOGCONFIG("standard_hw_vibration", "Vibrating now: No"); ESP_LOGCONFIG("${TAG_STD_HW_VIBRATION}", "Vibrating now: No");
- id: !extend dump_config_list_packages - id: !extend dump_config_list_packages
then: then:
- script.wait: dump_config - script.wait: dump_config
- lambda: |- - lambda: |-
// Check for requirements // Check for requirements
#if !defined(TX_ULTIMATE_EASY_CORE) #if !defined(TX_ULTIMATE_EASY_CORE_COMMON)
#error "The package TX-Ultimate-Easy-ESPHome_core.yaml is required." #error "The package TX-Ultimate-Easy-ESPHome_core_common.yaml is required."
#endif #endif
// Identify itself // Identify itself
@@ -149,20 +151,17 @@ switch:
pin: GPIO21 pin: GPIO21
restore_mode: ALWAYS_OFF restore_mode: ALWAYS_OFF
internal: true internal: true
on_turn_on: on_state:
then: then:
- binary_sensor.template.publish: - binary_sensor.template.publish:
id: bs_vibrating id: bs_vibrating
state: ON # yamllint disable-line rule:truthy state: !lambda return x;
on_turn_on:
then:
- delay: ${vibration_max_duration} - delay: ${vibration_max_duration}
- if: - if:
condition: condition:
switch.is_on: sw_vibration_motor switch.is_on: sw_vibration_motor
then: then:
- switch.turn_off: sw_vibration_motor - switch.turn_off: sw_vibration_motor
on_turn_off:
then:
- binary_sensor.template.publish:
id: bs_vibrating
state: OFF # yamllint disable-line rule:truthy
... ...

View File

@@ -0,0 +1,118 @@
####################################################################################################
##### TX Ultimate Easy for ESPHome #####
##### Repository: https://github.com/edwardtfn/TX-Ultimate-Easy #####
####################################################################################################
##### Purpose: ESPHome - Add-on - Media player #####
####################################################################################################
##### Author: edwardtfn - https://github.com/edwardtfn - https://buymeacoffee.com/edwardfirmo #####
####################################################################################################
##### NOTE: #####
##### - Make changes ONLY if absolutely necessary and you have the required knowledge. #####
##### - For normal system use, modifications to this file are NOT required. #####
####################################################################################################
---
substitutions:
TAG_STD_MEDIA_PLAYER: "std.media-player"
packages:
standard_hw_speaker: !include TX-Ultimate-Easy-ESPHome_standard_hw_speaker.yaml
esphome:
platformio_options:
build_flags:
- -D TX_ULTIMATE_EASY_STANDARD_MEDIA_PLAYER
globals:
- id: last_media_player_volume
type: uint8_t
restore_value: false
initial_value: '100'
- id: persistent_media_player_volume
type: uint8_t
restore_value: true
initial_value: '100'
media_player:
- id: mp_media_player
name: Media Player
internal: false
platform: speaker
codec_support_enabled: true
buffer_size: 250000
task_stack_in_psram: false
announcement_pipeline:
speaker: announcement_spk_resampling_input
format: WAV
num_channels: 1
sample_rate: 48000
media_pipeline:
speaker: media_spk_resampling_input
format: FLAC
num_channels: 1
sample_rate: 48000
on_state:
then:
- lambda: |-
// Check if volume has changed and update number entity
id(persistent_media_player_volume) = static_cast<uint8_t>(mp_media_player->volume * 100.0f);
if (id(persistent_media_player_volume) != id(last_media_player_volume)) {
id(last_media_player_volume) = id(persistent_media_player_volume);
ESP_LOGD("${TAG_STD_MEDIA_PLAYER}", "Media player volume changed to %" PRIu8 "%%, updating number entity",
id(persistent_media_player_volume));
}
script:
- id: !extend boot_sequence
then:
- script.execute: sync_initial_volume
- id: !extend dump_config
then:
- lambda: |-
// Volume sync configuration
ESP_LOGCONFIG("${TAG_STD_MEDIA_PLAYER}", "Media Player Volume Sync:");
ESP_LOGCONFIG("${TAG_STD_MEDIA_PLAYER}", " Persistent volume: %" PRIu8 "%",
id(persistent_media_player_volume));
ESP_LOGCONFIG("${TAG_STD_MEDIA_PLAYER}", " Current volume: %.1f%%", mp_media_player->volume * 100.0f);
- id: !extend dump_config_list_packages
then:
- script.wait: dump_config
- lambda: |-
// Check for requirements
#if !defined(TX_ULTIMATE_EASY_CORE_COMMON)
#error "The package TX-Ultimate-Easy-ESPHome_core_common.yaml is required."
#endif // TX_ULTIMATE_EASY_CORE
#if !defined(TX_ULTIMATE_EASY_STANDARD_HW_SPEAKER)
#error "The package TX-Ultimate-Easy-ESPHome_standard_hw_speaker.yaml is required."
#endif // TX_ULTIMATE_EASY_STANDARD_HW_SPEAKER
// Identify itself
ESP_LOGCONFIG(ESPHOME_PROJECT_NAME, " - Standard - Media Player");
- id: sync_initial_volume
mode: restart
then:
- delay: 2s # Wait for media player to initialize
- media_player.volume_set: # Sync initial volume from persisted number entity to media player
id: mp_media_player
volume: !lambda return id(persistent_media_player_volume) / 100.0f;
- lambda: |-
id(last_media_player_volume) = id(persistent_media_player_volume);
ESP_LOGI("${TAG_STD_MEDIA_PLAYER}", "Initial volume sync: %.1f%% (restored from persistence)",
id(persistent_media_player_volume));
speaker:
- platform: mixer
id: mixer_speaker_embedded
output_speaker: speaker_embedded
source_speakers:
- id: announcement_speaker_embedded
- id: media_speaker_embedded
- platform: resampler
id: announcement_spk_resampling_input
output_speaker: announcement_speaker_embedded
- platform: resampler
id: media_spk_resampling_input
output_speaker: media_speaker_embedded
...

148
README.md
View File

@@ -27,6 +27,48 @@
[buymeacoffee-shield]: https://img.shields.io/static/v1?label=Buy%20me%20an%20ice%20cream&message=❄&color=blue [buymeacoffee-shield]: https://img.shields.io/static/v1?label=Buy%20me%20an%20ice%20cream&message=❄&color=blue
[buymeacoffee]: https://www.buymeacoffee.com/edwardfirmo [buymeacoffee]: https://www.buymeacoffee.com/edwardfirmo
> [!WARNING]
> ESPHome builder v2025.8.0+ is required
## Framework Migration to ESP-IDF
> [!IMPORTANT]
> **ESP-IDF Framework Migration**
>
> Starting with version **2025.8.0**, TX Ultimate Easy will use **ESP-IDF** as the default framework, following ESPHome's direction towards ESP-IDF for better performance, stability, and feature support.
>
> **What this means for you:**
> - **New installations**: Will automatically use ESP-IDF (no action required)
> - **Existing Arduino users**: Can continue using Arduino but with limited support
> - **Migration recommended**: For better performance and full feature support
>
> **Arduino Framework Support:**
> - Still functional but **no longer actively tested**
> - **Limited support** will be provided for Arduino-specific issues
> - May miss out on new features optimized for ESP-IDF
### Migration Guidelines
#### For New Users
No action required - ESP-IDF will be used automatically.
#### For Existing Arduino Users
**Option 1: Migrate to ESP-IDF (Recommended)**
1. **Best practice**: Flash via serial/USB to ensure proper partition setup
2. **Alternative**: If serial flashing isn't possible, flash OTA twice consecutively to improve success rate
**Option 2: Continue with Arduino**
Add this to your YAML configuration to force Arduino framework:
```yaml
esp32:
framework:
type: arduino
```
> [!WARNING]
> **Migration via OTA:** When switching frameworks via OTA, the partition table may not update correctly. Flash twice in succession to ensure both firmware partitions contain the latest firmware and prevent boot failures.
## Integration with Home Assistant ## Integration with Home Assistant
TX Ultimate Easy exposes your device's components (sensors, touch panel, relays, etc.) to Home Assistant, TX Ultimate Easy exposes your device's components (sensors, touch panel, relays, etc.) to Home Assistant,
@@ -70,9 +112,10 @@ All device behaviors can be customized through Home Assistant automations withou
- **Flexible Light Control**: Customize LED behavior and effects - **Flexible Light Control**: Customize LED behavior and effects
- **Touch Panel Support**: Full support for touch gestures and multi-touch capabilities - **Touch Panel Support**: Full support for touch gestures and multi-touch capabilities
- **Advanced Automations**: Create complex automations using Home Assistant's powerful automation engine - **Advanced Automations**: Create complex automations using Home Assistant's powerful automation engine
- **BLE Proxy Support**: Optional Bluetooth Low Energy proxy functionality - **Bluetooth Proxy Support**: Compatible with ESPHome's bluetooth_proxy component for BLE device integration
- **Audio Feedback**: Built-in speaker support for audible feedback - **Audio Feedback**: Built-in speaker support for audible feedback
- **Haptic Feedback**: Vibration motor support for tactile feedback - **Haptic Feedback**: Vibration motor support for tactile feedback
- **ESP-IDF Framework**: Enhanced performance and stability with ESP-IDF support
## Hardware Support ## Hardware Support
@@ -111,28 +154,104 @@ Follow these steps to get your TX Ultimate device up and running with ESPHome.
4. Copy this basic configuration to your new device: 4. Copy this basic configuration to your new device:
```yaml ```yaml
substitutions: substitutions:
name: tx-ultimate-easy # Change this to your device name name: tx-ultimate-easy
friendly_name: TX Ultimate Easy friendly_name: TX Ultimate Easy
packages:
remote_package:
url: https://github.com/edwardtfn/TX-Ultimate-Easy
ref: main # For latest stable release
files:
- ESPHome/TX-Ultimate-Easy-ESPHome.yaml
wifi: wifi:
ssid: !secret wifi_ssid ssid: !secret wifi_ssid
password: !secret wifi_password password: !secret wifi_password
```
packages:
remote_package:
url: https://github.com/edwardtfn/TX-Ultimate-Easy
ref: main # Or you can specify a version, like `ref: v2024.12.6` or `ref: latest`
refresh: 5min
files:
- ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml # Core (essential) packages
- ESPHome/TX-Ultimate-Easy-ESPHome_standard.yaml # Non-essential, but recommended packages
```
You can also use a specific version tag for better control over updates: You can also use a specific version tag for better control over updates:
```yaml ```yaml
ref: v2024.12.2 # Using specific version for controlled updates ref: v2024.12.2 # Using specific version for controlled updates
``` ```
**Note:** [Click here](https://github.com/edwardtfn/TX-Ultimate-Easy/tags) for a full list of versions available. **Notes:**
- [Click here](https://github.com/edwardtfn/TX-Ultimate-Easy/tags) for a full list of versions available.
- [Click here](TX-Ultimate-Easy-ESPHome.yaml)
for the latest version of this yaml.
5. Click "Save" and then "Install" 5. Click "Save" and then "Install"
> [!IMPORTANT]
> Starting from version 2025.1.0, non-essential components like `web_server`, `captive_portal`, and `wifi`
> are no longer included in the core package. If you need these components,
> you must add them manually to your local configuration file.
> For example:
> ```yaml
> # Add these to your configuration if needed
> web_server:
> captive_portal:
> wifi:
> ap: # Access point configuration
> ```
### Optional Features
#### Bluetooth Proxy
TX Ultimate Easy is fully compatible with ESPHome's `bluetooth_proxy` component.
To enable Bluetooth proxy functionality, add the following to your device configuration:
```yaml
bluetooth_proxy:
# Optional: Configure specific settings
# active: true
```
**Requirements for Bluetooth Proxy:**
- ESP-IDF framework (default framework, not recommended with Arduino framework)
- Sufficient memory (TX Ultimate Easy components are compatible,
but adding additional custom components may require memory optimization)
> [!NOTE]
> Bluetooth proxy functionality is provided by ESPHome's native component.
> TX Ultimate Easy ensures compatibility but does not include it by default to maintain optimal memory usage.
### Advanced Settings
For more granular control over components,
you can use our [advanced configuration template](TX-Ultimate-Easy-ESPHome_advanced.yaml).
This template allows you to selectively include specific packages, which can be useful for:
- Troubleshooting specific components
- Reducing memory usage by excluding unused features
- Customizing functionality for specific use cases
Here's an example of the advanced configuration:
```yaml
packages:
remote_package:
url: https://github.com/edwardtfn/TX-Ultimate-Easy
ref: main # Or you can specify a version, like `ref: v2024.12.6` or `ref: latest`
refresh: 5min
files:
# Core (essential) packages
- ESPHome/TX-Ultimate-Easy-ESPHome_core_common.yaml # Basic shared settings
- ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml # Button logic
- ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml # LED configuration
- ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_touch.yaml # Touch panel support
# Optional but recommended packages
- ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml # Relay control
- ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_vibration.yaml # Haptic feedback
# Audio options (use none or choose only one - using both will fail)
- ESPHome/TX-Ultimate-Easy-ESPHome_standard_media_player.yaml # Media player (Recommended for most users)
# - ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_speaker.yaml # Basic speaker
# Optional: Add Bluetooth proxy support
bluetooth_proxy:
```
> [!NOTE]
> Use the advanced configuration with caution. Excluding core packages may cause instability or reduced functionality.
### Device Flashing ### Device Flashing
Initial flashing must be done via serial connection. Initial flashing must be done via serial connection.
@@ -232,7 +351,7 @@ If the device isn't discovered automatically:
- Button actions - Button actions
3. Optional: Configure advanced features 3. Optional: Configure advanced features
- LED behaviors - LED behaviors
- Touch sensitivity - Touch duration
- Haptic feedback - Haptic feedback
- Audio feedback - Audio feedback
4. Test your configuration: 4. Test your configuration:
@@ -252,17 +371,18 @@ After installation, you can:
2. Customize touch behaviors 2. Customize touch behaviors
3. Set up LED patterns and effects 3. Set up LED patterns and effects
4. Create automations 4. Create automations
5. Enable optional features like BLE proxy 5. Enable optional features like Bluetooth proxy
## Configuration Options ## Configuration Options
TX Ultimate Easy offers extensive configuration options: TX Ultimate Easy offers extensive configuration options:
- Touch panel sensitivity and gestures - Touch panel gestures
- LED colors, patterns, and behaviors - LED colors, patterns, and behaviors
- Relay modes and functions - Relay modes and functions
- Audio and haptic feedback settings - Audio and haptic feedback settings
- Network and connectivity options - Network and connectivity options
- Optional Bluetooth proxy functionality
## Contributing ## Contributing

View File

@@ -16,11 +16,10 @@ wifi:
packages: packages:
remote_package: remote_package:
url: https://github.com/edwardtfn/TX-Ultimate-Easy url: https://github.com/edwardtfn/TX-Ultimate-Easy
ref: latest # Or you can specify a version, like `ref: v2024.12.6` ref: stable # Or you can specify a version, like `ref: v2024.12.6` or `ref: latest` to the latest non-stable
refresh: 5min refresh: 5min
files: files:
- ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml # Core (essential) packages - ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml # Core (essential) packages
- ESPHome/TX-Ultimate-Easy-ESPHome_standard.yaml # Non-essential, but recommended packages - ESPHome/TX-Ultimate-Easy-ESPHome_standard.yaml # Non-essential, but recommended packages
# - ESPHome/TX-Ultimate-Easy-ESPHome_addon_ble_proxy.yaml # Adds BLE proxy support # - ESPHome/TX-Ultimate-Easy-ESPHome_addon_ble_proxy.yaml # Adds BLE proxy support
# - ESPHome/TX-Ultimate-Easy-ESPHome_addon_media_player.yaml # Adds experimental Media Player (limited)
... ...

View File

@@ -0,0 +1,38 @@
####################################################################################################
##### TX Ultimate Easy for ESPHome #####
##### Repository: https://github.com/edwardtfn/TX-Ultimate-Easy #####
####################################################################################################
##### Author: edwardtfn - https://github.com/edwardtfn - https://buymeacoffee.com/edwardfirmo #####
####################################################################################################
##### WARNING: This is an advanced configuration template. Use with caution as improper #####
##### configuration may lead to reduced functionality or system instability. #####
####################################################################################################
---
substitutions:
name: tx-ultimate-easy-adv
friendly_name: TX Ultimate Easy Advanced
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
packages:
remote_package:
url: https://github.com/edwardtfn/TX-Ultimate-Easy
ref: stable # Or you can specify a version, like `ref: v2024.12.6` or `ref: latest` to the latest non-stable
refresh: 5min
files:
# Core (essential) packages - Do not disable these unless you know what you're doing
- ESPHome/TX-Ultimate-Easy-ESPHome_core_common.yaml # Basic shared settings
- ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml # Button logic
- ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml # LED configuration
- ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_touch.yaml # Touch panel support
# Optional functionality - Can be enabled/disabled as needed
- ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml # Relay control
- ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_vibration.yaml # Haptic feedback
# Audio options (use none or choose only one - using both will fail)
- ESPHome/TX-Ultimate-Easy-ESPHome_standard_media_player.yaml # Media player (Recommended for most users)
# - ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_speaker.yaml # Basic speaker
...

View File

@@ -1,11 +1,12 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome import automation
from esphome.components import uart from esphome.components import uart
from esphome.components.esp32 import add_idf_sdkconfig_option
from esphome.const import ( from esphome.const import (
CONF_ID, CONF_ID,
) )
from esphome.core import CORE
from esphome import automation
CODEOWNERS = ["@edwardtfn"] CODEOWNERS = ["@edwardtfn"]
DEPENDENCIES = ['uart'] DEPENDENCIES = ['uart']
@@ -109,3 +110,17 @@ async def to_code(config):
await cg.register_component(var, config) await cg.register_component(var, config)
await uart.register_uart_device(var, config) await uart.register_uart_device(var, config)
await register_tx_ultimate_easy(var, config) await register_tx_ultimate_easy(var, config)
if CORE.using_esp_idf:
add_idf_sdkconfig_option("CONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST", True)
add_idf_sdkconfig_option("CONFIG_BT_BLE_DYNAMIC_ENV_MEMORY", True)
add_idf_sdkconfig_option("CONFIG_ESP32_REV_MIN_3", True)
add_idf_sdkconfig_option("CONFIG_MBEDTLS_DYNAMIC_BUFFER", True)
add_idf_sdkconfig_option("CONFIG_MBEDTLS_DYNAMIC_FREE_CA_CERT", True)
add_idf_sdkconfig_option("CONFIG_MBEDTLS_DYNAMIC_FREE_CONFIG_DATA", True)
add_idf_sdkconfig_option("CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC", True)
add_idf_sdkconfig_option("CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY", True)
add_idf_sdkconfig_option("CONFIG_SPIRAM_RODATA", True)
add_idf_sdkconfig_option("CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP", True)
cg.add_define("USE_TX_ULTIMATE_EASY")

View File

@@ -0,0 +1,58 @@
// tx_ultimate_easy_light.cpp
#ifdef TX_ULTIMATE_EASY_CORE_HW_LEDS
#include "esphome/core/log.h"
#include "tx_ultimate_easy_light.h"
#include <cinttypes>
#include <string>
namespace esphome {
namespace tx_ultimate_easy {
// Log tag
static const char *TAG = "tx_ultimate_easy.light";
// Constant definitions
const LightAttributes LIGHT_ATTRS_DEFAULT = {100, 255, 255, 255};
const uint32_t LIGHT_ATTRS_DEFAULT_PACKED = 0x64FFFFFF;
const uint64_t DEFAULT_DUAL_LIGHT_ATTRS_PACKED = 0x64FFFFFF64FFFFFF;
// Function implementations
uint32_t pack_light_attributes(const LightAttributes& attr) {
return (uint32_t(std::min(attr.brightness, uint8_t(100))) << 24) |
(uint32_t(attr.red) << 16) |
(uint32_t(attr.green) << 8) |
(uint32_t(attr.blue));
}
LightAttributes unpack_light_attributes(uint32_t packed) {
return {
.brightness = std::min(uint8_t((packed >> 24) & 0xFF), uint8_t(100)),
.red = uint8_t((packed >> 16) & 0xFF),
.green = uint8_t((packed >> 8) & 0xFF),
.blue = uint8_t(packed & 0xFF)
};
}
uint64_t pack_dual_light_attributes(const LightAttributes& group1_attrs, const LightAttributes& group2_attrs) {
uint32_t group1_packed = pack_light_attributes(group1_attrs);
uint32_t group2_packed = pack_light_attributes(group2_attrs);
return (uint64_t(group2_packed) << 32) | uint64_t(group1_packed);
}
std::pair<LightAttributes, LightAttributes> unpack_dual_light_attributes(uint64_t packed) {
uint32_t group1_packed = uint32_t(packed & 0xFFFFFFFF); // Lower 32 bits
uint32_t group2_packed = uint32_t((packed >> 32) & 0xFFFFFFFF); // Upper 32 bits
return std::make_pair(
unpack_light_attributes(group1_packed),
unpack_light_attributes(group2_packed)
);
}
} // namespace tx_ultimate_easy
} // namespace esphome
#endif // TX_ULTIMATE_EASY_CORE_HW_LEDS

View File

@@ -0,0 +1,116 @@
// tx_ultimate_easy_light.h
#pragma once
#ifdef TX_ULTIMATE_EASY_CORE_HW_LEDS
#include "esphome/core/automation.h"
#include "esphome/core/component.h"
#include "esphome/components/uart/uart.h"
#include "esphome/components/uart/uart_component.h"
#include "esphome/components/binary_sensor/binary_sensor.h"
#include "esphome/components/switch/switch.h"
#include "esphome/components/light/addressable_light.h"
#include <array>
namespace esphome {
namespace tx_ultimate_easy {
/**
* @brief Light attributes structure for storing color and brightness data.
*
* This structure represents the visual attributes of a light without the state (on/off).
* It's designed to be packed efficiently into a 32-bit integer for NVS storage.
* RGB components use the full 8-bit range (0-255) while brightness uses percentage (0-100).
*/
struct LightAttributes {
uint8_t brightness; ///< Light brightness percentage (0-100, where 100 = full brightness)
uint8_t red; ///< Red color component (0-255)
uint8_t green; ///< Green color component (0-255)
uint8_t blue; ///< Blue color component (0-255)
};
/**
* @brief Packs LightAttributes structure into a 32-bit integer for storage.
*
* Bit layout: [brightness(8)][red(8)][green(8)][blue(8)]
* This allows efficient storage in ESPHome globals with restore_value support.
* Note: Brightness values > 100 will be clamped to 100 during packing.
*
* @param attr The LightAttributes structure to pack
* @return uint32_t Packed representation suitable for NVS storage
*
* @example
* LightAttributes attrs = {75, 255, 128, 64};
* uint32_t packed = pack_light_attributes(attrs); // Returns 0x4BFF8040
*/
uint32_t pack_light_attributes(const LightAttributes& attr);
/**
* @brief Unpacks a 32-bit integer back into LightAttributes structure.
*
* Reverses the packing operation to extract individual color and brightness values.
* Brightness values > 100 in the packed data will be clamped to 100.
*
* @param packed The packed 32-bit representation
* @return LightAttributes The unpacked structure with individual components
*
* @example
* uint32_t packed = 0x4BFF8040;
* LightAttributes attrs = unpack_light_attributes(packed);
* // attrs.brightness = 75, attrs.red = 255, attrs.green = 128, attrs.blue = 64
*/
LightAttributes unpack_light_attributes(uint32_t packed);
/**
* @brief Default light attributes for initialization.
*
* Provides sensible defaults for new lights:
* - Brightness: 100 (full brightness)
* - Red: 255 (full intensity)
* - Green: 255 (full intensity)
* - Blue: 255 (full intensity)
* Result: Pure white color at full brightness
*/
extern const LightAttributes LIGHT_ATTRS_DEFAULT;
/**
* @brief Pre-packed version of DEFAULT_LIGHT_ATTRS for convenience.
*
* This constant contains LIGHT_ATTRS_DEFAULT already packed into uint32_t format,
* suitable for direct use in ESPHome YAML initial_value fields.
*
* Value: 0x64FFFFFF (brightness=100, RGB=255,255,255)
*/
extern const uint32_t LIGHT_ATTRS_DEFAULT_PACKED;
/**
* @brief Packs two LightAttributes into a single uint64_t for dual light storage.
*
* Bit layout: [group2_light(32)][group1_light(32)]
* Each light uses 32 bits: [brightness(8)][red(8)][green(8)][blue(8)]
*
* @param group1_attrs First light attributes (stored in lower 32 bits)
* @param group2_attrs Second light attributes (stored in upper 32 bits)
* @return uint64_t Packed representation suitable for NVS storage
*/
uint64_t pack_dual_light_attributes(const LightAttributes& group1_attrs, const LightAttributes& group2_attrs);
/**
* @brief Unpacks uint64_t back into two LightAttributes structures.
*
* @param packed The packed 64-bit representation
* @return std::pair<LightAttributes, LightAttributes> Pair of (group1, group2) attributes
*/
std::pair<LightAttributes, LightAttributes> unpack_dual_light_attributes(uint64_t packed);
/**
* @brief Pre-packed version of dual default attributes for convenience.
*
* Value: 0x64FFFFFF64FFFFFF (both lights: brightness=100, RGB=255,255,255)
*/
extern const uint64_t DEFAULT_DUAL_LIGHT_ATTRS_PACKED;
} // namespace tx_ultimate_easy
} // namespace esphome
#endif // TX_ULTIMATE_EASY_CORE_HW_LEDS

View File

@@ -1,13 +1,18 @@
// tx_ultimate_easy.cpp // tx_ultimate_easy_touch.cpp
#ifdef TX_ULTIMATE_EASY_CORE_HW_TOUCH
#include "esphome/core/log.h" #include "esphome/core/log.h"
#include "tx_ultimate_easy.h" #include "tx_ultimate_easy_touch.h"
#include <cinttypes> #include <cinttypes>
#include <string> #include <string>
namespace esphome { namespace esphome {
namespace tx_ultimate_easy { namespace tx_ultimate_easy {
// Log tag
static const char *TAG = "tx_ultimate_easy.touch";
void TxUltimateEasy::setup() { void TxUltimateEasy::setup() {
ESP_LOGI(TAG, "TX Ultimate Easy is initialized"); ESP_LOGI(TAG, "TX Ultimate Easy is initialized");
} }
@@ -69,9 +74,10 @@ namespace esphome {
if (this->gang_count_ == 1) if (this->gang_count_ == 1)
return 1; return 1;
//Calculate button number Change to round up insted of truncate (integer division) // Calculate button width (rounds up to ensure full coverage)
const uint8_t width = (TOUCH_MAX_POSITION + gang_count_) / this->gang_count_; // Width of each button region const uint8_t width =
if (width < 1) // Invalid width - and prevents division by zero removed "width > gang_count_" issue with 2 gang (TOUCH_MAX_POSITION + this->gang_count_) / this->gang_count_; // Width of each button region
if (width < 1) // Invalid width - and prevents division by zero
return 0; return 0;
const uint8_t button = std::min( const uint8_t button = std::min(
static_cast<uint8_t>((position / width) + 1), // Convert position to button index static_cast<uint8_t>((position / width) + 1), // Convert position to button index
@@ -141,8 +147,36 @@ namespace esphome {
switch (uart_received_bytes[4]) { switch (uart_received_bytes[4]) {
case TOUCH_STATE_RELEASE: case TOUCH_STATE_RELEASE:
case TOUCH_STATE_MULTI_TOUCH: case TOUCH_STATE_MULTI_TOUCH:
return uart_received_bytes[5];
case TOUCH_STATE_SWIPE_LEFT: case TOUCH_STATE_SWIPE_LEFT:
case TOUCH_STATE_SWIPE_RIGHT: case TOUCH_STATE_SWIPE_RIGHT:
// uart_received_bytes[5] indicates swipe direction:
// 12 = find last touch position (scan right to left)
// 13 = find first touch position (scan left to right)
// Bytes 6-7 contain a 10-bit bitmap of touch positions:
// Byte 7: bits 0-7 represent positions 1-8
// Byte 6: bits 0-1 represent positions 9-10
switch (uart_received_bytes[5]) {
case 12:
for (uint8_t i = 10; i > 0; i--) {
if (i > 8
? uart_received_bytes[6] & (1 << (i - 9))
: uart_received_bytes[7] & (1 << (i - 1))) {
return i;
}
}
break;
case 13:
for (uint8_t i = 1; i <= 10; i++) {
if (i > 8
? uart_received_bytes[6] & (1 << (i - 9))
: uart_received_bytes[7] & (1 << (i - 1))) {
return i;
}
}
break;
}
return uart_received_bytes[5]; return uart_received_bytes[5];
default: default:
@@ -152,8 +186,10 @@ namespace esphome {
int TxUltimateEasy::get_touch_state(const std::array<int, UART_RECEIVED_BYTES_SIZE> &uart_received_bytes) { int TxUltimateEasy::get_touch_state(const std::array<int, UART_RECEIVED_BYTES_SIZE> &uart_received_bytes) {
int state = uart_received_bytes[4]; int state = uart_received_bytes[4];
if (state == TOUCH_STATE_PRESS && uart_received_bytes[5] != 0) state = TOUCH_STATE_RELEASE; if (state == TOUCH_STATE_PRESS && uart_received_bytes[5] != 0)
if (state == TOUCH_STATE_RELEASE && uart_received_bytes[5] == TOUCH_STATE_MULTI_TOUCH) state = TOUCH_STATE_MULTI_TOUCH; state = TOUCH_STATE_RELEASE;
if (state == TOUCH_STATE_RELEASE && uart_received_bytes[5] == TOUCH_STATE_MULTI_TOUCH)
state = TOUCH_STATE_MULTI_TOUCH;
if (state == TOUCH_STATE_SWIPE) { if (state == TOUCH_STATE_SWIPE) {
state = (uart_received_bytes[5] == TOUCH_STATE_SWIPE_RIGHT) ? TOUCH_STATE_SWIPE_RIGHT : state = (uart_received_bytes[5] == TOUCH_STATE_SWIPE_RIGHT) ? TOUCH_STATE_SWIPE_RIGHT :
(uart_received_bytes[5] == TOUCH_STATE_SWIPE_LEFT) ? TOUCH_STATE_SWIPE_LEFT : state; (uart_received_bytes[5] == TOUCH_STATE_SWIPE_LEFT) ? TOUCH_STATE_SWIPE_LEFT : state;
@@ -195,3 +231,5 @@ namespace esphome {
} // namespace tx_ultimate_easy } // namespace tx_ultimate_easy
} // namespace esphome } // namespace esphome
#endif // TX_ULTIMATE_EASY_CORE_HW_TOUCH

View File

@@ -1,7 +1,9 @@
// tx_ultimate_easy.h // tx_ultimate_easy_touch.h
#pragma once #pragma once
#ifdef TX_ULTIMATE_EASY_CORE_HW_TOUCH
#include "esphome/core/automation.h" #include "esphome/core/automation.h"
#include "esphome/core/component.h" #include "esphome/core/component.h"
#include "esphome/components/uart/uart.h" #include "esphome/components/uart/uart.h"
@@ -31,9 +33,6 @@ namespace esphome {
constexpr int VALID_DATA_BYTE_2 = 0x01; constexpr int VALID_DATA_BYTE_2 = 0x01;
constexpr int VALID_DATA_BYTE_3 = 0x02; constexpr int VALID_DATA_BYTE_3 = 0x02;
// Log tag
static const char *TAG = "tx_ultimate_easy";
struct TouchPoint { struct TouchPoint {
uint8_t button = 0; uint8_t button = 0;
int8_t x = -1; int8_t x = -1;
@@ -84,3 +83,5 @@ namespace esphome {
} // namespace tx_ultimate_easy } // namespace tx_ultimate_easy
} // namespace esphome } // namespace esphome
#endif // TX_ULTIMATE_EASY_CORE_HW_TOUCH

View File

@@ -1 +1 @@
2024.12.26 2025.10.1

View File

@@ -1 +1 @@
version: 2024.12.26 version: 2025.10.1