Merge branch 'main' into dev

This commit is contained in:
Edward Firmo
2025-05-02 18:30:35 +02:00
committed by GitHub
46 changed files with 1177 additions and 1308 deletions

View File

@@ -1,6 +1,10 @@
--- ---
name: Validate ESPHome (Latest) name: Validate ESPHome (Latest)
permissions:
contents: read
pull-requests: read
on: # yamllint disable-line rule:truthy on: # yamllint disable-line rule:truthy
workflow_dispatch: workflow_dispatch:
@@ -27,36 +31,234 @@ jobs:
- 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 {} +
build_basic: build_ard_core:
name: Basic name: Core (Arduino)
needs: code_scan
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@main - uses: actions/checkout@main
- name: Build core - name: Build Core (Arduino)
uses: esphome/build-action@main
with:
yaml-file: ".test/esphome_ard_core.yaml"
version: latest
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: latest
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:
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 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_bluetooth_proxy_4: build_idf_basic:
name: Bluetooth Proxy (IDF) name: Basic (IDF)
needs: build_idf_core
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@main - uses: actions/checkout@main
- name: Build core - name: Build Basic (IDF)
uses: esphome/build-action@main uses: esphome/build-action@main
with: with:
yaml-file: ".test/esphome_idf_bluetooth_proxy.yaml" yaml-file: ".test/esphome_idf_basic.yaml"
version: latest version: latest
build_bluetooth_proxy_53: build_idf53_basic:
name: Bluetooth Proxy (IDF v5.3) name: Basic (IDF v5.3)
needs: build_idf53_core
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@main - uses: actions/checkout@main
- name: Build core - name: Build Basic (IDF v5.3)
uses: esphome/build-action@main uses: esphome/build-action@main
with: with:
yaml-file: ".test/esphome_idf53_bluetooth_proxy.yaml" yaml-file: ".test/esphome_idf53_basic.yaml"
version: latest
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: 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:
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: 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:
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: latest
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: 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
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: latest
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: latest
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: latest
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: latest version: latest
... ...

View File

@@ -1,27 +1,29 @@
--- ---
name: Validate ESPHome (Beta) name: Validate ESPHome (Beta)
permissions:
contents: read
pull-requests: read
on: # yamllint disable-line rule:truthy on: # yamllint disable-line rule:truthy
push: push:
paths: paths:
- "*.yaml" - "**/*.yml"
- "ESPHome/*.yaml" - "**/*.yaml"
- ".test/*.yaml" - "**/*.h"
- "*.h" - "**/*.cpp"
- "*.c" - "**/*.c"
- "*.cpp" - "**/*.py"
- "*.py"
branches:
- main
pull_request: pull_request:
paths: paths:
- "*.yaml" - "**/*.yml"
- "ESPHome/*.yaml" - "**/*.yaml"
- ".test/*.yaml" - "**/*.h"
- "*.h" - "**/*.cpp"
- "*.c" - "**/*.c"
- "*.cpp" - "**/*.py"
- "*.py"
workflow_dispatch: workflow_dispatch:
concurrency: concurrency:
@@ -47,36 +49,234 @@ jobs:
- 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 {} +
build_basic: build_ard_core:
name: Basic name: Core (Arduino)
needs: code_scan
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@main - uses: actions/checkout@main
- name: Build core - 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 uses: esphome/build-action@main
with: with:
yaml-file: ".test/esphome_ard_basic.yaml" yaml-file: ".test/esphome_ard_basic.yaml"
version: beta version: beta
build_bluetooth_proxy_4: build_idf_basic:
name: Bluetooth Proxy (IDF) name: Basic (IDF)
needs: build_idf_core
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@main - uses: actions/checkout@main
- name: Build core - name: Build Basic (IDF)
uses: esphome/build-action@main uses: esphome/build-action@main
with: with:
yaml-file: ".test/esphome_idf_bluetooth_proxy.yaml" yaml-file: ".test/esphome_idf_basic.yaml"
version: beta version: beta
build_bluetooth_proxy_53: build_idf53_basic:
name: Bluetooth Proxy (IDF v5.3) name: Basic (IDF v5.3)
needs: build_idf53_core
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@main - uses: actions/checkout@main
- name: Build core - name: Build Basic (IDF v5.3)
uses: esphome/build-action@main uses: esphome/build-action@main
with: with:
yaml-file: ".test/esphome_idf53_bluetooth_proxy.yaml" 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 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
@@ -56,10 +55,9 @@ jobs:
- name: Update stable tag - name: Update stable tag
if: | if: |
success() && ( success() &&
github.event_name == 'workflow_dispatch' && inputs.update_stable || github.event_name == 'workflow_dispatch' &&
github.event_name == 'push' inputs.update_stable
)
run: | run: |
# Verify version bump was successful # Verify version bump was successful
if [ ! -f "./versioning/VERSION" ]; then if [ ! -f "./versioning/VERSION" ]; then

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
...

19
.test/common_idf.yaml Normal file
View File

@@ -0,0 +1,19 @@
---
packages:
common: !include common.yaml
esp32:
framework:
type: esp-idf
light:
- id: !extend light_full
platform: esp32_rmt_led_strip
rgb_order: GRB
rmt_channel: 1
chipset: ws2812
max_refresh_rate: 33ms
use_psram: false
type: !remove
variant: !remove
...

10
.test/common_idf53.yaml Normal file
View File

@@ -0,0 +1,10 @@
---
packages:
common_idf: !include common_idf.yaml
esp32:
framework:
type: esp-idf
version: 5.3.1
platform_version: 6.9.0
...

View File

@@ -1,8 +1,6 @@
--- ---
packages: packages:
basic_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml # Basic package common_arduino: !include common_ard.yaml
core_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml
esp32: standard_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_standard.yaml
framework:
type: arduino
... ...

View File

@@ -0,0 +1,7 @@
---
packages:
common_arduino: !include common_ard.yaml
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
...

View File

@@ -0,0 +1,5 @@
---
packages:
common_arduino: !include common_ard.yaml
core_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml
...

View File

@@ -0,0 +1,6 @@
---
packages:
common_arduino: !include common_ard.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
...

View File

@@ -0,0 +1,6 @@
---
packages:
common_arduino: !include common_ard.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
...

View File

@@ -0,0 +1,6 @@
---
packages:
common_arduino: !include common_ard.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
...

View File

@@ -0,0 +1,8 @@
---
packages:
common_arduino: !include common_ard.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_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
...

View File

@@ -0,0 +1,11 @@
---
packages:
common_idf53: !include common_idf53.yaml
core_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml
standard_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_standard.yaml
media_player: !remove
switch:
- id: !remove sw_speaker_amplifier
...

View File

@@ -1,6 +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
addon_bluetooth_proxy: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_addon_ble_proxy.yaml addon_bluetooth_proxy: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_addon_ble_proxy.yaml
esp32: esp32:

View File

@@ -0,0 +1,5 @@
---
packages:
common_idf53: !include common_idf53.yaml
core_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml
...

View File

@@ -0,0 +1,6 @@
---
packages:
common_idf53: !include common_idf53.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
...

View File

@@ -0,0 +1,6 @@
---
packages:
common_idf53: !include common_idf53.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
...

View File

@@ -0,0 +1,6 @@
---
packages:
common_idf53: !include common_idf53.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
...

View File

@@ -0,0 +1,11 @@
---
packages:
common_idf: !include common_idf.yaml
core_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml
standard_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_standard.yaml
media_player: !remove
switch:
- id: !remove sw_speaker_amplifier
...

View File

@@ -1,5 +1,7 @@
--- ---
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
addon_bluetooth_proxy: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_addon_ble_proxy.yaml addon_bluetooth_proxy: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_addon_ble_proxy.yaml
... ...

View File

@@ -0,0 +1,5 @@
---
packages:
common_idf: !include common_idf.yaml
core_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml
...

View File

@@ -0,0 +1,6 @@
---
packages:
common_idf: !include common_idf.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
...

View File

@@ -0,0 +1,6 @@
---
packages:
common_idf: !include common_idf.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
...

View File

@@ -0,0 +1,6 @@
---
packages:
common_idf: !include common_idf.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
...

BIN
Assets/Logo_dark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

BIN
Assets/Logo_dark.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 261 KiB

BIN
Assets/Logo_light.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

View File

@@ -11,22 +11,14 @@
##### - For normal system use, modifications to this file are NOT required. ##### ##### - For normal system use, modifications to this file are NOT required. #####
#################################################################################################### ####################################################################################################
--- ---
packages:
standard_hw_speaker: !include TX-Ultimate-Easy-ESPHome_standard_hw_speaker.yaml
bluetooth_proxy: bluetooth_proxy:
esp32: esp32:
framework: framework:
type: esp-idf type: esp-idf
sdkconfig_options:
CONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST: "y"
CONFIG_BT_BLE_DYNAMIC_ENV_MEMORY: "y"
CONFIG_ESP32_REV_MIN_3: "y"
CONFIG_MBEDTLS_DYNAMIC_BUFFER: "y"
CONFIG_MBEDTLS_DYNAMIC_FREE_CA_CERT: "y"
CONFIG_MBEDTLS_DYNAMIC_FREE_CONFIG_DATA: "y"
CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC: "y"
CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY: "y"
CONFIG_SPIRAM_RODATA: "y"
CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP: "y"
esphome: esphome:
platformio_options: platformio_options:
@@ -39,11 +31,28 @@ light:
- id: !extend light_full - id: !extend light_full
platform: esp32_rmt_led_strip platform: esp32_rmt_led_strip
rgb_order: GRB rgb_order: GRB
rmt_channel: 0 rmt_channel: 1
chipset: ws2812 chipset: ws2812
max_refresh_rate: 33ms
use_psram: false
type: !remove type: !remove
variant: !remove variant: !remove
max_refresh_rate: 33ms
media_player: !remove media_player: !remove
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");
switch:
- id: !remove sw_speaker_amplifier
... ...

View File

@@ -0,0 +1,68 @@
####################################################################################################
##### 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,202 +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"
packages: packages:
# yamllint disable rule:colons # yamllint disable rule:colons
core_hw_buttons: !include TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml core_common: !include TX-Ultimate-Easy-ESPHome_core_common.yaml
core_hw_leds: !include TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml core_hw_buttons: !include TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml
core_hw_relays: !include TX-Ultimate-Easy-ESPHome_core_hw_relays.yaml core_hw_leds: !include TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml
core_hw_speaker: !include TX-Ultimate-Easy-ESPHome_core_hw_speaker.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
core_hw_vibration: !include TX-Ultimate-Easy-ESPHome_core_hw_vibration.yaml
# yamllint enable rule:colons # yamllint enable rule:colons
api:
id: api_server
on_client_connected:
then:
- script.execute: publish_device_info
button:
- id: bt_restart
name: Restart
internal: false
platform: restart
captive_portal:
id: ap_captive_portal
esp32:
board: esp32dev
flash_size: 8MB
esphome:
name: ${name}
friendly_name: ${friendly_name}
comment: TX Ultimate Easy
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
improv_serial:
id: serial_improv
logger:
level: DEBUG
ota:
platform: esphome
psram:
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
- id: boot_sequence
mode: restart
then:
- script.execute: publish_device_info
- 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
- 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:
- lambda: tx_ultimate->set_gang_count(static_cast<uint8_t>(i)+1);
text_sensor:
- id: tx_fw_version
name: 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;
time:
- id: time_source
platform: homeassistant
web_server:
wifi:
ap:
... ...

View File

@@ -19,6 +19,10 @@ substitutions:
BUTTON_2_ACTION_TEXT: "Relay 2 (toggle)" BUTTON_2_ACTION_TEXT: "Relay 2 (toggle)"
BUTTON_3_ACTION_TEXT: "Relay 3 (toggle)" BUTTON_3_ACTION_TEXT: "Relay 3 (toggle)"
BUTTON_4_ACTION_TEXT: "Relay 4 (toggle)" BUTTON_4_ACTION_TEXT: "Relay 4 (toggle)"
BUTTON_1_ACTION_FAILSAFE_TEXT: "Relay 1 (API failsafe only)"
BUTTON_2_ACTION_FAILSAFE_TEXT: "Relay 2 (API failsafe only)"
BUTTON_3_ACTION_FAILSAFE_TEXT: "Relay 3 (API failsafe only)"
BUTTON_4_ACTION_FAILSAFE_TEXT: "Relay 4 (API failsafe only)"
BUTTON_CLICK_MIN_LENGTH: '50' # The minimum duration the click should last, in msec BUTTON_CLICK_MIN_LENGTH: '50' # The minimum duration the click should last, in msec
BUTTON_CLICK_MAX_LENGTH: '350' # The maximum duration the click should last, in msec BUTTON_CLICK_MAX_LENGTH: '350' # The maximum duration the click should last, in msec
@@ -53,6 +57,11 @@ binary_sensor:
internal: false internal: false
<<: *binary_sensor_button_base <<: *binary_sensor_button_base
esphome:
platformio_options:
build_flags:
- -D TX_ULTIMATE_EASY_CORE_HW_BUTTONS
globals: globals:
- id: button_press_button - id: button_press_button
type: uint8_t type: uint8_t
@@ -82,28 +91,19 @@ script:
- id: boot_initialize_buttons - id: boot_initialize_buttons
mode: restart mode: restart
then: then:
- wait_until:
condition:
- lambda: return sl_tx_model_gang->active_index().has_value();
- lambda: |- - lambda: |-
const uint8_t num_gangs = (sl_tx_model_gang->active_index().has_value()) ?
(sl_tx_model_gang->active_index().value() + 1) : 0;
if (num_gangs < 1 || num_gangs > 4) {
ESP_LOGE("core_hw_buttons", "Invalid number of gangs: %" PRIu8, num_gangs);
return;
}
bs_button_1->publish_state(false); bs_button_1->publish_state(false);
bs_button_1->set_internal(num_gangs < 1); bs_button_1->set_internal(id(gang_count) < 1);
sl_button_1_action->set_internal(num_gangs < 1); sl_button_1_action->set_internal(id(gang_count) < 1);
bs_button_2->publish_state(false); bs_button_2->publish_state(false);
bs_button_2->set_internal(num_gangs < 2); bs_button_2->set_internal(id(gang_count) < 2);
sl_button_2_action->set_internal(num_gangs < 2); sl_button_2_action->set_internal(id(gang_count) < 2);
bs_button_3->publish_state(false); bs_button_3->publish_state(false);
bs_button_3->set_internal(num_gangs < 3); bs_button_3->set_internal(id(gang_count) < 3);
sl_button_3_action->set_internal(num_gangs < 3); sl_button_3_action->set_internal(id(gang_count) < 3);
bs_button_4->publish_state(false); bs_button_4->publish_state(false);
bs_button_4->set_internal(num_gangs < 4); bs_button_4->set_internal(id(gang_count) < 4);
sl_button_4_action->set_internal(num_gangs < 4); sl_button_4_action->set_internal(id(gang_count) < 4);
- id: button_action - id: button_action
mode: parallel mode: parallel
@@ -205,6 +205,41 @@ script:
} }
} }
- id: !extend dump_config
then:
- lambda: |-
// Button's actions
ESP_LOGCONFIG("core_hw_buttons", "Button%s action%s:",
id(gang_count_plural_suffix).c_str(),
id(gang_count_plural_suffix).c_str());
ESP_LOGCONFIG("core_hw_buttons", " Relay 1: %s", sl_button_1_action->has_state()
? sl_button_1_action->state.c_str()
: "Unknown");
if (id(gang_count) >= 2)
ESP_LOGCONFIG("core_hw_buttons", " Relay 2: %s", sl_button_2_action->has_state()
? sl_button_2_action->state.c_str()
: "Unknown");
if (id(gang_count) >= 3)
ESP_LOGCONFIG("core_hw_buttons", " Relay 3: %s", sl_button_3_action->has_state()
? sl_button_3_action->state.c_str()
: "Unknown");
if (id(gang_count) >= 4)
ESP_LOGCONFIG("core_hw_buttons", " Relay 4: %s", sl_button_4_action->has_state()
? sl_button_4_action->state.c_str()
: "Unknown");
- 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, " - Core - Hardware - Buttons");
- id: !extend touch_on_multi_touch_release - id: !extend touch_on_multi_touch_release
then: then:
- script.execute: buttons_release - script.execute: buttons_release
@@ -289,6 +324,7 @@ select:
options: options:
- "${BUTTON_ACTION_NONE_TEXT}" - "${BUTTON_ACTION_NONE_TEXT}"
- "${BUTTON_1_ACTION_TEXT}" - "${BUTTON_1_ACTION_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: true
@@ -303,6 +339,7 @@ select:
options: options:
- "${BUTTON_ACTION_NONE_TEXT}" - "${BUTTON_ACTION_NONE_TEXT}"
- "${BUTTON_2_ACTION_TEXT}" - "${BUTTON_2_ACTION_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
@@ -312,6 +349,7 @@ select:
options: options:
- "${BUTTON_ACTION_NONE_TEXT}" - "${BUTTON_ACTION_NONE_TEXT}"
- "${BUTTON_3_ACTION_TEXT}" - "${BUTTON_3_ACTION_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
@@ -321,6 +359,7 @@ select:
options: options:
- "${BUTTON_ACTION_NONE_TEXT}" - "${BUTTON_ACTION_NONE_TEXT}"
- "${BUTTON_4_ACTION_TEXT}" - "${BUTTON_4_ACTION_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
... ...

View File

@@ -28,7 +28,11 @@ substitutions:
LIGHT_FULL_RESTORE_MODE: RESTORE_DEFAULT_OFF LIGHT_FULL_RESTORE_MODE: RESTORE_DEFAULT_OFF
LIGHT_SIDES_RESTORE_MODE: RESTORE_DEFAULT_OFF LIGHT_SIDES_RESTORE_MODE: RESTORE_DEFAULT_OFF
LIGHT_INDIVIDUAL_RESTORE_MODE: RESTORE_DEFAULT_OFF LIGHT_INDIVIDUAL_RESTORE_MODE: RESTORE_DEFAULT_OFF
LIGHT_RELAYS_RESTORE_MODE: RESTORE_DEFAULT_OFF
esphome:
platformio_options:
build_flags:
- -D TX_ULTIMATE_EASY_CORE_HW_LEDS
globals: globals:
- id: gb_lights_1 - id: gb_lights_1
@@ -43,13 +47,12 @@ light:
- id: light_full - id: light_full
name: Light - All name: Light - All
internal: true internal: true
platform: neopixelbus
type: GRB
variant: WS2811
# method: esp32_rmt
num_leds: 32 num_leds: 32
pin: GPIO13 pin: GPIO13
restore_mode: ${LIGHT_FULL_RESTORE_MODE} restore_mode: ${LIGHT_FULL_RESTORE_MODE}
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
@@ -418,370 +421,77 @@ light:
from: 31 from: 31
to: 31 to: 31
# These lights are used to indicate relay's statuses
- &light_partition_relays
id: light_eu_rl_1_1_bottom
name: Light - Relay 1 of 1 (bottom)
platform: partition
internal: true
default_transition_length: ${default_transition_length}
restore_mode: ${LIGHT_RELAYS_RESTORE_MODE}
segments:
- id: light_full
from: 9
to: 9
- id: light_eu_rl_1_1_top
name: Light - Relay 1 of 1 (top)
<<: *light_partition_relays
segments:
- id: light_full
from: 23
to: 23
- id: light_eu_rl_1_2_bottom
name: Light - Relay 1 of 2 (bottom)
<<: *light_partition_relays
segments:
- id: light_full
from: 11
to: 11
- id: light_eu_rl_1_2_top
name: Light - Relay 1 of 2 (top)
<<: *light_partition_relays
segments:
- id: light_full
from: 21
to: 21
- id: light_eu_rl_2_2_bottom
name: Light - Relay 2 of 2 (bottom)
<<: *light_partition_relays
segments:
- id: light_full
from: 7
to: 7
- id: light_eu_rl_2_2_top
name: Light - Relay 2 of 2 (top)
<<: *light_partition_relays
segments:
- id: light_full
from: 25
to: 25
- id: light_eu_rl_1_3_bottom
name: Light - Relay 1 of 3 (bottom)
<<: *light_partition_relays
segments:
- id: light_full
from: 12
to: 12
- id: light_eu_rl_1_3_top
name: Light - Relay 1 of 3 (top)
<<: *light_partition_relays
segments:
- id: light_full
from: 20
to: 20
- id: light_eu_rl_2_3_bottom
name: Light - Relay 2 of 3 (bottom)
<<: *light_partition_relays
segments:
- id: light_full
from: 9
to: 9
- id: light_eu_rl_2_3_top
name: Light - Relay 2 of 3 (top)
<<: *light_partition_relays
segments:
- id: light_full
from: 23
to: 23
- id: light_eu_rl_3_3_bottom
name: Light - Relay 3 of 3 (bottom)
<<: *light_partition_relays
segments:
- id: light_full
from: 6
to: 6
- id: light_eu_rl_3_3_top
name: Light - Relay 3 of 3 (top)
<<: *light_partition_relays
segments:
- id: light_full
from: 26
to: 26
- id: light_eu_rl_1_4_bottom
name: Light - Relay 1 of 4 (bottom)
<<: *light_partition_relays
segments:
- id: light_full
from: 12
to: 12
- id: light_eu_rl_1_4_top
name: Light - Relay 1 of 4 (top)
<<: *light_partition_relays
segments:
- id: light_full
from: 20
to: 20
- id: light_eu_rl_2_4_bottom
name: Light - Relay 2 of 4 (bottom)
<<: *light_partition_relays
segments:
- id: light_full
from: 10
to: 10
- id: light_eu_rl_2_4_top
name: Light - Relay 2 of 4 (top)
<<: *light_partition_relays
segments:
- id: light_full
from: 22
to: 22
- id: light_eu_rl_3_4_bottom
name: Light - Relay 3 of 4 (bottom)
<<: *light_partition_relays
segments:
- id: light_full
from: 8
to: 8
- id: light_eu_rl_3_4_top
name: Light - Relay 3 of 4 (top)
<<: *light_partition_relays
segments:
- id: light_full
from: 24
to: 24
- id: light_eu_rl_4_4_bottom
name: Light - Relay 4 of 4 (bottom)
<<: *light_partition_relays
segments:
- id: light_full
from: 6
to: 6
- id: light_eu_rl_4_4_top
name: Light - Relay 4 of 4 (top)
<<: *light_partition_relays
segments:
- id: light_full
from: 26
to: 26
- id: light_us_rl_1_1_left
name: Light - Relay 1 of 1 (left)
<<: *light_partition_relays
segments:
- id: light_full
from: 15
to: 24
- id: light_us_rl_1_1_right
name: Light - Relay 1 of 1 (right)
<<: *light_partition_relays
segments:
- id: light_full
from: 31
to: 31
- id: light_full
from: 0
to: 8
- id: light_us_rl_1_2_left
name: Light - Relay 1 of 2 (left)
<<: *light_partition_relays
segments:
- id: light_full
from: 20
to: 24
- id: light_us_rl_1_2_right
name: Light - Relay 1 of 2 (right)
<<: *light_partition_relays
segments:
- id: light_full
from: 31
to: 31
- id: light_full
from: 0
to: 3
- id: light_us_rl_2_2_left
name: Light - Relay 2 of 2 (left)
<<: *light_partition_relays
segments:
- id: light_full
from: 15
to: 19
- id: light_us_rl_2_2_right
name: Light - Relay 2 of 2 (right)
<<: *light_partition_relays
segments:
- id: light_full
from: 4
to: 8
- id: light_us_rl_1_3_left
name: Light - Relay 1 of 3 (left)
<<: *light_partition_relays
segments:
- id: light_full
from: 22
to: 23
- id: light_us_rl_1_3_right
name: Light - Relay 1 of 3 (right)
<<: *light_partition_relays
segments:
- id: light_full
from: 0
to: 1
- id: light_us_rl_2_3_left
name: Light - Relay 2 of 3 (left)
<<: *light_partition_relays
segments:
- id: light_full
from: 19
to: 20
- id: light_us_rl_2_3_right
name: Light - Relay 2 of 3 (right)
<<: *light_partition_relays
segments:
- id: light_full
from: 3
to: 4
- id: light_us_rl_3_3_left
name: Light - Relay 3 of 3 (left)
<<: *light_partition_relays
segments:
- id: light_full
from: 17
to: 18
- id: light_us_rl_3_3_right
name: Light - Relay 3 of 3 (right)
<<: *light_partition_relays
segments:
- id: light_full
from: 6
to: 7
- id: light_us_rl_1_4_left
name: Light - Relay 1 of 4 (left)
<<: *light_partition_relays
segments:
- id: light_full
from: 12
to: 12
- id: light_us_rl_1_4_right
name: Light - Relay 1 of 4 (right)
<<: *light_partition_relays
segments:
- id: light_full
from: 31
to: 31
- id: light_us_rl_2_4_left
name: Light - Relay 2 of 4 (left)
<<: *light_partition_relays
segments:
- id: light_full
from: 24
to: 24
- id: light_us_rl_2_4_right
name: Light - Relay 2 of 4 (right)
<<: *light_partition_relays
segments:
- id: light_full
from: 2
to: 2
- id: light_us_rl_3_4_left
name: Light - Relay 3 of 4 (left)
<<: *light_partition_relays
segments:
- id: light_full
from: 21
to: 21
- id: light_us_rl_3_4_right
name: Light - Relay 3 of 4 (right)
<<: *light_partition_relays
segments:
- id: light_full
from: 4
to: 4
- id: light_us_rl_4_4_left
name: Light - Relay 4 of 4 (left)
<<: *light_partition_relays
segments:
- id: light_full
from: 19
to: 19
- id: light_us_rl_4_4_right
name: Light - Relay 4 of 4 (right)
<<: *light_partition_relays
segments:
- id: light_full
from: 7
to: 7
script: script:
- id: !extend boot_initialize - id: !extend boot_initialize
then: then:
- wait_until:
condition:
- lambda: return sl_tx_model_format->active_index().has_value();
- lambda: return sl_tx_model_gang->active_index().has_value();
- lambda: |- - lambda: |-
const bool is_model_us = sl_tx_model_format->active_index().has_value() and light_28->set_internal(!id(is_us_model));
sl_tx_model_format->active_index().value() == 1; light_29->set_internal(!id(is_us_model));
const uint8_t num_gangs = (sl_tx_model_gang->active_index().has_value()) ? light_30->set_internal(!id(is_us_model));
(sl_tx_model_gang->active_index().value() + 1) : 0; light_31->set_internal(!id(is_us_model));
if (num_gangs < 1 || num_gangs > 4) { light_eu->set_internal(id(is_us_model));
ESP_LOGE("core_hw_leds", "Invalid number of gangs: %" PRIu8, num_gangs); light_eu_bottom->set_internal(id(is_us_model));
return; light_eu_left->set_internal(id(is_us_model));
} light_eu_right->set_internal(id(is_us_model));
light_28->set_internal(!is_model_us); light_eu_top->set_internal(id(is_us_model));
light_29->set_internal(!is_model_us); light_us->set_internal(!id(is_us_model));
light_30->set_internal(!is_model_us); light_us_bottom->set_internal(!id(is_us_model));
light_31->set_internal(!is_model_us); light_us_left->set_internal(!id(is_us_model));
light_eu->set_internal(is_model_us); light_us_right->set_internal(!id(is_us_model));
light_eu_bottom->set_internal(is_model_us); light_us_top->set_internal(!id(is_us_model));
light_eu_left->set_internal(is_model_us);
light_eu_right->set_internal(is_model_us); - id: !extend dump_config
light_eu_top->set_internal(is_model_us); then:
light_us->set_internal(!is_model_us); - lambda: |-
light_us_bottom->set_internal(!is_model_us); // Relay's LEDs modes
light_us_left->set_internal(!is_model_us); ESP_LOGCONFIG("core_hw_leds", "Relay%s LEDs mode%s:",
light_us_right->set_internal(!is_model_us); id(gang_count_plural_suffix).c_str(),
light_us_top->set_internal(!is_model_us); id(gang_count_plural_suffix).c_str());
switch (num_gangs) { ESP_LOGCONFIG("core_hw_leds", " Relay 1: %s", id(is_us_model)
case 1: // 1 Gang ? sl_relay_1_light_mode_us->state.c_str()
if (is_model_us) { : sl_relay_1_light_mode_eu->state.c_str());
id(gb_lights_1) = { light_us_rl_1_1_left }; if (id(gang_count) >= 2)
id(gb_lights_2) = { light_us_rl_1_1_right }; ESP_LOGCONFIG("core_hw_leds", " Relay 2: %s", id(is_us_model)
} else { ? sl_relay_2_light_mode_us->state.c_str()
id(gb_lights_1) = { light_eu_rl_1_1_bottom }; : sl_relay_2_light_mode_eu->state.c_str());
id(gb_lights_2) = { light_eu_rl_1_1_top }; if (id(gang_count) >= 3)
} ESP_LOGCONFIG("core_hw_leds", " Relay 3: %s", id(is_us_model)
break; ? sl_relay_3_light_mode_us->state.c_str()
case 2: // 2 Gang : sl_relay_3_light_mode_eu->state.c_str());
if (is_model_us) { if (id(gang_count) >= 4)
id(gb_lights_1) = { light_us_rl_1_2_left, light_us_rl_2_2_left }; ESP_LOGCONFIG("core_hw_leds", " Relay 4: %s", id(is_us_model)
id(gb_lights_2) = { light_us_rl_1_2_right, light_us_rl_2_2_right }; ? sl_relay_4_light_mode_us->state.c_str()
} else { : sl_relay_4_light_mode_eu->state.c_str());
id(gb_lights_1) = { light_eu_rl_1_2_bottom, light_eu_rl_2_2_bottom };
id(gb_lights_2) = { light_eu_rl_1_2_top, light_eu_rl_2_2_top }; // Transition times
} ESP_LOGCONFIG("core_hw_leds", "Transition times:");
break; ESP_LOGCONFIG("core_hw_leds", " Default: ${default_transition_length}");
case 3: // 3 Gang if (${LIGHT_TRANSITION_TURN_ON} > 0)
if (is_model_us) { ESP_LOGCONFIG("core_hw_leds", " Turning on: ${LIGHT_TRANSITION_TURN_ON}ms");
id(gb_lights_1) = { light_us_rl_1_3_left, light_us_rl_2_3_left, light_us_rl_3_3_left }; else
id(gb_lights_2) = { light_us_rl_1_3_right, light_us_rl_2_3_right, light_us_rl_3_3_right }; ESP_LOGCONFIG("core_hw_leds", " Turning on: Disabled");
} else { if (${LIGHT_TRANSITION_TURN_OFF} > 0)
id(gb_lights_1) = { light_eu_rl_1_3_bottom, light_eu_rl_2_3_bottom, light_eu_rl_3_3_bottom }; ESP_LOGCONFIG("core_hw_leds", " Turning off: ${LIGHT_TRANSITION_TURN_OFF}ms");
id(gb_lights_2) = { light_eu_rl_1_3_top, light_eu_rl_2_3_top, light_eu_rl_3_3_top }; else
} ESP_LOGCONFIG("core_hw_leds", " Turning off: Disabled");
break;
case 4: // 4 Gang // Restore modes
if (is_model_us) { ESP_LOGCONFIG("core_hw_leds", "LED restore modes:");
id(gb_lights_1) = { light_us_rl_1_4_left, light_us_rl_2_4_left, ESP_LOGCONFIG("core_hw_leds", " Light - All: ${LIGHT_FULL_RESTORE_MODE}");
light_us_rl_3_4_left, light_us_rl_4_4_left }; ESP_LOGCONFIG("core_hw_leds", " Sides: ${LIGHT_SIDES_RESTORE_MODE}");
id(gb_lights_2) = { light_us_rl_1_4_right, light_us_rl_2_4_right, ESP_LOGCONFIG("core_hw_leds", " Individual: ${LIGHT_INDIVIDUAL_RESTORE_MODE}");
light_us_rl_3_4_right, light_us_rl_4_4_right };
} else { - id: !extend dump_config_list_packages
id(gb_lights_1) = { light_eu_rl_1_4_bottom, light_eu_rl_2_4_bottom, then:
light_eu_rl_3_4_bottom, light_eu_rl_4_4_bottom }; - script.wait: dump_config
id(gb_lights_2) = { light_eu_rl_1_4_top, light_eu_rl_2_4_top, - lambda: |-
light_eu_rl_3_4_top, light_eu_rl_4_4_top }; // Check for requirements
} #if !defined(TX_ULTIMATE_EASY_CORE)
break; #error "The package TX-Ultimate-Easy-ESPHome_core.yaml is required."
} #endif
// Identify itself
ESP_LOGCONFIG(ESPHOME_PROJECT_NAME, " - Core - Hardware - LEDs");
- id: light_set_state - id: light_set_state
mode: parallel mode: parallel
@@ -795,10 +505,12 @@ script:
static const uint32_t LIGHT_TRANSITION_TURN_OFF = ${LIGHT_TRANSITION_TURN_OFF}; static const uint32_t LIGHT_TRANSITION_TURN_OFF = ${LIGHT_TRANSITION_TURN_OFF};
static const uint8_t LIGHT_BRIGHTNESS_TURN_ON = ${LIGHT_BRIGHTNESS_TURN_ON}; static const uint8_t LIGHT_BRIGHTNESS_TURN_ON = ${LIGHT_BRIGHTNESS_TURN_ON};
const bool is_model_us = sl_tx_model_format->active_index().has_value() and const std::string light_mode_group_1 = id(is_us_model)
sl_tx_model_format->active_index().value() == 1; ? "${LIGHT_MODE_LEFT_TEXT}"
const std::string light_mode_group_1 = is_model_us ? "${LIGHT_MODE_LEFT_TEXT}" : "${LIGHT_MODE_BOTTOM_TEXT}"; : "${LIGHT_MODE_BOTTOM_TEXT}";
const std::string light_mode_group_2 = is_model_us ? "${LIGHT_MODE_RIGHT_TEXT}" : "${LIGHT_MODE_TOP_TEXT}"; const std::string light_mode_group_2 = id(is_us_model)
? "${LIGHT_MODE_RIGHT_TEXT}"
: "${LIGHT_MODE_TOP_TEXT}";
switch (light_group) { switch (light_group) {
case 1: case 1:
@@ -869,15 +581,12 @@ select:
entity_category: config entity_category: config
disabled_by_default: false disabled_by_default: false
icon: mdi:dots-square icon: mdi:dots-square
- id: sl_relay_2_light_mode_eu - id: sl_relay_2_light_mode_eu
name: Relay 2 light mode name: Relay 2 light mode
<<: *relay_light_mode_eu <<: *relay_light_mode_eu
- id: sl_relay_3_light_mode_eu - id: sl_relay_3_light_mode_eu
name: Relay 3 light mode name: Relay 3 light mode
<<: *relay_light_mode_eu <<: *relay_light_mode_eu
- id: sl_relay_4_light_mode_eu - id: sl_relay_4_light_mode_eu
name: Relay 4 light mode name: Relay 4 light mode
<<: *relay_light_mode_eu <<: *relay_light_mode_eu
@@ -899,15 +608,12 @@ select:
entity_category: config entity_category: config
disabled_by_default: false disabled_by_default: false
icon: mdi:dots-square icon: mdi:dots-square
- id: sl_relay_2_light_mode_us - id: sl_relay_2_light_mode_us
name: Relay 2 light mode name: Relay 2 light mode
<<: *relay_light_mode_us <<: *relay_light_mode_us
- id: sl_relay_3_light_mode_us - id: sl_relay_3_light_mode_us
name: Relay 3 light mode name: Relay 3 light mode
<<: *relay_light_mode_us <<: *relay_light_mode_us
- id: sl_relay_4_light_mode_us - id: sl_relay_4_light_mode_us
name: Relay 4 light mode name: Relay 4 light mode
<<: *relay_light_mode_us <<: *relay_light_mode_us

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 - Speaker ##### ##### Purpose: ESPHome Core - Hardware - Media player #####
#################################################################################################### ####################################################################################################
##### Author: edwardtfn - https://github.com/edwardtfn - https://buymeacoffee.com/edwardfirmo ##### ##### Author: edwardtfn - https://github.com/edwardtfn - https://buymeacoffee.com/edwardfirmo #####
#################################################################################################### ####################################################################################################
@@ -17,13 +17,13 @@ i2s_audio:
i2s_lrclk_pin: GPIO4 i2s_lrclk_pin: GPIO4
media_player: media_player:
- id: mp_speaker - id: mp_media_player
name: Speaker name: Media Player
internal: false
platform: i2s_audio platform: i2s_audio
i2s_dout_pin: GPIO15 i2s_dout_pin: GPIO15
i2s_audio_id: if_i2s_audio i2s_audio_id: if_i2s_audio
i2s_comm_fmt: lsb i2s_comm_fmt: msb # Experimental - It was 'lsb' until v2024.12.12
dac_type: external dac_type: external
mode: mono mode: mono
internal: false
... ...

View File

@@ -1,441 +0,0 @@
####################################################################################################
##### TX Ultimate Easy for ESPHome #####
##### Repository: https://github.com/edwardtfn/TX-Ultimate-Easy #####
####################################################################################################
##### Purpose: ESPHome Core - Hardware - Relays #####
####################################################################################################
##### 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:
RELAY_MODE_TEXT_SWITCH: "Switch"
RELAY_MODE_TEXT_LIGHT: "Light"
RELAY_MODE_TEXT_NOT_USED: "Not in use"
RELAY_RESTORE_MODE: RESTORE_DEFAULT_OFF
globals:
- id: boot_initialization_relays
type: bool
restore_value: false
initial_value: 'false'
light:
# These lights are used for the physical relays to be shown as lights
- id: light_output_1
name: Light output 1
output: output_relay_1
platform: binary
internal: true
restore_mode: ${RELAY_RESTORE_MODE}
on_turn_on:
then:
- if:
condition:
- switch.is_off: sw_relay_1
then:
- switch.turn_on: sw_relay_1
on_turn_off:
then:
- if:
condition:
- switch.is_on: sw_relay_1
then:
- switch.turn_off: sw_relay_1
- id: light_output_2
name: Light output 2
output: output_relay_2
platform: binary
internal: true
on_turn_on:
then:
- if:
condition:
- switch.is_off: sw_relay_2
then:
- switch.turn_on: sw_relay_2
on_turn_off:
then:
- if:
condition:
- switch.is_on: sw_relay_2
then:
- switch.turn_off: sw_relay_2
- id: light_output_3
name: Light output 3
output: output_relay_3
platform: binary
internal: true
on_turn_on:
then:
- if:
condition:
- switch.is_off: sw_relay_3
then:
- switch.turn_on: sw_relay_3
on_turn_off:
then:
- if:
condition:
- switch.is_on: sw_relay_3
then:
- switch.turn_off: sw_relay_3
- id: light_output_4
name: Light output 4
output: output_relay_4
platform: binary
internal: true
on_turn_on:
then:
- if:
condition:
- switch.is_off: sw_relay_4
then:
- switch.turn_on: sw_relay_4
on_turn_off:
then:
- if:
condition:
- switch.is_on: sw_relay_4
then:
- switch.turn_off: sw_relay_4
output:
- id: output_relay_1
platform: gpio
pin: GPIO18
- id: output_relay_2
platform: gpio
pin: GPIO17
- id: output_relay_3
platform: gpio
pin: GPIO27
- id: output_relay_4
platform: gpio
pin: GPIO23
script:
- id: !extend boot_initialize
then:
- script.execute: boot_initialize_relays
- id: boot_initialize_relays
mode: restart
then:
- wait_until:
condition:
- lambda: return sl_tx_model_format->active_index().has_value();
- lambda: return sl_tx_model_gang->active_index().has_value();
- lambda: |-
const bool is_model_us = sl_tx_model_format->active_index().has_value() and
sl_tx_model_format->active_index().value() == 1;
const uint8_t num_gangs = (sl_tx_model_gang->active_index().has_value()) ?
(sl_tx_model_gang->active_index().value() + 1) : 0;
if (num_gangs < 1 || num_gangs > 4) {
ESP_LOGE("core_hw_relays", "Invalid number of gangs: %" PRIu8, num_gangs);
return;
}
sl_relay_1_mode->set_internal(num_gangs < 1);
sl_relay_1_light_mode_eu->set_internal(is_model_us or num_gangs < 1);
sl_relay_1_light_mode_us->set_internal(!is_model_us or num_gangs < 1);
sl_relay_2_mode->set_internal(num_gangs < 2);
sl_relay_2_light_mode_eu->set_internal(is_model_us or num_gangs < 2);
sl_relay_2_light_mode_us->set_internal(!is_model_us or num_gangs < 2);
sl_relay_3_mode->set_internal(num_gangs < 3);
sl_relay_3_light_mode_eu->set_internal(is_model_us or num_gangs < 3);
sl_relay_3_light_mode_us->set_internal(!is_model_us or num_gangs < 3);
sl_relay_4_mode->set_internal(num_gangs < 4);
sl_relay_4_light_mode_eu->set_internal(is_model_us or num_gangs < 4);
sl_relay_4_light_mode_us->set_internal(!is_model_us or num_gangs < 4);
auto relay_mode_index = sl_relay_1_mode->active_index();
if (relay_mode_index.has_value()) {
light_output_1->set_internal(false or relay_mode_index.value() != 1);
sw_relay_1->set_internal(false or relay_mode_index.value() != 0);
}
relay_mode_index = sl_relay_2_mode->active_index();
if (relay_mode_index.has_value()) {
light_output_2->set_internal(num_gangs < 2 or relay_mode_index.value() != 1);
sw_relay_2->set_internal(num_gangs < 2 or relay_mode_index.value() != 0);
}
relay_mode_index = sl_relay_3_mode->active_index();
if (relay_mode_index.has_value()) {
light_output_3->set_internal(num_gangs < 3 or relay_mode_index.value() != 1);
sw_relay_3->set_internal(num_gangs < 3 or relay_mode_index.value() != 0);
}
relay_mode_index = sl_relay_4_mode->active_index();
if (relay_mode_index.has_value()) {
light_output_4->set_internal(num_gangs < 4 or relay_mode_index.value() != 1);
sw_relay_4->set_internal(num_gangs < 4 or relay_mode_index.value() != 0);
}
id(boot_initialization_relays) = true;
- id: !extend button_click_event
then:
- lambda: |-
// Handle only single clicks
if (click_count != 1)
return;
// Ignore if other touch events are active
if (bs_multi_touch->state ||
bs_swipe_left->state ||
bs_swipe_down->state ||
bs_swipe_right->state) {
return;
}
// Toggle relay if corresponding button action is enabled
ESP_LOGVV("core_hw_relays", "Toggle relays: (${BUTTON_ACTION_NONE_TEXT})");
ESP_LOGVV("core_hw_relays", " Relay 1: %s (%s)",
YESNO(sl_button_1_action->state != "${BUTTON_ACTION_NONE_TEXT}"),
sl_button_1_action->state.c_str());
ESP_LOGVV("core_hw_relays", " Relay 2: %s (%s)",
YESNO(sl_button_2_action->state != "${BUTTON_ACTION_NONE_TEXT}"),
sl_button_2_action->state.c_str());
ESP_LOGVV("core_hw_relays", " Relay 3: %s (%s)",
YESNO(sl_button_3_action->state != "${BUTTON_ACTION_NONE_TEXT}"),
sl_button_3_action->state.c_str());
ESP_LOGVV("core_hw_relays", " Relay 4: %s (%s)",
YESNO(sl_button_4_action->state != "${BUTTON_ACTION_NONE_TEXT}"),
sl_button_4_action->state.c_str());
switch (button) {
case ${BUTTON_1_ID}:
if (sl_button_1_action->state != "${BUTTON_ACTION_NONE_TEXT}") {
ESP_LOGV("core_hw_relays", "Toggle relay 1: (%s)", sl_button_1_action->state.c_str());
sw_relay_1->toggle();
} else {
ESP_LOGV("core_hw_relays", "Ignoring toggle for relay 1: action is '%s'",
sl_button_1_action->state.c_str());
}
break;
case ${BUTTON_2_ID}:
if (sl_button_2_action->state != "${BUTTON_ACTION_NONE_TEXT}") {
ESP_LOGV("core_hw_relays", "Toggle relay 2: (%s)", sl_button_2_action->state.c_str());
sw_relay_2->toggle();
} else {
ESP_LOGV("core_hw_relays", "Ignoring toggle for relay 2: action is '%s'",
sl_button_2_action->state.c_str());
}
break;
case ${BUTTON_3_ID}:
if (sl_button_3_action->state != "${BUTTON_ACTION_NONE_TEXT}") {
ESP_LOGV("core_hw_relays", "Toggle relay 3: (%s)", sl_button_3_action->state.c_str());
sw_relay_3->toggle();
} else {
ESP_LOGV("core_hw_relays", "Ignoring toggle for relay 3: action is '%s'",
sl_button_3_action->state.c_str());
}
break;
case ${BUTTON_4_ID}:
if (sl_button_4_action->state != "${BUTTON_ACTION_NONE_TEXT}") {
ESP_LOGV("core_hw_relays", "Toggle relay 4: (%s)", sl_button_4_action->state.c_str());
sw_relay_4->toggle();
} else {
ESP_LOGV("core_hw_relays", "Ignoring toggle for relay 4: action is '%s'",
sl_button_4_action->state.c_str());
}
break;
}
- id: show_relay_status
mode: restart
then:
- script.wait: boot_initialize_relays
- wait_until:
condition:
- lambda: return id(boot_initialization_relays);
- lambda: |-
const bool is_model_us = sl_tx_model_format->active_index().has_value() and
sl_tx_model_format->active_index().value() == 1;
const uint8_t num_gangs = (sl_tx_model_gang->active_index().has_value())
? (sl_tx_model_gang->active_index().value() + 1)
: 0;
if (num_gangs < 1 || num_gangs > 4) {
ESP_LOGE("core_hw_relays", "Invalid number of gangs: %" PRIu8, num_gangs);
return;
}
for (uint8_t i = 0; i < num_gangs; i++) {
bool relay_state = false;
std::string light_mode;
const std::string light_mode_group_1 = is_model_us
? "${LIGHT_MODE_LEFT_TEXT}"
: "${LIGHT_MODE_BOTTOM_TEXT}";
const std::string light_mode_group_2 = is_model_us
? "${LIGHT_MODE_RIGHT_TEXT}"
: "${LIGHT_MODE_TOP_TEXT}";
// Determine relay state and light mode
switch (i) {
case 0:
relay_state = id(sw_relay_1).state;
light_mode = is_model_us
? id(sl_relay_1_light_mode_us)->state
: id(sl_relay_1_light_mode_eu)->state;
break;
case 1:
relay_state = id(sw_relay_2).state;
light_mode = is_model_us
? id(sl_relay_2_light_mode_us)->state
: id(sl_relay_2_light_mode_eu)->state;
break;
case 2:
relay_state = id(sw_relay_3).state;
light_mode = is_model_us
? id(sl_relay_3_light_mode_us)->state
: id(sl_relay_3_light_mode_eu)->state;
break;
case 3:
relay_state = id(sw_relay_4).state;
light_mode = is_model_us
? id(sl_relay_4_light_mode_us)->state
: id(sl_relay_4_light_mode_eu)->state;
break;
}
ESP_LOGV("core_hw_relays", "Relay %" PRIu8 " is %s", i + 1, relay_state ? "ON" : "OFF");
ESP_LOGV("core_hw_relays", "Light Mode for Relay %" PRIu8 ": %s", i + 1, light_mode.c_str());
// Use light_set_state for light updates
if (relay_state) {
if (light_mode == light_mode_group_1 || light_mode == "${LIGHT_MODE_BOTH_TEXT}") {
light_set_state->execute(1, i, true); // Left/bottom light
}
if (light_mode == light_mode_group_2 || light_mode == "${LIGHT_MODE_BOTH_TEXT}") {
light_set_state->execute(2, i, true); // Right/top light
}
} else {
light_set_state->execute(1, i, false); // Turn off left/bottom light
light_set_state->execute(2, i, false); // Turn off right/top light
}
}
select:
- &relay_select_mode_base
id: sl_relay_1_mode
name: Relay 1 display mode
platform: template
options:
- "${RELAY_MODE_TEXT_SWITCH}"
- "${RELAY_MODE_TEXT_LIGHT}"
- "${RELAY_MODE_TEXT_NOT_USED}"
initial_option: "${RELAY_MODE_TEXT_SWITCH}"
optimistic: true
restore_value: true
internal: true
entity_category: config
disabled_by_default: false
icon: mdi:dip-switch
- id: sl_relay_2_mode
name: Relay 2 display mode
<<: *relay_select_mode_base
- id: sl_relay_3_mode
name: Relay 3 display mode
<<: *relay_select_mode_base
- id: sl_relay_4_mode
name: Relay 4 display mode
<<: *relay_select_mode_base
switch:
- id: sw_relay_1
name: Relay 1
output: output_relay_1
platform: output
restore_mode: ${RELAY_RESTORE_MODE}
internal: true
on_turn_on:
then:
- script.execute: show_relay_status
- if:
condition:
- light.is_off: light_output_1
then:
light.turn_on: light_output_1
on_turn_off:
then:
- script.execute: show_relay_status
- if:
condition:
- light.is_on: light_output_1
then:
- light.turn_off: light_output_1
- id: sw_relay_2
name: Relay 2
output: output_relay_2
platform: output
restore_mode: ${RELAY_RESTORE_MODE}
internal: true
on_turn_on:
then:
- script.execute: show_relay_status
- if:
condition:
- light.is_off: light_output_2
then:
light.turn_on: light_output_2
on_turn_off:
then:
- script.execute: show_relay_status
- if:
condition:
- light.is_on: light_output_2
then:
- light.turn_off: light_output_2
- id: sw_relay_3
name: Relay 3
output: output_relay_3
platform: output
restore_mode: ${RELAY_RESTORE_MODE}
internal: true
on_turn_on:
then:
- script.execute: show_relay_status
- if:
condition:
- light.is_off: light_output_3
then:
- light.turn_on: light_output_3
on_turn_off:
then:
- script.execute: show_relay_status
- if:
condition:
- light.is_on: light_output_3
then:
- light.turn_off: light_output_3
- id: sw_relay_4
name: Relay 4
output: output_relay_4
platform: output
restore_mode: ${RELAY_RESTORE_MODE}
internal: true
on_turn_on:
then:
- script.execute: show_relay_status
- if:
condition:
- light.is_off: light_output_4
then:
- light.turn_on: light_output_4
on_turn_off:
then:
- script.execute: show_relay_status
- if:
condition:
- light.is_on: light_output_4
then:
- light.turn_off: light_output_4
...

View File

@@ -198,6 +198,11 @@ binary_sensor:
id: bs_swipe_down id: bs_swipe_down
state: OFF # yamllint disable-line rule:truthy state: OFF # yamllint disable-line rule:truthy
esphome:
platformio_options:
build_flags:
- -D TX_ULTIMATE_EASY_CORE_HW_TOUCH
external_components: external_components:
- source: - source:
type: git type: git
@@ -256,13 +261,8 @@ script:
- id: boot_initialize_touch_gang - id: boot_initialize_touch_gang
mode: restart mode: restart
then: then:
- wait_until:
condition:
- lambda: return sl_tx_model_gang->active_index().has_value();
- lambda: |- - lambda: |-
const uint8_t num_gangs = (sl_tx_model_gang->active_index().has_value()) ? tx_ultimate->set_gang_count(id(gang_count));
(sl_tx_model_gang->active_index().value() + 1) : 0;
tx_ultimate->set_gang_count(num_gangs);
bs_button_1_click_event->publish_state(false); bs_button_1_click_event->publish_state(false);
bs_button_2_click_event->publish_state(false); bs_button_2_click_event->publish_state(false);
bs_button_3_click_event->publish_state(false); bs_button_3_click_event->publish_state(false);
@@ -275,18 +275,40 @@ script:
bs_button_2_long_press_event->publish_state(false); bs_button_2_long_press_event->publish_state(false);
bs_button_3_long_press_event->publish_state(false); bs_button_3_long_press_event->publish_state(false);
bs_button_4_long_press_event->publish_state(false); bs_button_4_long_press_event->publish_state(false);
bs_button_1_click_event->set_internal(num_gangs < 1); bs_button_1_click_event->set_internal(id(gang_count) < 1);
bs_button_2_click_event->set_internal(num_gangs < 2); bs_button_2_click_event->set_internal(id(gang_count) < 2);
bs_button_3_click_event->set_internal(num_gangs < 3); bs_button_3_click_event->set_internal(id(gang_count) < 3);
bs_button_4_click_event->set_internal(num_gangs < 4); bs_button_4_click_event->set_internal(id(gang_count) < 4);
bs_button_1_double_click_event->set_internal(num_gangs < 1); bs_button_1_double_click_event->set_internal(id(gang_count) < 1);
bs_button_2_double_click_event->set_internal(num_gangs < 2); bs_button_2_double_click_event->set_internal(id(gang_count) < 2);
bs_button_3_double_click_event->set_internal(num_gangs < 3); bs_button_3_double_click_event->set_internal(id(gang_count) < 3);
bs_button_4_double_click_event->set_internal(num_gangs < 4); bs_button_4_double_click_event->set_internal(id(gang_count) < 4);
bs_button_1_long_press_event->set_internal(num_gangs < 1); bs_button_1_long_press_event->set_internal(id(gang_count) < 1);
bs_button_2_long_press_event->set_internal(num_gangs < 2); bs_button_2_long_press_event->set_internal(id(gang_count) < 2);
bs_button_3_long_press_event->set_internal(num_gangs < 3); bs_button_3_long_press_event->set_internal(id(gang_count) < 3);
bs_button_4_long_press_event->set_internal(num_gangs < 4); bs_button_4_long_press_event->set_internal(id(gang_count) < 4);
- id: !extend dump_config
then:
- lambda: |-
// Touch panel's state
if (sw_touch_panel_power->state)
ESP_LOGCONFIG("core_hw_touch", "Touch panel - Power: On");
else
ESP_LOGW("core_hw_touch", "Touch panel - Power: OFF");
ESP_LOGCONFIG("core_hw_touch", "Touch event duration: %.0fms", nr_touch_duration->state);
- 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, " - Core - Hardware - Touchpad");
- id: touch_on_multi_touch_release - id: touch_on_multi_touch_release
mode: restart mode: restart
@@ -296,7 +318,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
@@ -358,9 +380,9 @@ tx_ultimate_easy:
- 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("tx_ultimate_easy", "Invalid long-touch position: %" PRIu8, touch_position); ESP_LOGE("core_hw_touch", "Invalid long-touch position: %" PRIu8, touch_position);
} else { } else {
ESP_LOGI("tx_ultimate_easy", "Long-touch released at position %" PRIu8, touch_position); ESP_LOGI("core_hw_touch", "Long-touch released at position %" PRIu8, touch_position);
} }
on_multi_touch_release: on_multi_touch_release:
@@ -373,21 +395,21 @@ tx_ultimate_easy:
type: multi_touch type: multi_touch
action: release action: release
position: !lambda return touch.x; position: !lambda return touch.x;
- lambda: ESP_LOGI("tx_ultimate_easy", "Multi-touch released"); - lambda: ESP_LOGI("core_hw_touch", "Multi-touch released");
- script.execute: touch_on_multi_touch_release - script.execute: touch_on_multi_touch_release
on_press: on_press:
- lambda: |- - lambda: |-
const uint8_t position = static_cast<uint8_t>(touch.x); const uint8_t position = static_cast<uint8_t>(touch.x);
if (position > ${TOUCH_POSITION_MAX_VALUE}) { // Check for valid range if (position > ${TOUCH_POSITION_MAX_VALUE}) { // Check for valid range
ESP_LOGE("tx_ultimate_easy", "Invalid touch position: %" PRIu8, position); ESP_LOGE("core_hw_touch", "Invalid touch position: %" PRIu8, position);
} else { } else {
ESP_LOGI("tx_ultimate_easy", "Pressed at position %" PRIu8, position); ESP_LOGI("core_hw_touch", "Pressed at position %" PRIu8, position);
touch_on_press->execute(static_cast<uint8_t>(touch.button), position); touch_on_press->execute(static_cast<uint8_t>(touch.button), position);
} }
on_release: on_release:
- lambda: ESP_LOGI("tx_ultimate_easy", "Released"); - lambda: ESP_LOGI("core_hw_touch", "Released");
- script.execute: touch_on_release - script.execute: touch_on_release
on_swipe_left: on_swipe_left:
@@ -399,12 +421,12 @@ tx_ultimate_easy:
domain: touch domain: touch
type: swipe type: swipe
action: !lambda |- action: !lambda |-
return sl_tx_model_format->state == "${TX_MODEL_FORMAT_US_TEXT}" ? "left" : "down"; return sl_tx_model_format->state == "${TX_MODEL_FORMAT_EU_TEXT}" ? "left" : "down";
swipe-direction: !lambda |- swipe-direction: !lambda |-
return sl_tx_model_format->state == "${TX_MODEL_FORMAT_US_TEXT}" ? "left" : "down"; return sl_tx_model_format->state == "${TX_MODEL_FORMAT_EU_TEXT}" ? "left" : "down";
position: !lambda return touch.x; position: !lambda return touch.x;
- lambda: |- - lambda: |-
ESP_LOGI("tx_ultimate_easy", "Swipe %s", sl_tx_model_format->state == "${TX_MODEL_FORMAT_US_TEXT}" ESP_LOGI("core_hw_touch", "Swipe %s", sl_tx_model_format->state == "${TX_MODEL_FORMAT_EU_TEXT}"
? "left" ? "left"
: "down"); : "down");
- script.execute: touch_swipe_left - script.execute: touch_swipe_left
@@ -418,21 +440,21 @@ tx_ultimate_easy:
domain: touch domain: touch
type: swipe type: swipe
action: !lambda |- action: !lambda |-
return sl_tx_model_format->state == "${TX_MODEL_FORMAT_US_TEXT}" ? "right" : "up"; return sl_tx_model_format->state == "${TX_MODEL_FORMAT_EU_TEXT}" ? "right" : "up";
swipe-direction: !lambda |- swipe-direction: !lambda |-
return sl_tx_model_format->state == "${TX_MODEL_FORMAT_US_TEXT}" ? "right" : "up"; return sl_tx_model_format->state == "${TX_MODEL_FORMAT_EU_TEXT}" ? "right" : "up";
position: !lambda return touch.x; position: !lambda return touch.x;
- lambda: |- - lambda: |-
ESP_LOGI("tx_ultimate_easy", "Swipe %s", sl_tx_model_format->state == "${TX_MODEL_FORMAT_US_TEXT}" ESP_LOGI("core_hw_touch", "Swipe %s", sl_tx_model_format->state == "${TX_MODEL_FORMAT_EU_TEXT}"
? "right" ? "right"
: "up"); : "up");
- script.execute: touch_swipe_right - script.execute: touch_swipe_right
on_touch_event: on_touch_event:
- lambda: |- - lambda: |-
ESP_LOGD("tx_ultimate_easy", "Touch event:"); ESP_LOGD("core_hw_touch", "Touch event:");
ESP_LOGD("tx_ultimate_easy", " State: %i (%s)", touch.state, touch.state_str.c_str()); ESP_LOGD("core_hw_touch", " State: %i (%s)", touch.state, touch.state_str.c_str());
ESP_LOGD("tx_ultimate_easy", " Position: %i", touch.x); ESP_LOGD("core_hw_touch", " Position: %i", touch.x);
uart: uart:
- id: uart_touch - id: uart_touch

View File

@@ -1,137 +0,0 @@
####################################################################################################
##### TX Ultimate Easy for ESPHome #####
##### Repository: https://github.com/edwardtfn/TX-Ultimate-Easy #####
####################################################################################################
##### Purpose: ESPHome Core - Hardware - Vibration system #####
####################################################################################################
##### 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:
vibration_max_duration: 2s
binary_sensor:
- id: bs_vibrating
name: Vibrating
icon: mdi:vibrate
internal: false
platform: template
lambda: return sw_vibration_motor->state;
button:
- id: bt_vibrate
name: Vibrate
icon: mdi:vibrate
internal: false
platform: template
on_press:
then:
- script.execute: vibrate
number:
- id: nr_vibrating_duration
name: Vibrate duration
icon: mdi:vibrate
unit_of_measurement: ms
internal: false
entity_category: config
platform: template
min_value: 1
max_value: 500
step: 1
initial_value: 15
optimistic: true
restore_value: true
ota:
on_state_change:
then:
- switch.turn_off: sw_vibration_motor
script:
- id: !extend touch_on_multi_touch_release
then:
- script.execute: touch_on_multi_touch_release_vibration
- id: touch_on_multi_touch_release_vibration
mode: restart
then:
- lambda: |-
if (sl_touch_vibration_feedback->state != "Disabled")
vibrate->execute();
- id: !extend touch_on_press
then:
- script.execute: touch_on_press_vibration
- id: touch_on_press_vibration
mode: restart
then:
- lambda: |-
if (sl_touch_vibration_feedback->state == "On press" or sl_touch_vibration_feedback->state == "Always")
vibrate->execute();
- id: !extend touch_on_release
then:
- script.execute: touch_on_release_vibration
- id: touch_on_release_vibration
mode: restart
then:
- lambda: |-
if (sl_touch_vibration_feedback->state == "On release" or sl_touch_vibration_feedback->state == "Always")
vibrate->execute();
- id: vibrate
mode: restart
then:
- switch.turn_on: sw_vibration_motor
- delay: !lambda return nr_vibrating_duration->state;
- switch.turn_off: sw_vibration_motor
select:
- id: sl_touch_vibration_feedback
name: Touch - Vibration feedback
platform: template
options:
- "Disabled"
- "On press"
- "On release"
- "Always"
initial_option: "Disabled"
optimistic: true
restore_value: true
internal: false
entity_category: config
disabled_by_default: false
icon: mdi:vibrate
switch:
- id: sw_vibration_motor
name: Vibration motor
platform: gpio
pin: GPIO21
restore_mode: ALWAYS_OFF
internal: true
on_turn_on:
then:
- binary_sensor.template.publish:
id: bs_vibrating
state: ON # yamllint disable-line rule:truthy
- delay: ${vibration_max_duration}
- if:
condition:
switch.is_on: sw_vibration_motor
then:
- switch.turn_off: sw_vibration_motor
on_turn_off:
then:
- binary_sensor.template.publish:
id: bs_vibrating
state: OFF # yamllint disable-line rule:truthy
...

223
README.md
View File

@@ -8,10 +8,10 @@
[![Discord][discord-shield]](https://discord.gg/Db6WJWzWuf) [![Discord][discord-shield]](https://discord.gg/Db6WJWzWuf)
[![Buy me an ice-cream][buymeacoffee-shield]](https://www.buymeacoffee.com/edwardfirmo) [![Buy me an ice-cream][buymeacoffee-shield]](https://www.buymeacoffee.com/edwardfirmo)
<!-- markdownlint-disable MD013 --> <!-- markdownlint-disable MD013 MD033 -->
| &nbsp;![TX Ultimate Easy Logo](Assets/Logo.webp) | TX Ultimate Easy provides custom ESPHome firmware for Sonoff TX Ultimate devices. Our project focuses on user-friendly configuration through the Home Assistant UI, eliminating the need for manual YAML editing. Whether you're new to home automation or an experienced user, TX Ultimate Easy makes it simple to manage your device. | | &nbsp;<picture><source media="(prefers-color-scheme: dark)" srcset="Assets/Logo_dark.png"><source media="(prefers-color-scheme: light)" srcset="Assets/Logo_light.png"><img alt="TX Ultimate Easy Logo" src="Assets/Logo_light.png"></picture> | TX Ultimate Easy provides custom ESPHome firmware for Sonoff TX Ultimate devices. Our project focuses on user-friendly configuration through the Home Assistant UI, eliminating the need for manual YAML editing. Whether you're new to home automation or an experienced user, TX Ultimate Easy makes it simple to manage your device. |
| --- | :-- | | --- | :-- |
<!-- markdownlint-enable MD013 --> <!-- markdownlint-enable MD013 MD033 -->
[version-shield]: https://img.shields.io/github/v/tag/edwardtfn/TX-Ultimate-Easy?label=version [version-shield]: https://img.shields.io/github/v/tag/edwardtfn/TX-Ultimate-Easy?label=version
[version]: https://github.com/edwardtfn/TX-Ultimate-Easy/tags [version]: https://github.com/edwardtfn/TX-Ultimate-Easy/tags
@@ -92,12 +92,210 @@ Before getting started, ensure you have:
## Installation ## Installation
Detailed installation instructions coming soon. The process will include: Follow these steps to get your TX Ultimate device up and running with ESPHome.
1. Initial ESPHome setup ### ESPHome Integration Setup
2. Device flashing
3. Home Assistant integration 1. Install the ESPHome add-on in Home Assistant if not already installed:
4. Basic configuration - Go to Settings → Add-ons → Add-on Store
- Search for "ESPHome"
- Click Install
2. Start the ESPHome add-on and verify it's running
3. Access ESPHome dashboard through Home Assistant
### Setup Device
1. In the ESPHome dashboard, click "+ New Device"
2. Name your device (e.g., "tx-ultimate-living-room")
3. Select ESP32 as your device type
4. Copy this basic configuration to your new device:
```yaml
substitutions:
name: tx-ultimate-easy
friendly_name: TX Ultimate Easy
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:
- 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_addon_ble_proxy.yaml # Adds BLE proxy support
```
You can also use a specific version tag for better control over updates:
```yaml
ref: v2024.12.2 # Using specific version for controlled updates
```
**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"
> [!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
> ```
### 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: 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
- 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
```
> [!NOTE]
> Use the advanced configuration with caution. Excluding core packages may cause instability or reduced functionality.
### Device Flashing
Initial flashing must be done via serial connection.
We recommend using [ESPHome Web](https://web.esphome.io) for the simplest experience.
<!-- markdownlint-disable MD028 -->
> [!IMPORTANT]
> **SAFETY WARNINGS**
> - ALWAYS disconnect the device from mains power before opening
> - NEVER work on the device while connected to mains power
> - Ensure the device is completely powered off before making any connections
> - Keep your workspace clean, dry, and static-free
> - Use proper insulated tools when working with electronics
> - If unsure about any step, seek help from an experienced person
> [!CAUTION]
>⚡ **CRITICAL: VOLTAGE WARNING**
> Using a UART adapter with voltage higher than 3.3V WILL DAMAGE YOUR DEVICE.
> Double-check your adapter's voltage before connecting - many common FTDI adapters
> default to 5V which will permanently damage the ESP32 chip.
<!-- markdownlint-enable MD028 -->
#### Required Hardware
- USB-to-UART adapter:
- 3.3V logic level ONLY (DO NOT use 5V adapters)
- Must be capable of supplying at least 500mA
- Common adapters: CP2102, CH340, FTDI (ensure 3.3V setting)
- Small Phillips screwdriver
- 5 wires for connections (including one for BOOT to GND)
#### Flashing Process
1. Open your TX Ultimate device carefully
2. Locate the programming header pins
3. Connect your USB-to-UART adapter:
| Adapter | Device |
|---------|--------|
| GND | GND |
| 3.3V | 3.3V |
| TX | RX |
| RX | TX |
4. Put device in flash mode:
- Temporarily connect the BOOT pin to GND using a jumper wire
- While holding BOOT to GND, power up the device
- After device powers up (wait a couple of seconds), remove the BOOT to GND connection
5. Visit [ESPHome Web](https://web.esphome.io)
6. Connect to your device and flash the firmware
7. After successful flash, device will restart and be ready for OTA updates
#### Detailed Visual Guides
<!-- markdownlint-disable MD013 -->
For step-by-step visual instructions, you can reference these existing guides:
- 🇬🇧 [WirelessThings Guide](https://wirelessthings.io/index.php/2023/12/19/how-to-flash-sonoff-tx-ultimate-with-esphome/) - English guide with detailed photos
- 🇪🇸 [Un loco y su tecnología](https://youtu.be/58v8oqSQgXQ?t=143) - Spanish video tutorial
- 🇩🇪 [SmartHome yourself](https://youtu.be/naDLhX89enQ?t=465) - German video tutorial
<!-- markdownlint-enable MD013 -->
Note: While these guides may use different firmware, the physical flashing process remains the same.
#### Subsequent Updates
After initial flashing, all future updates can be done wirelessly (OTA)
through the ESPHome dashboard in your ESPHome add-on.
### Home Assistant Integration
After successful flashing:
1. Ensure your device and Home Assistant are on the same network
2. Device should be automatically discovered within 12 minutes
3. Accept the discovery notification in Home Assistant to add device
Note: If discovery takes longer than 5 minutes, proceed to troubleshooting steps
4. Device will appear in your Home Assistant Devices dashboard
#### Troubleshooting Integration
If the device isn't discovered automatically:
1. Verify your device is powered and connected to your network:
- Look for the device in your router's client list
- Consider using [manual IP](https://esphome.io/components/wifi.html#manual-ips) in your device
2. If you missed the discovery notification:
- Go to Settings → Devices & Services
- Click "Add Integration"
- Search for "ESPHome" and enter your device's IP address
3. Still having issues?
- Check your network allows mDNS/discovery traffic
- Verify there are no VLANs or network isolation preventing communication
- Try rebooting both the device and Home Assistant
### Initial Configuration
1. In Home Assistant, navigate to:
- Settings → Devices & Services → ESPHome
- Click on your device to access its configuration page
2. Set basic device parameters:
- Model format (EU/US)
- Number of gangs (1-4)
- Relay modes (switch/light)
- Button actions
3. Optional: Configure advanced features
- LED behaviors
- Touch sensitivity
- Haptic feedback
- Audio feedback
4. Test your configuration:
- Verify each relay responds to controls
- Test configured button actions
- Confirm LED behavior matches settings
- Check haptic/audio feedback if enabled
> [!NOTE]
> Some changes may require a device restart
## Usage ## Usage
@@ -154,8 +352,11 @@ Note: For proper tracking and resolution:
This project builds upon the work of several amazing projects and contributors: This project builds upon the work of several amazing projects and contributors:
<!-- markdownlint-disable MD013 -->
- [SmartHome yourself - SONOFF TX Ultimate for ESPHome](https://github.com/SmartHome-yourself/sonoff-tx-ultimate-for-esphome) - [SmartHome yourself - SONOFF TX Ultimate for ESPHome](https://github.com/SmartHome-yourself/sonoff-tx-ultimate-for-esphome)
- [Un loco y su tecnología - Sonoff TX Ultimate with ESPHome](https://www.youtube.com/watch?v=58v8oqSQgXQ) - [Un loco y su tecnología - Sonoff TX Ultimate with ESPHome](https://www.youtube.com/watch?v=58v8oqSQgXQ)
- [@PxPert](https://github.com/PxPert) - [Sonoff TX Ultimate and Voice Assistant](https://community.home-assistant.io/t/sonoff-tx-ultimate-and-voice-assistant/682214?u=edwardtfn)
<!-- markdownlint-enable MD013 -->
Special thanks to all contributors and community members who help make this project better. Special thanks to all contributors and community members who help make this project better.
@@ -166,5 +367,9 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
--- ---
<!-- markdownlint-disable MD033 --> <!-- markdownlint-disable MD033 -->
<img src="Assets/Logo.webp" alt="TX Ultimate Easy Logo" width="100"/> <picture>
<source media="(prefers-color-scheme: dark)" srcset="Assets/Logo_dark.png">
<source media="(prefers-color-scheme: light)" srcset="Assets/Logo_light.png">
<img alt="TX Ultimate Easy Logo" src="Assets/Logo_light.png" width="100">
</picture>
<!-- markdownlint-enable MD033 --> <!-- markdownlint-enable MD033 -->

View File

@@ -16,9 +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 - ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml # Core (essential) packages
# - ESPHome/TX-Ultimate-Easy-ESPHome_addon_ble_proxy.yaml # Adds experimental BLE proxy support (limited) - 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
... ...

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

@@ -2,6 +2,7 @@
#include "esphome/core/log.h" #include "esphome/core/log.h"
#include "tx_ultimate_easy.h" #include "tx_ultimate_easy.h"
#include <cinttypes>
#include <string> #include <string>
namespace esphome { namespace esphome {
@@ -68,9 +69,9 @@ namespace esphome {
if (this->gang_count_ == 1) if (this->gang_count_ == 1)
return 1; return 1;
// Calculate button number //Calculate button number Change to round up insted of truncate (integer division)
const uint8_t width = (TOUCH_MAX_POSITION + 1) / this->gang_count_; // Width of each button region const uint8_t width = (TOUCH_MAX_POSITION + gang_count_) / this->gang_count_; // Width of each button region
if (width < 1 or width > this->gang_count_) // Invalid width - and prevents division by zero if (width < 1) // Invalid width - and prevents division by zero removed "width > gang_count_" issue with 2 gang
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

View File

@@ -1 +1 @@
2024.12.12 2025.3.1

View File

@@ -1 +1 @@
version: 2024.12.12 version: 2025.3.1