From ecfc64d2f33623291a92242490e5e7f30f5fab6a Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Mon, 4 Aug 2025 21:29:29 +0200 Subject: [PATCH 01/50] add PR information to tag descriptions in versioning workflow Enhance the versioning workflow to include PR title and description in tag messages. Changes: - Add PR information extraction step using GitHub API - Format tag messages with version, PR title, and description - Apply structured messages to stable, latest, and version tags - Handle fallback cases for manual dispatches and API errors - Use temporary file approach for reliable multiline message handling Tag descriptions now follow the format: `# v{version} - {PR title}` This improves release traceability by providing clear context for what changes are included in each tagged version. --- .github/workflows/versioning.yml | 90 ++++++++++++++++++++++++++++++-- 1 file changed, 86 insertions(+), 4 deletions(-) diff --git a/.github/workflows/versioning.yml b/.github/workflows/versioning.yml index dc50d74..d3116b7 100644 --- a/.github/workflows/versioning.yml +++ b/.github/workflows/versioning.yml @@ -41,11 +41,87 @@ jobs: git config user.name "GitHub Actions" git config user.email "actions@github.com" + - name: Get PR information + id: pr_info + if: github.event_name == 'push' + uses: actions/github-script@v7 + with: + script: | + try { + // Get the commit that triggered this workflow + const commit = await github.rest.repos.getCommit({ + owner: context.repo.owner, + repo: context.repo.repo, + ref: context.sha + }); + + // Look for PR number in commit message (GitHub merge commits include this) + const commitMessage = commit.data.commit.message; + const prMatch = commitMessage.match(/Merge pull request #(\d+)/); + + if (prMatch) { + const prNumber = parseInt(prMatch[1]); + const pr = await github.rest.pulls.get({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: prNumber + }); + + return { + title: pr.data.title, + body: pr.data.body || 'No description provided', + number: prNumber + }; + } else { + // Fallback for direct pushes + return { + title: 'Direct push to main', + body: commitMessage, + number: null + }; + } + } catch (error) { + console.log('Could not get PR info:', error.message); + return { + title: 'Version update', + body: 'Automated version bump', + number: null + }; + } + - name: Bump version run: | chmod +x ./versioning/bump_version.sh ./versioning/bump_version.sh + - name: Create tag message + id: tag_message + run: | + NEW_VERSION=$(cat ./versioning/VERSION) + + if [ "${{ github.event_name }}" == "push" ]; then + # Use PR information from previous step + PR_TITLE='${{ fromJson(steps.pr_info.outputs.result).title }}' + PR_BODY='${{ fromJson(steps.pr_info.outputs.result).body }}' + + # Create formatted tag message + cat > tag_message.txt << EOF + # v${NEW_VERSION} - ${PR_TITLE} + + ${PR_BODY} + EOF + else + # Manual dispatch - simpler message + cat > tag_message.txt << EOF + # v${NEW_VERSION} - Manual tag update + + Tag updated via manual workflow dispatch. + EOF + fi + + # Store the message for use in subsequent steps + echo "TAG_MESSAGE_FILE=tag_message.txt" >> $GITHUB_ENV + - name: Push Changes and Tags env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -72,10 +148,10 @@ jobs: echo "Backing up current stable tag ($OLD_STABLE)" fi - # Update tag + # Update tag with detailed message NEW_VERSION=$(cat ./versioning/VERSION) echo "Updating stable tag to $NEW_VERSION" - git tag -fa stable -m "Update stable tag to $NEW_VERSION" + git tag -fa stable -F "$TAG_MESSAGE_FILE" git push origin stable --force - name: Update latest tag @@ -97,9 +173,15 @@ jobs: echo "Backing up current latest tag ($OLD_LATEST)" fi - # Update tag + # Update tag with detailed message NEW_VERSION=$(cat ./versioning/VERSION) echo "Updating latest tag to $NEW_VERSION" - git tag -fa latest -m "Update latest tag to $NEW_VERSION" + git tag -fa latest -F "$TAG_MESSAGE_FILE" git push origin latest --force + + - name: Cleanup + if: always() + run: | + # Clean up temporary files + [ -f tag_message.txt ] && rm tag_message.txt || true ... From 42ae160217e08ddbe7e0b0fe4f3fa3fda2e52a1d Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Mon, 4 Aug 2025 23:50:06 +0200 Subject: [PATCH 02/50] Introduce `restore_from_nvs` on core.common --- .../TX-Ultimate-Easy-ESPHome_core_common.yaml | 59 ++++++++++++------- 1 file changed, 39 insertions(+), 20 deletions(-) diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core_common.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core_common.yaml index a254e28..c392348 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core_common.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core_common.yaml @@ -48,9 +48,9 @@ binary_sensor: then: - lambda: |- if (x) - ESP_LOGW("core", "Pending restart: YES"); + ESP_LOGW("binary_sensor.bs_pending_restart.core.common", "Pending restart: YES"); else - ESP_LOGCONFIG("core", "Pending restart: No"); + ESP_LOGCONFIG("binary_sensor.bs_pending_restart.core.common", "Pending restart: No"); - script.execute: dump_config_caller button: @@ -77,6 +77,10 @@ esphome: - -D TX_ULTIMATE_EASY_CORE on_boot: + - priority: 750 + then: + - script.execute: restore_from_nvs + - priority: 700 then: - script.execute: boot_initialize @@ -149,7 +153,7 @@ script: id(is_us_model) = (sl_tx_model_format->state == "${TX_MODEL_FORMAT_US_TEXT}"); id(gang_count) = sl_tx_model_gang->active_index().value() + 1; if (id(gang_count) < 1 || id(gang_count) > 4) { - ESP_LOGE("core_hw_leds", "Invalid number of gangs: %" PRIu8, id(gang_count)); + ESP_LOGE("script.boot_initialize.core.common", "Invalid number of gangs: %" PRIu8, id(gang_count)); } - id: boot_sequence @@ -166,33 +170,37 @@ script: # Extended by all modules - lambda: |- // Device identification - ESP_LOGCONFIG("core", "Device friendly name: ${friendly_name}"); - ESP_LOGCONFIG("core", "Device name: ${name}"); - ESP_LOGCONFIG("core", "Device name (HA): %s", tx_device_name->state.c_str()); - ESP_LOGCONFIG("core", "Device hostname: %s", App.get_name().c_str()); + ESP_LOGCONFIG("script.dump_config.core.common", "Device friendly name: ${friendly_name}"); + ESP_LOGCONFIG("script.dump_config.core.common", "Device name: ${name}"); + ESP_LOGCONFIG("script.dump_config.core.common", "Device name (HA): %s", tx_device_name->state.c_str()); + ESP_LOGCONFIG("script.dump_config.core.common", "Device hostname: %s", App.get_name().c_str()); dump_config_versions->execute(); // Framework detection #ifdef USE_ARDUINO - ESP_LOGCONFIG("core", "Framework: Arduino"); + ESP_LOGCONFIG("script.dump_config.core.common", "Framework: Arduino"); #elif defined(USE_ESP_IDF) - ESP_LOGCONFIG("core", "Framework: ESP-IDF"); + ESP_LOGCONFIG("script.dump_config.core.common", "Framework: ESP-IDF"); #else - ESP_LOGW("core", "Framework: UNKNOWN"); + ESP_LOGW("script.dump_config.core.common", "Framework: UNKNOWN"); #endif // Model configuration - ESP_LOGCONFIG("core", "Model format (selected): %s", sl_tx_model_format->state.c_str()); - ESP_LOGCONFIG("core", "Model format (detected): %s", id(is_us_model) ? "US" : "EU"); - ESP_LOGCONFIG("core", "Gangs (selected): %s", sl_tx_model_gang->state.c_str()); - ESP_LOGCONFIG("core", "Gangs (detected): %" PRIu8 "-Gang%s", id(gang_count), id(gang_count) > 1 ? "s" : ""); + ESP_LOGCONFIG("script.dump_config.core.common", "Model format (selected): %s", + sl_tx_model_format->state.c_str()); + ESP_LOGCONFIG("script.dump_config.core.common", "Model format (detected): %s", + id(is_us_model) ? "US" : "EU"); + ESP_LOGCONFIG("script.dump_config.core.common", "Gangs (selected): %s", + sl_tx_model_gang->state.c_str()); + ESP_LOGCONFIG("script.dump_config.core.common", "Gangs (detected): %" PRIu8 "-Gang%s", + id(gang_count), id(gang_count) > 1 ? "s" : ""); // System state if (bs_pending_restart->state) - ESP_LOGW("core", "Pending restart: YES"); + ESP_LOGW("script.dump_config.core.common", "Pending restart: YES"); else - ESP_LOGCONFIG("core", "Pending restart: No"); + ESP_LOGCONFIG("script.dump_config.core.common", "Pending restart: No"); - id: dump_config_caller mode: restart @@ -229,6 +237,16 @@ script: tx_fw_version->publish_state("${version}"); tx_device_name->publish_state(App.get_name().c_str()); + - id: restore_from_nvs + mode: single + then: + - select.set_index: + id: sl_tx_model_format + index: !lambda if (id(is_us_model)) return 1; else return 0; + - select.set_index: + id: sl_tx_model_gang + index: !lambda return (id(gang_count)-1); + select: - id: sl_tx_model_format name: Model (Format) @@ -238,7 +256,7 @@ select: - "${TX_MODEL_FORMAT_US_TEXT}" initial_option: "${TX_MODEL_FORMAT_EU_TEXT}" optimistic: true - restore_value: true + restore_value: false internal: false entity_category: config disabled_by_default: false @@ -250,7 +268,7 @@ select: state: true - lambda: |- id(is_us_model) = (x == "${TX_MODEL_FORMAT_US_TEXT}"); - ESP_LOGI("core", "New model selected: %s", id(is_us_model) ? "US" : "EU"); + ESP_LOGI("select.sl_tx_model_gang.core.common", "New model selected: %s", id(is_us_model) ? "US" : "EU"); - id: sl_tx_model_gang name: Model (Gang) @@ -262,7 +280,7 @@ select: - "${TX_MODEL_4_GANG_TEXT}" initial_option: "${TX_MODEL_1_GANG_TEXT}" optimistic: true - restore_value: true + restore_value: false internal: false entity_category: config disabled_by_default: false @@ -274,7 +292,8 @@ select: state: true - lambda: |- id(gang_count) = static_cast(i) + 1; - ESP_LOGI("core", "New model selected: %" PRIu8 "-%s", id(gang_count), id(gang_count) > 1 ? "Gangs" : "Gang"); + ESP_LOGI("select.sl_tx_model_gang.core.common", "New model selected: %" PRIu8 "-%s", + id(gang_count), id(gang_count) > 1 ? "Gangs" : "Gang"); tx_ultimate->set_gang_count(id(gang_count)); text_sensor: From 6d16d7d645b72e59f2f52783e6bb2e02a7d12713 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Tue, 5 Aug 2025 08:11:50 +0200 Subject: [PATCH 03/50] `restore_from_nvs` for `core_hw_buttons` --- ...Ultimate-Easy-ESPHome_core_hw_buttons.yaml | 83 ++++++++++++++----- 1 file changed, 64 insertions(+), 19 deletions(-) diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml index d2be16f..a64a136 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml @@ -63,6 +63,14 @@ esphome: - -D TX_ULTIMATE_EASY_CORE_HW_BUTTONS globals: + # Packed button actions - replaces 4 separate selects + - id: button_actions_packed + type: uint16_t + restore_value: true + initial_value: '4369' # Binary: 0001000100010001 = all buttons set to option 1 (default action) + # Bit layout: [button4(4)][button3(4)][button2(4)][button1(4)] + # Each button uses 4 bits: 0=None, 1=Toggle, 2=Failsafe, 3-15=Reserved for future options + - id: button_press_button type: uint8_t restore_value: false @@ -113,6 +121,8 @@ script: count: uint8_t then: - lambda: |- + static const char *TAG = "script.button_action.core.hw.buttons"; + // Send event to Home Assistant esphome::api::CustomAPIDevice ha_event; ha_event.fire_homeassistant_event("${EVENT_NAME}", { @@ -125,7 +135,7 @@ script: {"count", std::to_string(count)}, {"position", std::to_string(id(button_press_position))} }); - ESP_LOGI("core_hw_buttons", "Button %" PRIu8 " action: '%s'", button, action.c_str()); + ESP_LOGI(TAG, "Button %" PRIu8 " action: '%s'", button, action.c_str()); if (action == "click") { switch (button) { case ${BUTTON_1_ID}: @@ -209,26 +219,24 @@ script: - id: !extend dump_config then: - lambda: |- + static const char *TAG = "script.dump_config.core.hw.buttons"; + std::string gang_count_plural_suffix = id(gang_count) > 1 ? "s" : ""; // Button's actions - ESP_LOGCONFIG("core_hw_buttons", "Button%s action%s:", + ESP_LOGCONFIG(TAG, "Button%s action%s:", gang_count_plural_suffix.c_str(), gang_count_plural_suffix.c_str()); - ESP_LOGCONFIG("core_hw_buttons", " Relay 1: %s", sl_button_1_action->has_state() - ? sl_button_1_action->state.c_str() - : "Unknown"); + ESP_LOGCONFIG(TAG, " 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"); + ESP_LOGCONFIG(TAG, " 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"); + ESP_LOGCONFIG(TAG, " 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"); + ESP_LOGCONFIG(TAG, " Relay 4: %s", sl_button_4_action->has_state() ? + sl_button_4_action->state.c_str() : "Unknown"); - id: !extend dump_config_list_packages then: @@ -242,6 +250,21 @@ script: // Identify itself ESP_LOGCONFIG(ESPHOME_PROJECT_NAME, " - Core - Hardware - Buttons"); + - id: !extend restore_from_nvs + then: + - select.set_index: + id: sl_button_1_action + index: !lambda return std::min((uint8_t)(id(button_actions_packed) & 0x0F), (uint8_t)2); + - select.set_index: + id: sl_button_2_action + index: !lambda return std::min((uint8_t)((id(button_actions_packed) >> 4) & 0x0F), (uint8_t)2); + - select.set_index: + id: sl_button_3_action + index: !lambda return std::min((uint8_t)((id(button_actions_packed) >> 8) & 0x0F), (uint8_t)2); + - select.set_index: + id: sl_button_4_action + index: !lambda return std::min((uint8_t)((id(button_actions_packed) >> 12) & 0x0F), (uint8_t)2); + - id: !extend touch_on_multi_touch_release then: - script.execute: buttons_release @@ -288,25 +311,27 @@ script: - id: !extend touch_on_release then: - lambda: |- + static const char *TAG = "script.touch_on_release.core.hw.buttons"; + uint32_t current_time = millis(); buttons_release->execute(); if (id(button_press_start_time) > 0 and id(button_press_start_time) < current_time) { uint32_t press_duration = current_time - id(button_press_start_time); // Handle overflow (optional, since it's unlikely to happen here) - ESP_LOGI("core_hw_buttons", "Button press duration: %" PRIu32 " ms", press_duration); + ESP_LOGI(TAG, "Button press duration: %" PRIu32 " ms", press_duration); if (press_duration < ${BUTTON_CLICK_MIN_LENGTH}) { - ESP_LOGW("core_hw_buttons", "Ignoring button press (too short)"); + ESP_LOGW(TAG, "Ignoring button press (too short)"); } else if (press_duration >= ${BUTTON_CLICK_MIN_LENGTH} and press_duration <= ${BUTTON_CLICK_MAX_LENGTH}) { // Short/normal click button_click_event->execute(id(button_press_button), id(click_counter)); } else if (press_duration >= ${BUTTON_LONG_PRESS_DELAY} and press_duration <= ${BUTTON_PRESS_TIMEOUT}) { button_action->execute(id(button_press_button), "long_press", 1); } else if (press_duration > ${BUTTON_PRESS_TIMEOUT}) { // Timeout or invalid - ESP_LOGW("core_hw_buttons", "Button press cancelled or timed out after ${BUTTON_PRESS_TIMEOUT} ms"); + ESP_LOGW(TAG, "Button press cancelled or timed out after ${BUTTON_PRESS_TIMEOUT} ms"); } } else { - ESP_LOGW("core_hw_buttons", "Press event timestamp not recorded yet"); + ESP_LOGW(TAG, "Press event timestamp not recorded yet"); } id(button_press_start_time) = 0; @@ -329,11 +354,16 @@ select: - "${BUTTON_1_ACTION_FAILSAFE_TEXT}" initial_option: "${BUTTON_1_ACTION_TEXT}" optimistic: true - restore_value: true + restore_value: false internal: true entity_category: config disabled_by_default: false icon: mdi:dip-switch + on_value: + then: + - lambda: |- + id(button_actions_packed) = (id(button_actions_packed) & ~(0x07 << 0)) | + ((id(sl_button_1_action).active_index().value_or(1) & 0x07) << 0); - id: sl_button_2_action name: Button 2 action @@ -344,6 +374,11 @@ select: - "${BUTTON_2_ACTION_FAILSAFE_TEXT}" initial_option: "${BUTTON_2_ACTION_TEXT}" <<: *button_select_action_base + on_value: + then: + - lambda: |- + id(button_actions_packed) = (id(button_actions_packed) & ~(0x07 << 4)) | + ((id(sl_button_2_action).active_index().value_or(1) & 0x07) << 4); - id: sl_button_3_action name: Button 3 action @@ -354,6 +389,11 @@ select: - "${BUTTON_3_ACTION_FAILSAFE_TEXT}" initial_option: "${BUTTON_3_ACTION_TEXT}" <<: *button_select_action_base + on_value: + then: + - lambda: |- + id(button_actions_packed) = (id(button_actions_packed) & ~(0x07 << 8)) | + ((id(sl_button_3_action).active_index().value_or(1) & 0x07) << 8); - id: sl_button_4_action name: Button 4 action @@ -364,4 +404,9 @@ select: - "${BUTTON_4_ACTION_FAILSAFE_TEXT}" initial_option: "${BUTTON_4_ACTION_TEXT}" <<: *button_select_action_base + on_value: + then: + - lambda: |- + id(button_actions_packed) = (id(button_actions_packed) & ~(0x07 << 12)) | + ((id(sl_button_4_action).active_index().value_or(1) & 0x07) << 12); ... From 1d8b4fee1a7374e52fd03fd3b78b16f287bdfd13 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Tue, 5 Aug 2025 08:54:45 +0200 Subject: [PATCH 04/50] Standardize log tag --- .../TX-Ultimate-Easy-ESPHome_core_common.yaml | 41 +++++---- ...X-Ultimate-Easy-ESPHome_core_hw_touch.yaml | 43 +++++---- ...imate-Easy-ESPHome_standard_hw_relays.yaml | 92 +++++++++---------- ...mate-Easy-ESPHome_standard_hw_speaker.yaml | 4 +- ...te-Easy-ESPHome_standard_hw_vibration.yaml | 12 ++- ...te-Easy-ESPHome_standard_media_player.yaml | 16 +++- 6 files changed, 115 insertions(+), 93 deletions(-) diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core_common.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core_common.yaml index c392348..d1c85d8 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core_common.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core_common.yaml @@ -47,10 +47,12 @@ binary_sensor: on_state: then: - lambda: |- + static const char *TAG = "binary_sensor.bs_pending_restart.core.common"; + if (x) - ESP_LOGW("binary_sensor.bs_pending_restart.core.common", "Pending restart: YES"); + ESP_LOGW(TAG, "Pending restart: YES"); else - ESP_LOGCONFIG("binary_sensor.bs_pending_restart.core.common", "Pending restart: No"); + ESP_LOGD(TAG, "Pending restart: No"); - script.execute: dump_config_caller button: @@ -150,10 +152,12 @@ script: - lambda: return sl_tx_model_format->active_index().has_value(); - lambda: return sl_tx_model_gang->active_index().has_value(); - lambda: |- + static const char *TAG = "script.boot_initialize.core.common"; + id(is_us_model) = (sl_tx_model_format->state == "${TX_MODEL_FORMAT_US_TEXT}"); id(gang_count) = sl_tx_model_gang->active_index().value() + 1; if (id(gang_count) < 1 || id(gang_count) > 4) { - ESP_LOGE("script.boot_initialize.core.common", "Invalid number of gangs: %" PRIu8, id(gang_count)); + ESP_LOGE(TAG, "Invalid number of gangs: %" PRIu8, id(gang_count)); } - id: boot_sequence @@ -169,38 +173,40 @@ script: then: # Extended by all modules - lambda: |- + static const char *TAG = "script.dump_config.core.common"; + // Device identification - ESP_LOGCONFIG("script.dump_config.core.common", "Device friendly name: ${friendly_name}"); - ESP_LOGCONFIG("script.dump_config.core.common", "Device name: ${name}"); - ESP_LOGCONFIG("script.dump_config.core.common", "Device name (HA): %s", tx_device_name->state.c_str()); - ESP_LOGCONFIG("script.dump_config.core.common", "Device hostname: %s", App.get_name().c_str()); + ESP_LOGCONFIG(TAG, "Device friendly name: ${friendly_name}"); + ESP_LOGCONFIG(TAG, "Device name: ${name}"); + ESP_LOGCONFIG(TAG, "Device name (HA): %s", tx_device_name->state.c_str()); + ESP_LOGCONFIG(TAG, "Device hostname: %s", App.get_name().c_str()); dump_config_versions->execute(); // Framework detection #ifdef USE_ARDUINO - ESP_LOGCONFIG("script.dump_config.core.common", "Framework: Arduino"); + ESP_LOGCONFIG(TAG, "Framework: Arduino"); #elif defined(USE_ESP_IDF) - ESP_LOGCONFIG("script.dump_config.core.common", "Framework: ESP-IDF"); + ESP_LOGCONFIG(TAG, "Framework: ESP-IDF"); #else - ESP_LOGW("script.dump_config.core.common", "Framework: UNKNOWN"); + ESP_LOGW(TAG, "Framework: UNKNOWN"); #endif // Model configuration - ESP_LOGCONFIG("script.dump_config.core.common", "Model format (selected): %s", + ESP_LOGCONFIG(TAG, "Model format (selected): %s", sl_tx_model_format->state.c_str()); - ESP_LOGCONFIG("script.dump_config.core.common", "Model format (detected): %s", + ESP_LOGCONFIG(TAG, "Model format (detected): %s", id(is_us_model) ? "US" : "EU"); - ESP_LOGCONFIG("script.dump_config.core.common", "Gangs (selected): %s", + ESP_LOGCONFIG(TAG, "Gangs (selected): %s", sl_tx_model_gang->state.c_str()); - ESP_LOGCONFIG("script.dump_config.core.common", "Gangs (detected): %" PRIu8 "-Gang%s", + ESP_LOGCONFIG(TAG, "Gangs (detected): %" PRIu8 "-Gang%s", id(gang_count), id(gang_count) > 1 ? "s" : ""); // System state if (bs_pending_restart->state) - ESP_LOGW("script.dump_config.core.common", "Pending restart: YES"); + ESP_LOGW(TAG, "Pending restart: YES"); else - ESP_LOGCONFIG("script.dump_config.core.common", "Pending restart: No"); + ESP_LOGCONFIG(TAG, "Pending restart: No"); - id: dump_config_caller mode: restart @@ -268,7 +274,6 @@ select: state: true - lambda: |- id(is_us_model) = (x == "${TX_MODEL_FORMAT_US_TEXT}"); - ESP_LOGI("select.sl_tx_model_gang.core.common", "New model selected: %s", id(is_us_model) ? "US" : "EU"); - id: sl_tx_model_gang name: Model (Gang) @@ -292,8 +297,6 @@ select: state: true - lambda: |- id(gang_count) = static_cast(i) + 1; - ESP_LOGI("select.sl_tx_model_gang.core.common", "New model selected: %" PRIu8 "-%s", - id(gang_count), id(gang_count) > 1 ? "Gangs" : "Gang"); tx_ultimate->set_gang_count(id(gang_count)); text_sensor: diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_touch.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_touch.yaml index 0bf617c..0e1377b 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_touch.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_touch.yaml @@ -291,12 +291,14 @@ script: - id: !extend dump_config then: - lambda: |- + static const char *TAG = "script.dump_config.core.hw.touch"; + // Touch panel's state if (sw_touch_panel_power->state) - ESP_LOGCONFIG("core_hw_touch", "Touch panel - Power: On"); + ESP_LOGCONFIG(TAG, "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); + ESP_LOGW(TAG, "Touch panel - Power: OFF"); + ESP_LOGCONFIG(TAG, "Touch event duration: %.0fms", nr_touch_duration->state); - id: !extend dump_config_list_packages then: @@ -378,11 +380,13 @@ tx_ultimate_easy: on_long_touch_release: - lambda: |- + static const char *TAG = "tx_ultimate_easy.on_long_touch_release.core.hw.touch"; + const uint8_t touch_position = static_cast(touch.x); if (touch_position > ${TOUCH_POSITION_MAX_VALUE}) { // Check for valid range - ESP_LOGE("core_hw_touch", "Invalid long-touch position: %" PRIu8, touch_position); + ESP_LOGE(TAG, "Invalid long-touch position: %" PRIu8, touch_position); } else { - ESP_LOGI("core_hw_touch", "Long-touch released at position %" PRIu8, touch_position); + ESP_LOGI(TAG, "Long-touch released at position %" PRIu8, touch_position); } on_multi_touch_release: @@ -397,23 +401,29 @@ tx_ultimate_easy: action: release position: !lambda return std::to_string(touch.x); - lambda: |- - ESP_LOGI("core_hw_touch", "Multi-touch released"); + static const char *TAG = "tx_ultimate_easy.on_multi_touch_release.core.hw.touch"; + + ESP_LOGI(TAG, "Multi-touch released"); touch_on_multi_touch_release->execute(); on_press: then: - lambda: |- + static const char *TAG = "tx_ultimate_easy.on_press.core.hw.touch"; + const uint8_t position = static_cast(touch.x); if (position > ${TOUCH_POSITION_MAX_VALUE}) { // Check for valid range - ESP_LOGE("core_hw_touch", "Invalid touch position: %" PRIu8, position); + ESP_LOGE(TAG, "Invalid touch position: %" PRIu8, position); } else { - ESP_LOGI("core_hw_touch", "Pressed at position %" PRIu8, position); + ESP_LOGI(TAG, "Pressed at position %" PRIu8, position); touch_on_press->execute(static_cast(touch.button), position); } on_release: then: - - lambda: ESP_LOGI("core_hw_touch", "Released"); + - lambda: |- + static const char *TAG = "tx_ultimate_easy.on_release.core.hw.touch"; + ESP_LOGI(TAG, "Released"); - script.execute: touch_on_release on_swipe_left: @@ -437,8 +447,8 @@ tx_ultimate_easy: return "down"; position: !lambda return std::to_string(touch.x); - lambda: |- - ESP_LOGI("core_hw_touch", "Swipe %s", - (sl_tx_model_format->state == "${TX_MODEL_FORMAT_EU_TEXT}") ? "left" : "down"); + static const char *TAG = "tx_ultimate_easy.on_swipe_left.core.hw.touch"; + ESP_LOGI(TAG, "Swipe %s", (sl_tx_model_format->state == "${TX_MODEL_FORMAT_EU_TEXT}") ? "left" : "down"); touch_swipe_left->execute(); on_swipe_right: @@ -462,15 +472,16 @@ tx_ultimate_easy: return "up"; position: !lambda return std::to_string(touch.x); - lambda: |- - ESP_LOGI("core_hw_touch", "Swipe %s", - (sl_tx_model_format->state == "${TX_MODEL_FORMAT_EU_TEXT}") ? "right" : "up"); + static const char *TAG = "tx_ultimate_easy.on_swipe_right.core.hw.touch"; + ESP_LOGI(TAG, "Swipe %s", (sl_tx_model_format->state == "${TX_MODEL_FORMAT_EU_TEXT}") ? "right" : "up"); touch_swipe_right->execute(); on_touch_event: - lambda: |- - ESP_LOGD("core_hw_touch", "Touch event:"); - ESP_LOGD("core_hw_touch", " State: %i (%s)", touch.state, touch.state_str.c_str()); - ESP_LOGD("core_hw_touch", " Position: %i", touch.x); + static const char *TAG = "tx_ultimate_easy.on_touch_event.core.hw.touch"; + ESP_LOGD(TAG, "Touch event:"); + ESP_LOGD(TAG, " State: %i (%s)", touch.state, touch.state_str.c_str()); + ESP_LOGD(TAG, " Position: %i", touch.x); uart: - id: uart_touch diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml index 0f019eb..9e9839e 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml @@ -466,6 +466,8 @@ script: mode: restart then: - lambda: |- + static const char *TAG = "script.boot_initialize_relays_group_assignments.standard.hw.relays"; + // LED Group Assignment: // - For US model: gb_lights_1 = left side LEDs, gb_lights_2 = right side LEDs // - For EU model: gb_lights_1 = bottom LEDs, gb_lights_2 = top LEDs @@ -512,7 +514,7 @@ script: } break; default: - ESP_LOGE("standard_hw_relays", "Invalid gang count: %" PRIu8, id(gang_count)); + ESP_LOGE(TAG, "Invalid gang count: %" PRIu8, id(gang_count)); break; } id(boot_initialization_relays_group_assignments) = true; @@ -635,6 +637,8 @@ script: - id: !extend button_click_event then: - lambda: |- + static const char *TAG = "script.button_click_event.standard.hw.relays"; + // Handle only single clicks if (click_count != 1) return; @@ -653,13 +657,13 @@ script: std::string select_state; bool toggle_relay = false; if (api_disconnected) { - ESP_LOGW("standard_hw_relays", "Toggle relays:"); - ESP_LOGW("standard_hw_relays", " API state: DISCONNECTED"); - ESP_LOGW("standard_hw_relays", " Button: %" PRIu8, button); + ESP_LOGW(TAG, "Toggle relays:"); + ESP_LOGW(TAG, " API state: DISCONNECTED"); + ESP_LOGW(TAG, " Button: %" PRIu8, button); } else { - ESP_LOGVV("standard_hw_relays", "Toggle relays:"); - ESP_LOGVV("standard_hw_relays", " API state: Connected"); - ESP_LOGVV("standard_hw_relays", " Button: %" PRIu8, button); + ESP_LOGVV(TAG, "Toggle relays:"); + ESP_LOGVV(TAG, " API state: Connected"); + ESP_LOGVV(TAG, " Button: %" PRIu8, button); } switch (button) { case ${BUTTON_1_ID}: @@ -692,65 +696,57 @@ script: break; } if (toggle_relay) { - ESP_LOGI("standard_hw_relays", "Toggle relay %" PRIu8 ": (%s)", button, select_state.c_str()); + ESP_LOGI(TAG, "Toggle relay %" PRIu8 ": (%s)", button, select_state.c_str()); } else if (not select_state.empty()) { - ESP_LOGV("standard_hw_relays", "Ignoring toggle for relay %" PRIu8 ": action is '%s'", + ESP_LOGV(TAG, "Ignoring toggle for relay %" PRIu8 ": action is '%s'", button, select_state.c_str()); } else { - ESP_LOGE("standard_hw_relays", "No button action selected for button %" PRIu8, button); + ESP_LOGE(TAG, "No button action selected for button %" PRIu8, button); } - id: !extend dump_config then: - lambda: |- + static const char *TAG = "script.dump_config.standard.hw.relays"; + std::string gang_count_plural_suffix = id(gang_count) > 1 ? "s" : ""; // Relay's modes - ESP_LOGCONFIG("standard_hw_relays", "Relay%s mode%s:", - gang_count_plural_suffix.c_str(), - gang_count_plural_suffix.c_str()); - ESP_LOGCONFIG("standard_hw_relays", " Relay 1: %s", sl_relay_1_mode->has_state() - ? sl_relay_1_mode->state.c_str() - : "Unknown"); + ESP_LOGCONFIG(TAG, "Relay%s mode%s:", gang_count_plural_suffix.c_str(), gang_count_plural_suffix.c_str()); + ESP_LOGCONFIG(TAG, " Relay 1: %s", sl_relay_1_mode->has_state() ? + sl_relay_1_mode->state.c_str() : "Unknown"); if (id(gang_count) >= 2) - ESP_LOGCONFIG("standard_hw_relays", " Relay 2: %s", sl_relay_2_mode->has_state() - ? sl_relay_2_mode->state.c_str() - : "Unknown"); + ESP_LOGCONFIG(TAG, " Relay 2: %s", sl_relay_2_mode->has_state() ? + sl_relay_2_mode->state.c_str() : "Unknown"); if (id(gang_count) >= 3) - ESP_LOGCONFIG("standard_hw_relays", " Relay 3: %s", sl_relay_3_mode->has_state() - ? sl_relay_3_mode->state.c_str() - : "Unknown"); + ESP_LOGCONFIG(TAG, " Relay 3: %s", sl_relay_3_mode->has_state() ? + sl_relay_3_mode->state.c_str() : "Unknown"); if (id(gang_count) >= 4) - ESP_LOGCONFIG("standard_hw_relays", " Relay 4: %s", sl_relay_4_mode->has_state() - ? sl_relay_4_mode->state.c_str() - : "Unknown"); + ESP_LOGCONFIG(TAG, " Relay 4: %s", sl_relay_4_mode->has_state() ? + sl_relay_4_mode->state.c_str() : "Unknown"); // Relay's states - ESP_LOGCONFIG("standard_hw_relays", "Relay%s state%s:", - gang_count_plural_suffix.c_str(), - gang_count_plural_suffix.c_str()); - ESP_LOGCONFIG("standard_hw_relays", " Relay 1: %s", sw_relay_1->state ? "ON" : "OFF"); + ESP_LOGCONFIG(TAG, "Relay%s state%s:", gang_count_plural_suffix.c_str(), gang_count_plural_suffix.c_str()); + ESP_LOGCONFIG(TAG, " Relay 1: %s", sw_relay_1->state ? "ON" : "OFF"); if (id(gang_count) >= 2) - ESP_LOGCONFIG("standard_hw_relays", " Relay 2: %s", sw_relay_2->state ? "ON" : "OFF"); + ESP_LOGCONFIG(TAG, " Relay 2: %s", sw_relay_2->state ? "ON" : "OFF"); if (id(gang_count) >= 3) - ESP_LOGCONFIG("standard_hw_relays", " Relay 3: %s", sw_relay_3->state ? "ON" : "OFF"); + ESP_LOGCONFIG(TAG, " Relay 3: %s", sw_relay_3->state ? "ON" : "OFF"); if (id(gang_count) >= 4) - ESP_LOGCONFIG("standard_hw_relays", " Relay 4: %s", sw_relay_4->state ? "ON" : "OFF"); - ESP_LOGCONFIG("standard_hw_relays", "Expose Relay's LEDs to Home Assistant: %s", - YESNO(sw_expose_relays_leds_to_ha->state)); + ESP_LOGCONFIG(TAG, " Relay 4: %s", sw_relay_4->state ? "ON" : "OFF"); + ESP_LOGCONFIG(TAG, "Expose Relay's LEDs to Home Assistant: %s", YESNO(sw_expose_relays_leds_to_ha->state)); // Boot initialization statuses - ESP_LOGCONFIG("standard_hw_relays", "Boot initialization flags:"); - ESP_LOGCONFIG("standard_hw_relays", " Overall: %s", YESNO(id(boot_initialization_relays))); - ESP_LOGCONFIG("standard_hw_relays", " Groups: %s", - YESNO(id(boot_initialization_relays_group_assignments))); - ESP_LOGCONFIG("standard_hw_relays", " Light modes: %s", YESNO(id(boot_initialization_relays_light_modes))); - ESP_LOGCONFIG("standard_hw_relays", " Relay modes: %s", YESNO(id(boot_initialization_relays_relay_modes))); - ESP_LOGCONFIG("standard_hw_relays", " Status LEDs: %s", YESNO(id(boot_initialization_relays_relay_leds))); + ESP_LOGCONFIG(TAG, "Boot initialization flags:"); + ESP_LOGCONFIG(TAG, " Overall: %s", YESNO(id(boot_initialization_relays))); + ESP_LOGCONFIG(TAG, " Groups: %s", YESNO(id(boot_initialization_relays_group_assignments))); + ESP_LOGCONFIG(TAG, " Light modes: %s", YESNO(id(boot_initialization_relays_light_modes))); + ESP_LOGCONFIG(TAG, " Relay modes: %s", YESNO(id(boot_initialization_relays_relay_modes))); + ESP_LOGCONFIG(TAG, " Status LEDs: %s", YESNO(id(boot_initialization_relays_relay_leds))); // Substitutions - ESP_LOGCONFIG("standard_hw_relays", "Restore modes:"); - ESP_LOGCONFIG("standard_hw_relays", " Relays: ${RELAY_RESTORE_MODE}"); - ESP_LOGCONFIG("standard_hw_relays", " Status LEDs: ${LIGHT_RELAYS_RESTORE_MODE}"); + ESP_LOGCONFIG(TAG, "Restore modes:"); + ESP_LOGCONFIG(TAG, " Relays: ${RELAY_RESTORE_MODE}"); + ESP_LOGCONFIG(TAG, " Status LEDs: ${LIGHT_RELAYS_RESTORE_MODE}"); - id: !extend dump_config_list_packages then: @@ -769,8 +765,10 @@ script: then: - script.wait: boot_initialize_relays - lambda: |- + static const char *TAG = "script.show_relay_status.standard.hw.relays"; + if (not id(boot_initialization_relays)) { - ESP_LOGW("standard_hw_relays", "Relays not initialized yet"); + ESP_LOGW(TAG, "Relays not initialized yet"); if (not boot_initialize_relays->is_running()) boot_initialize_relays->execute(); return; @@ -813,8 +811,8 @@ script: break; } - ESP_LOGV("standard_hw_relays", "Relay %" PRIu8 " is %s", i + 1, relay_state ? "ON" : "OFF"); - ESP_LOGV("standard_hw_relays", "Light Mode for Relay %" PRIu8 ": %s", i + 1, light_mode.c_str()); + ESP_LOGV(TAG, "Relay %" PRIu8 " is %s", i + 1, relay_state ? "ON" : "OFF"); + ESP_LOGV(TAG, "Light Mode for Relay %" PRIu8 ": %s", i + 1, light_mode.c_str()); // Use light_set_state for light updates if (relay_state) { diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_speaker.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_speaker.yaml index e84a8a1..ceee20f 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_speaker.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_speaker.yaml @@ -29,9 +29,11 @@ api: tone: string then: - lambda: |- + static const char *TAG = "api.actions.rtttl_play.standard.hw.speaker"; + rtttl_speaker->stop(); if (speaker_volume->state > 0) { - ESP_LOGI("standard_speaker", "Play tone: '%s'", tone.c_str()); + ESP_LOGI(TAG, "Play tone: '%s'", tone.c_str()); rtttl_speaker->play(tone.c_str()); } diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_vibration.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_vibration.yaml index bf7205c..bb55b43 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_vibration.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_vibration.yaml @@ -61,17 +61,19 @@ script: - id: !extend dump_config then: - lambda: |- + static const char *TAG = "script.dump_config.standard.hw.vibration"; + // Configuration - ESP_LOGCONFIG("standard_hw_vibration", "Touch - Vibration feedback: %s", + ESP_LOGCONFIG(TAG, "Touch - Vibration feedback: %s", sl_touch_vibration_feedback->state.c_str()); - ESP_LOGCONFIG("standard_hw_vibration", "Vibrate duration: %.0fms", nr_vibrating_duration->state); - ESP_LOGCONFIG("standard_hw_vibration", "Vibrate max duration: ${vibration_max_duration}"); + ESP_LOGCONFIG(TAG, "Vibrate duration: %.0fms", nr_vibrating_duration->state); + ESP_LOGCONFIG(TAG, "Vibrate max duration: ${vibration_max_duration}"); // State if (bs_vibrating->state) - ESP_LOGW("standard_hw_vibration", "Vibrating now: YES"); + ESP_LOGW(TAG, "Vibrating now: YES"); else - ESP_LOGCONFIG("standard_hw_vibration", "Vibrating now: No"); + ESP_LOGCONFIG(TAG, "Vibrating now: No"); - id: !extend dump_config_list_packages then: diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_media_player.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_media_player.yaml index 203ee28..95bebb6 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_media_player.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_media_player.yaml @@ -50,11 +50,13 @@ media_player: on_state: then: - lambda: |- + static const char *TAG = "media_player.on_state.standard.hw.media_player"; + // Check if volume has changed and update number entity id(persistent_media_player_volume) = static_cast(mp_media_player->volume * 100.0f); if (id(persistent_media_player_volume) != id(last_media_player_volume)) { id(last_media_player_volume) = id(persistent_media_player_volume); - ESP_LOGD("media_player_volume", "Media player volume changed to %" PRIu8 "%%, updating number entity", id(persistent_media_player_volume)); + ESP_LOGD(TAG, "Media player volume changed to %" PRIu8 "%%, updating number entity", id(persistent_media_player_volume)); } script: @@ -65,10 +67,12 @@ script: - id: !extend dump_config then: - lambda: |- + static const char *TAG = "script.boot_sequence.standard.hw.media_player"; + // Volume sync configuration - ESP_LOGCONFIG("standard_media_player", "Media Player Volume Sync:"); - ESP_LOGCONFIG("standard_media_player", " Persistent volume: %" PRIu8 "%", id(persistent_media_player_volume)); - ESP_LOGCONFIG("standard_media_player", " Current volume: %.1f%%", mp_media_player->volume * 100.0f); + ESP_LOGCONFIG(TAG, "Media Player Volume Sync:"); + ESP_LOGCONFIG(TAG, " Persistent volume: %" PRIu8 "%", id(persistent_media_player_volume)); + ESP_LOGCONFIG(TAG, " Current volume: %.1f%%", mp_media_player->volume * 100.0f); - id: !extend dump_config_list_packages then: @@ -93,8 +97,10 @@ script: id: mp_media_player volume: !lambda return id(persistent_media_player_volume) / 100.0f; - lambda: |- + static const char *TAG = "script.sync_initial_volume.standard.hw.media_player"; + id(last_media_player_volume) = id(persistent_media_player_volume); - ESP_LOGI("media_player_volume", "Initial volume sync: %.1f%% (restored from persistence)", id(persistent_media_player_volume)); + ESP_LOGI(TAG, "Initial volume sync: %.1f%% (restored from persistence)", id(persistent_media_player_volume)); speaker: - platform: mixer From c1f1b5a7e7e40748dcfe96b035aa55baf56b0326 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Tue, 5 Aug 2025 09:01:36 +0200 Subject: [PATCH 05/50] Standardize log tag - hw leds --- ...TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml | 68 +++++++++---------- 1 file changed, 33 insertions(+), 35 deletions(-) diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml index 8af7ff7..056f6e8 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml @@ -497,44 +497,46 @@ script: - id: !extend dump_config then: - lambda: |- + static const char *TAG = "script.dump_config.core.hw.leds"; + std::string gang_count_plural_suffix = id(gang_count) > 1 ? "s" : ""; // Relay's LEDs modes - ESP_LOGCONFIG("core_hw_leds", "Relay%s LEDs mode%s:", + ESP_LOGCONFIG(TAG, "Relay%s LEDs mode%s:", gang_count_plural_suffix.c_str(), gang_count_plural_suffix.c_str()); - ESP_LOGCONFIG("core_hw_leds", " Relay 1: %s", id(is_us_model) - ? sl_relay_1_light_mode_us->state.c_str() - : sl_relay_1_light_mode_eu->state.c_str()); + ESP_LOGCONFIG(TAG, " Relay 1: %s", id(is_us_model) ? + sl_relay_1_light_mode_us->state.c_str() : + sl_relay_1_light_mode_eu->state.c_str()); if (id(gang_count) >= 2) - ESP_LOGCONFIG("core_hw_leds", " Relay 2: %s", id(is_us_model) - ? sl_relay_2_light_mode_us->state.c_str() - : sl_relay_2_light_mode_eu->state.c_str()); + ESP_LOGCONFIG(TAG, " Relay 2: %s", id(is_us_model) ? + sl_relay_2_light_mode_us->state.c_str() : + sl_relay_2_light_mode_eu->state.c_str()); if (id(gang_count) >= 3) - ESP_LOGCONFIG("core_hw_leds", " Relay 3: %s", id(is_us_model) - ? sl_relay_3_light_mode_us->state.c_str() - : sl_relay_3_light_mode_eu->state.c_str()); + ESP_LOGCONFIG(TAG, " Relay 3: %s", id(is_us_model) ? + sl_relay_3_light_mode_us->state.c_str() : + sl_relay_3_light_mode_eu->state.c_str()); if (id(gang_count) >= 4) - ESP_LOGCONFIG("core_hw_leds", " Relay 4: %s", id(is_us_model) - ? sl_relay_4_light_mode_us->state.c_str() - : sl_relay_4_light_mode_eu->state.c_str()); + ESP_LOGCONFIG(TAG, " Relay 4: %s", id(is_us_model) ? + sl_relay_4_light_mode_us->state.c_str() : + sl_relay_4_light_mode_eu->state.c_str()); // Transition times - ESP_LOGCONFIG("core_hw_leds", "Transition times:"); - ESP_LOGCONFIG("core_hw_leds", " Default: ${default_transition_length}"); + ESP_LOGCONFIG(TAG, "Transition times:"); + ESP_LOGCONFIG(TAG, " Default: ${default_transition_length}"); if (${LIGHT_TRANSITION_TURN_ON} > 0) - ESP_LOGCONFIG("core_hw_leds", " Turning on: ${LIGHT_TRANSITION_TURN_ON}ms"); + ESP_LOGCONFIG(TAG, " Turning on: ${LIGHT_TRANSITION_TURN_ON}ms"); else - ESP_LOGCONFIG("core_hw_leds", " Turning on: Disabled"); + ESP_LOGCONFIG(TAG, " Turning on: Disabled"); if (${LIGHT_TRANSITION_TURN_OFF} > 0) - ESP_LOGCONFIG("core_hw_leds", " Turning off: ${LIGHT_TRANSITION_TURN_OFF}ms"); + ESP_LOGCONFIG(TAG, " Turning off: ${LIGHT_TRANSITION_TURN_OFF}ms"); else - ESP_LOGCONFIG("core_hw_leds", " Turning off: Disabled"); + ESP_LOGCONFIG(TAG, " Turning off: Disabled"); // Restore modes - ESP_LOGCONFIG("core_hw_leds", "LED restore modes:"); - ESP_LOGCONFIG("core_hw_leds", " Light - All: ${LIGHT_FULL_RESTORE_MODE}"); - ESP_LOGCONFIG("core_hw_leds", " Sides: ${LIGHT_SIDES_RESTORE_MODE}"); - ESP_LOGCONFIG("core_hw_leds", " Individual: ${LIGHT_INDIVIDUAL_RESTORE_MODE}"); + ESP_LOGCONFIG(TAG, "LED restore modes:"); + ESP_LOGCONFIG(TAG, " Light - All: ${LIGHT_FULL_RESTORE_MODE}"); + ESP_LOGCONFIG(TAG, " Sides: ${LIGHT_SIDES_RESTORE_MODE}"); + ESP_LOGCONFIG(TAG, " Individual: ${LIGHT_INDIVIDUAL_RESTORE_MODE}"); - id: !extend dump_config_list_packages then: @@ -556,6 +558,8 @@ script: state: bool then: - lambda: |- + static const char *TAG = "script.light_set_state.core.hw.leds"; + static const uint32_t LIGHT_TRANSITION_TURN_ON = ${LIGHT_TRANSITION_TURN_ON}; static const uint32_t LIGHT_TRANSITION_TURN_OFF = ${LIGHT_TRANSITION_TURN_OFF}; static const uint8_t LIGHT_BRIGHTNESS_TURN_ON = ${LIGHT_BRIGHTNESS_TURN_ON}; @@ -570,13 +574,11 @@ script: switch (light_group) { case 1: if (light_index >= id(gb_lights_1).size()) { - ESP_LOGE("core_hw_leds", "Invalid set with %s light for Relay %" PRIu8, - light_mode_group_1.c_str(), light_index+1); + ESP_LOGE(TAG, "Invalid set with %s light for Relay %" PRIu8, light_mode_group_1.c_str(), light_index+1); return; } if (state and !id(gb_lights_1)[light_index]->current_values.is_on()) { - ESP_LOGI("core_hw_leds", "Turn-on %s light for Relay %" PRIu8, - light_mode_group_1.c_str(), light_index+1); + ESP_LOGI(TAG, "Turn-on %s light for Relay %" PRIu8, light_mode_group_1.c_str(), light_index+1); auto call = id(gb_lights_1)[light_index]->turn_on(); if (LIGHT_TRANSITION_TURN_ON > 0) call.set_transition_length(LIGHT_TRANSITION_TURN_ON); // in ms @@ -584,8 +586,7 @@ script: call.set_brightness(LIGHT_BRIGHTNESS_TURN_ON / 100.0f); call.perform(); } else if (!state and id(gb_lights_1)[light_index]->current_values.is_on()) { - ESP_LOGI("core_hw_leds", "Turn-off %s light for Relay %" PRIu8, - light_mode_group_1.c_str(), light_index+1); + ESP_LOGI(TAG, "Turn-off %s light for Relay %" PRIu8, light_mode_group_1.c_str(), light_index+1); auto call = id(gb_lights_1)[light_index]->turn_off(); if (LIGHT_TRANSITION_TURN_OFF > 0) call.set_transition_length(LIGHT_TRANSITION_TURN_OFF); // in ms @@ -594,13 +595,11 @@ script: break; case 2: if (light_index >= id(gb_lights_2).size()) { - ESP_LOGE("core_hw_leds", "Invalid set with %s light for Relay %" PRIu8, - light_mode_group_2.c_str(), light_index+1); + ESP_LOGE(TAG, "Invalid set with %s light for Relay %" PRIu8, light_mode_group_2.c_str(), light_index+1); return; } if (state and !id(gb_lights_2)[light_index]->current_values.is_on()) { - ESP_LOGI("core_hw_leds", "Turn-on %s light for Relay %" PRIu8, - light_mode_group_2.c_str(), light_index+1); + ESP_LOGI(TAG, "Turn-on %s light for Relay %" PRIu8, light_mode_group_2.c_str(), light_index+1); auto call = id(gb_lights_2)[light_index]->turn_on(); if (LIGHT_TRANSITION_TURN_ON > 0) call.set_transition_length(LIGHT_TRANSITION_TURN_ON); // in ms @@ -608,8 +607,7 @@ script: call.set_brightness(LIGHT_BRIGHTNESS_TURN_ON / 100.0f); call.perform(); } else if (!state and id(gb_lights_2)[light_index]->current_values.is_on()) { - ESP_LOGI("core_hw_leds", "Turn-off %s light for Relay %" PRIu8, - light_mode_group_2.c_str(), light_index+1); + ESP_LOGI(TAG, "Turn-off %s light for Relay %" PRIu8, light_mode_group_2.c_str(), light_index+1); auto call = id(gb_lights_2)[light_index]->turn_off(); if (LIGHT_TRANSITION_TURN_OFF > 0) call.set_transition_length(LIGHT_TRANSITION_TURN_OFF); // in ms From 07de172188ff92bb0f6fc27bd70a06203c2058fc Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Tue, 5 Aug 2025 10:57:08 +0200 Subject: [PATCH 06/50] Claude's changes for CodeRabbit suggestion --- .github/workflows/versioning.yml | 57 +++++++++++++++----------------- 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/.github/workflows/versioning.yml b/.github/workflows/versioning.yml index d3116b7..17ba8dc 100644 --- a/.github/workflows/versioning.yml +++ b/.github/workflows/versioning.yml @@ -48,36 +48,36 @@ jobs: with: script: | try { - // Get the commit that triggered this workflow - const commit = await github.rest.repos.getCommit({ + // Use GitHub's reliable API to find PRs associated with this commit + const { data: associatedPRs } = await github.rest.repos.listPullRequestsAssociatedWithCommit({ owner: context.repo.owner, repo: context.repo.repo, - ref: context.sha + commit_sha: context.sha }); - // Look for PR number in commit message (GitHub merge commits include this) - const commitMessage = commit.data.commit.message; - const prMatch = commitMessage.match(/Merge pull request #(\d+)/); - - if (prMatch) { - const prNumber = parseInt(prMatch[1]); - const pr = await github.rest.pulls.get({ + if (associatedPRs.length > 0) { + // Get the most recent merged PR + const pr = associatedPRs.find(pr => pr.state === 'closed' && pr.merged_at) || associatedPRs[0]; + + return { + title: pr.title, + body: pr.body || 'No description provided', + number: pr.number, + found: true + }; + } else { + // Fallback for direct pushes or when no PR is found + const commit = await github.rest.repos.getCommit({ owner: context.repo.owner, repo: context.repo.repo, - pull_number: prNumber + ref: context.sha }); - return { - title: pr.data.title, - body: pr.data.body || 'No description provided', - number: prNumber - }; - } else { - // Fallback for direct pushes return { title: 'Direct push to main', - body: commitMessage, - number: null + body: commit.data.commit.message, + number: null, + found: false }; } } catch (error) { @@ -85,7 +85,8 @@ jobs: return { title: 'Version update', body: 'Automated version bump', - number: null + number: null, + found: false }; } @@ -99,19 +100,15 @@ jobs: run: | NEW_VERSION=$(cat ./versioning/VERSION) - if [ "${{ github.event_name }}" == "push" ]; then - # Use PR information from previous step - PR_TITLE='${{ fromJson(steps.pr_info.outputs.result).title }}' - PR_BODY='${{ fromJson(steps.pr_info.outputs.result).body }}' - - # Create formatted tag message + if [ "${{ github.event_name }}" == "push" ] && [ "${{ steps.pr_info.outcome }}" == "success" ]; then + # Use PR information directly in heredoc to avoid quoting issues cat > tag_message.txt << EOF - # v${NEW_VERSION} - ${PR_TITLE} + # v${NEW_VERSION} - ${{ fromJson(steps.pr_info.outputs.result).title }} - ${PR_BODY} + ${{ fromJson(steps.pr_info.outputs.result).body }} EOF else - # Manual dispatch - simpler message + # Manual dispatch or PR info unavailable - simpler message cat > tag_message.txt << EOF # v${NEW_VERSION} - Manual tag update From 243a18bdf28f53d019e017c6d69728efe4d84c40 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Tue, 5 Aug 2025 11:08:16 +0200 Subject: [PATCH 07/50] Fix 4-bit mask --- ...TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml index a64a136..804f302 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml @@ -362,8 +362,8 @@ select: on_value: then: - lambda: |- - id(button_actions_packed) = (id(button_actions_packed) & ~(0x07 << 0)) | - ((id(sl_button_1_action).active_index().value_or(1) & 0x07) << 0); + id(button_actions_packed) = (id(button_actions_packed) & ~(0x0F << 0)) | + ((id(sl_button_1_action).active_index().value_or(1) & 0x0F) << 0); - id: sl_button_2_action name: Button 2 action @@ -377,8 +377,8 @@ select: on_value: then: - lambda: |- - id(button_actions_packed) = (id(button_actions_packed) & ~(0x07 << 4)) | - ((id(sl_button_2_action).active_index().value_or(1) & 0x07) << 4); + id(button_actions_packed) = (id(button_actions_packed) & ~(0x0F << 4)) | + ((id(sl_button_2_action).active_index().value_or(1) & 0x0F) << 4); - id: sl_button_3_action name: Button 3 action @@ -392,8 +392,8 @@ select: on_value: then: - lambda: |- - id(button_actions_packed) = (id(button_actions_packed) & ~(0x07 << 8)) | - ((id(sl_button_3_action).active_index().value_or(1) & 0x07) << 8); + id(button_actions_packed) = (id(button_actions_packed) & ~(0x0F << 8)) | + ((id(sl_button_3_action).active_index().value_or(1) & 0x0F) << 8); - id: sl_button_4_action name: Button 4 action @@ -407,6 +407,6 @@ select: on_value: then: - lambda: |- - id(button_actions_packed) = (id(button_actions_packed) & ~(0x07 << 12)) | - ((id(sl_button_4_action).active_index().value_or(1) & 0x07) << 12); + id(button_actions_packed) = (id(button_actions_packed) & ~(0x0F << 12)) | + ((id(sl_button_4_action).active_index().value_or(1) & 0x0F) << 12); ... From 250b22562d080399d19e555f9df9be529a1972f5 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Tue, 5 Aug 2025 11:36:09 +0200 Subject: [PATCH 08/50] Simplifies tags It should be limited to 15ish chars --- .../TX-Ultimate-Easy-ESPHome_core_common.yaml | 46 +++++------ ...Ultimate-Easy-ESPHome_core_hw_buttons.yaml | 37 ++++----- ...TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml | 48 ++++++----- ...X-Ultimate-Easy-ESPHome_core_hw_touch.yaml | 44 +++++----- ...timate-Easy-ESPHome_standard_hw_audio.yaml | 2 + ...imate-Easy-ESPHome_standard_hw_relays.yaml | 80 +++++++++---------- ...mate-Easy-ESPHome_standard_hw_speaker.yaml | 6 +- ...te-Easy-ESPHome_standard_hw_vibration.yaml | 14 ++-- ...te-Easy-ESPHome_standard_media_player.yaml | 22 ++--- 9 files changed, 141 insertions(+), 158 deletions(-) diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core_common.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core_common.yaml index d1c85d8..09e699e 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core_common.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core_common.yaml @@ -29,6 +29,8 @@ substitutions: DUMP_CONFIG_CALLER_DELAY: 10s # Delay to dump config after requested + TAG_CORE_COMMON: core.common + api: id: api_server on_client_connected: @@ -47,12 +49,10 @@ binary_sensor: on_state: then: - lambda: |- - static const char *TAG = "binary_sensor.bs_pending_restart.core.common"; - if (x) - ESP_LOGW(TAG, "Pending restart: YES"); + ESP_LOGW("${TAG_CORE_COMMON}", "Pending restart: YES"); else - ESP_LOGD(TAG, "Pending restart: No"); + ESP_LOGD("${TAG_CORE_COMMON}", "Pending restart: No"); - script.execute: dump_config_caller button: @@ -152,12 +152,10 @@ script: - lambda: return sl_tx_model_format->active_index().has_value(); - lambda: return sl_tx_model_gang->active_index().has_value(); - lambda: |- - static const char *TAG = "script.boot_initialize.core.common"; - id(is_us_model) = (sl_tx_model_format->state == "${TX_MODEL_FORMAT_US_TEXT}"); id(gang_count) = sl_tx_model_gang->active_index().value() + 1; if (id(gang_count) < 1 || id(gang_count) > 4) { - ESP_LOGE(TAG, "Invalid number of gangs: %" PRIu8, id(gang_count)); + ESP_LOGE("${TAG_CORE_COMMON}", "Invalid number of gangs: %" PRIu8, id(gang_count)); } - id: boot_sequence @@ -173,40 +171,38 @@ script: then: # Extended by all modules - lambda: |- - static const char *TAG = "script.dump_config.core.common"; - // Device identification - ESP_LOGCONFIG(TAG, "Device friendly name: ${friendly_name}"); - ESP_LOGCONFIG(TAG, "Device name: ${name}"); - ESP_LOGCONFIG(TAG, "Device name (HA): %s", tx_device_name->state.c_str()); - ESP_LOGCONFIG(TAG, "Device hostname: %s", App.get_name().c_str()); + ESP_LOGCONFIG("${TAG_CORE_COMMON}", "Device friendly name: ${friendly_name}"); + ESP_LOGCONFIG("${TAG_CORE_COMMON}", "Device name: ${name}"); + ESP_LOGCONFIG("${TAG_CORE_COMMON}", "Device name (HA): %s", tx_device_name->state.c_str()); + ESP_LOGCONFIG("${TAG_CORE_COMMON}", "Device hostname: %s", App.get_name().c_str()); dump_config_versions->execute(); // Framework detection #ifdef USE_ARDUINO - ESP_LOGCONFIG(TAG, "Framework: Arduino"); + ESP_LOGCONFIG("${TAG_CORE_COMMON}", "Framework: Arduino"); #elif defined(USE_ESP_IDF) - ESP_LOGCONFIG(TAG, "Framework: ESP-IDF"); + ESP_LOGCONFIG("${TAG_CORE_COMMON}", "Framework: ESP-IDF"); #else - ESP_LOGW(TAG, "Framework: UNKNOWN"); + ESP_LOGW("${TAG_CORE_COMMON}", "Framework: UNKNOWN"); #endif // Model configuration - ESP_LOGCONFIG(TAG, "Model format (selected): %s", + ESP_LOGCONFIG("${TAG_CORE_COMMON}", "Model format (selected): %s", sl_tx_model_format->state.c_str()); - ESP_LOGCONFIG(TAG, "Model format (detected): %s", + ESP_LOGCONFIG("${TAG_CORE_COMMON}", "Model format (detected): %s", id(is_us_model) ? "US" : "EU"); - ESP_LOGCONFIG(TAG, "Gangs (selected): %s", + ESP_LOGCONFIG("${TAG_CORE_COMMON}", "Gangs (selected): %s", sl_tx_model_gang->state.c_str()); - ESP_LOGCONFIG(TAG, "Gangs (detected): %" PRIu8 "-Gang%s", + ESP_LOGCONFIG("${TAG_CORE_COMMON}", "Gangs (detected): %" PRIu8 "-Gang%s", id(gang_count), id(gang_count) > 1 ? "s" : ""); // System state if (bs_pending_restart->state) - ESP_LOGW(TAG, "Pending restart: YES"); + ESP_LOGW("${TAG_CORE_COMMON}", "Pending restart: YES"); else - ESP_LOGCONFIG(TAG, "Pending restart: No"); + ESP_LOGCONFIG("${TAG_CORE_COMMON}", "Pending restart: No"); - id: dump_config_caller mode: restart @@ -231,10 +227,10 @@ script: then: - lambda: |- // Version information - ESP_LOGCONFIG("core", "TX Ultimate firmware version: ${version}"); + ESP_LOGCONFIG("${TAG_CORE_COMMON}", "TX Ultimate firmware version: ${version}"); // ESPHome builder information - ESP_LOGCONFIG("core", "ESPHome builder: " ESPHOME_VERSION); - ESP_LOGCONFIG("core", "ESPHome build timestamp: %s", App.get_compilation_time().c_str()); + ESP_LOGCONFIG("${TAG_CORE_COMMON}", "ESPHome builder: " ESPHOME_VERSION); + ESP_LOGCONFIG("${TAG_CORE_COMMON}", "ESPHome build timestamp: %s", App.get_compilation_time().c_str()); - id: publish_device_info mode: restart diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml index 804f302..c70670d 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml @@ -36,6 +36,8 @@ substitutions: BUTTON_3_ID: '3' BUTTON_4_ID: '4' + TAG_CORE_HW_BUTTONS: core.hw.buttons + binary_sensor: - &binary_sensor_button_base id: bs_button_2 @@ -121,8 +123,6 @@ script: count: uint8_t then: - lambda: |- - static const char *TAG = "script.button_action.core.hw.buttons"; - // Send event to Home Assistant esphome::api::CustomAPIDevice ha_event; ha_event.fire_homeassistant_event("${EVENT_NAME}", { @@ -135,7 +135,7 @@ script: {"count", std::to_string(count)}, {"position", std::to_string(id(button_press_position))} }); - ESP_LOGI(TAG, "Button %" PRIu8 " action: '%s'", button, action.c_str()); + ESP_LOGI("${TAG_CORE_HW_BUTTONS}", "Button %" PRIu8 " action: '%s'", button, action.c_str()); if (action == "click") { switch (button) { case ${BUTTON_1_ID}: @@ -219,24 +219,22 @@ script: - id: !extend dump_config then: - lambda: |- - static const char *TAG = "script.dump_config.core.hw.buttons"; - std::string gang_count_plural_suffix = id(gang_count) > 1 ? "s" : ""; // Button's actions - ESP_LOGCONFIG(TAG, "Button%s action%s:", + ESP_LOGCONFIG("${TAG_CORE_HW_BUTTONS}", "Button%s action%s:", gang_count_plural_suffix.c_str(), gang_count_plural_suffix.c_str()); - ESP_LOGCONFIG(TAG, " Relay 1: %s", sl_button_1_action->has_state() ? - sl_button_1_action->state.c_str() : "Unknown"); + ESP_LOGCONFIG("${TAG_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(TAG, " Relay 2: %s", sl_button_2_action->has_state() ? - sl_button_2_action->state.c_str() : "Unknown"); + ESP_LOGCONFIG("${TAG_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(TAG, " Relay 3: %s", sl_button_3_action->has_state() ? - sl_button_3_action->state.c_str() : "Unknown"); + ESP_LOGCONFIG("${TAG_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(TAG, " Relay 4: %s", sl_button_4_action->has_state() ? - sl_button_4_action->state.c_str() : "Unknown"); + ESP_LOGCONFIG("${TAG_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: @@ -311,27 +309,26 @@ script: - id: !extend touch_on_release then: - lambda: |- - static const char *TAG = "script.touch_on_release.core.hw.buttons"; - uint32_t current_time = millis(); buttons_release->execute(); if (id(button_press_start_time) > 0 and id(button_press_start_time) < current_time) { uint32_t press_duration = current_time - id(button_press_start_time); // Handle overflow (optional, since it's unlikely to happen here) - ESP_LOGI(TAG, "Button press duration: %" PRIu32 " ms", press_duration); + ESP_LOGI("${TAG_CORE_HW_BUTTONS}", "Button press duration: %" PRIu32 " ms", press_duration); if (press_duration < ${BUTTON_CLICK_MIN_LENGTH}) { - ESP_LOGW(TAG, "Ignoring button press (too short)"); + ESP_LOGW("${TAG_CORE_HW_BUTTONS}", "Ignoring button press (too short)"); } else if (press_duration >= ${BUTTON_CLICK_MIN_LENGTH} and press_duration <= ${BUTTON_CLICK_MAX_LENGTH}) { // Short/normal click button_click_event->execute(id(button_press_button), id(click_counter)); } else if (press_duration >= ${BUTTON_LONG_PRESS_DELAY} and press_duration <= ${BUTTON_PRESS_TIMEOUT}) { button_action->execute(id(button_press_button), "long_press", 1); } else if (press_duration > ${BUTTON_PRESS_TIMEOUT}) { // Timeout or invalid - ESP_LOGW(TAG, "Button press cancelled or timed out after ${BUTTON_PRESS_TIMEOUT} ms"); + ESP_LOGW("${TAG_CORE_HW_BUTTONS}", + "Button press cancelled or timed out after ${BUTTON_PRESS_TIMEOUT} ms"); } } else { - ESP_LOGW(TAG, "Press event timestamp not recorded yet"); + ESP_LOGW("${TAG_CORE_HW_BUTTONS}", "Press event timestamp not recorded yet"); } id(button_press_start_time) = 0; diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml index 056f6e8..90dddea 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml @@ -29,6 +29,8 @@ substitutions: LIGHT_SIDES_RESTORE_MODE: RESTORE_DEFAULT_OFF LIGHT_INDIVIDUAL_RESTORE_MODE: RESTORE_DEFAULT_OFF + TAG_CORE_HW_LEDS: core.hw.leds + esphome: platformio_options: build_flags: @@ -497,46 +499,44 @@ script: - id: !extend dump_config then: - lambda: |- - static const char *TAG = "script.dump_config.core.hw.leds"; - std::string gang_count_plural_suffix = id(gang_count) > 1 ? "s" : ""; // Relay's LEDs modes - ESP_LOGCONFIG(TAG, "Relay%s LEDs mode%s:", + ESP_LOGCONFIG("${TAG_CORE_HW_LEDS}", "Relay%s LEDs mode%s:", gang_count_plural_suffix.c_str(), gang_count_plural_suffix.c_str()); - ESP_LOGCONFIG(TAG, " Relay 1: %s", id(is_us_model) ? + ESP_LOGCONFIG("${TAG_CORE_HW_LEDS}", " Relay 1: %s", id(is_us_model) ? sl_relay_1_light_mode_us->state.c_str() : sl_relay_1_light_mode_eu->state.c_str()); if (id(gang_count) >= 2) - ESP_LOGCONFIG(TAG, " Relay 2: %s", id(is_us_model) ? + ESP_LOGCONFIG("${TAG_CORE_HW_LEDS}", " Relay 2: %s", id(is_us_model) ? sl_relay_2_light_mode_us->state.c_str() : sl_relay_2_light_mode_eu->state.c_str()); if (id(gang_count) >= 3) - ESP_LOGCONFIG(TAG, " Relay 3: %s", id(is_us_model) ? + ESP_LOGCONFIG("${TAG_CORE_HW_LEDS}", " Relay 3: %s", id(is_us_model) ? sl_relay_3_light_mode_us->state.c_str() : sl_relay_3_light_mode_eu->state.c_str()); if (id(gang_count) >= 4) - ESP_LOGCONFIG(TAG, " Relay 4: %s", id(is_us_model) ? + ESP_LOGCONFIG("${TAG_CORE_HW_LEDS}", " Relay 4: %s", id(is_us_model) ? sl_relay_4_light_mode_us->state.c_str() : sl_relay_4_light_mode_eu->state.c_str()); // Transition times - ESP_LOGCONFIG(TAG, "Transition times:"); - ESP_LOGCONFIG(TAG, " Default: ${default_transition_length}"); + ESP_LOGCONFIG("${TAG_CORE_HW_LEDS}", "Transition times:"); + ESP_LOGCONFIG("${TAG_CORE_HW_LEDS}", " Default: ${default_transition_length}"); if (${LIGHT_TRANSITION_TURN_ON} > 0) - ESP_LOGCONFIG(TAG, " Turning on: ${LIGHT_TRANSITION_TURN_ON}ms"); + ESP_LOGCONFIG("${TAG_CORE_HW_LEDS}", " Turning on: ${LIGHT_TRANSITION_TURN_ON}ms"); else - ESP_LOGCONFIG(TAG, " Turning on: Disabled"); + ESP_LOGCONFIG("${TAG_CORE_HW_LEDS}", " Turning on: Disabled"); if (${LIGHT_TRANSITION_TURN_OFF} > 0) - ESP_LOGCONFIG(TAG, " Turning off: ${LIGHT_TRANSITION_TURN_OFF}ms"); + ESP_LOGCONFIG("${TAG_CORE_HW_LEDS}", " Turning off: ${LIGHT_TRANSITION_TURN_OFF}ms"); else - ESP_LOGCONFIG(TAG, " Turning off: Disabled"); + ESP_LOGCONFIG("${TAG_CORE_HW_LEDS}", " Turning off: Disabled"); // Restore modes - ESP_LOGCONFIG(TAG, "LED restore modes:"); - ESP_LOGCONFIG(TAG, " Light - All: ${LIGHT_FULL_RESTORE_MODE}"); - ESP_LOGCONFIG(TAG, " Sides: ${LIGHT_SIDES_RESTORE_MODE}"); - ESP_LOGCONFIG(TAG, " Individual: ${LIGHT_INDIVIDUAL_RESTORE_MODE}"); + ESP_LOGCONFIG("${TAG_CORE_HW_LEDS}", "LED restore modes:"); + ESP_LOGCONFIG("${TAG_CORE_HW_LEDS}", " Light - All: ${LIGHT_FULL_RESTORE_MODE}"); + ESP_LOGCONFIG("${TAG_CORE_HW_LEDS}", " Sides: ${LIGHT_SIDES_RESTORE_MODE}"); + ESP_LOGCONFIG("${TAG_CORE_HW_LEDS}", " Individual: ${LIGHT_INDIVIDUAL_RESTORE_MODE}"); - id: !extend dump_config_list_packages then: @@ -558,8 +558,6 @@ script: state: bool then: - lambda: |- - static const char *TAG = "script.light_set_state.core.hw.leds"; - static const uint32_t LIGHT_TRANSITION_TURN_ON = ${LIGHT_TRANSITION_TURN_ON}; static const uint32_t LIGHT_TRANSITION_TURN_OFF = ${LIGHT_TRANSITION_TURN_OFF}; static const uint8_t LIGHT_BRIGHTNESS_TURN_ON = ${LIGHT_BRIGHTNESS_TURN_ON}; @@ -574,11 +572,11 @@ script: switch (light_group) { case 1: if (light_index >= id(gb_lights_1).size()) { - ESP_LOGE(TAG, "Invalid set with %s light for Relay %" PRIu8, light_mode_group_1.c_str(), light_index+1); + ESP_LOGE("${TAG_CORE_HW_LEDS}", "Invalid set with %s light for Relay %" PRIu8, light_mode_group_1.c_str(), light_index+1); return; } if (state and !id(gb_lights_1)[light_index]->current_values.is_on()) { - ESP_LOGI(TAG, "Turn-on %s light for Relay %" PRIu8, light_mode_group_1.c_str(), light_index+1); + ESP_LOGI("${TAG_CORE_HW_LEDS}", "Turn-on %s light for Relay %" PRIu8, light_mode_group_1.c_str(), light_index+1); auto call = id(gb_lights_1)[light_index]->turn_on(); if (LIGHT_TRANSITION_TURN_ON > 0) call.set_transition_length(LIGHT_TRANSITION_TURN_ON); // in ms @@ -586,7 +584,7 @@ script: call.set_brightness(LIGHT_BRIGHTNESS_TURN_ON / 100.0f); call.perform(); } else if (!state and id(gb_lights_1)[light_index]->current_values.is_on()) { - ESP_LOGI(TAG, "Turn-off %s light for Relay %" PRIu8, light_mode_group_1.c_str(), light_index+1); + ESP_LOGI("${TAG_CORE_HW_LEDS}", "Turn-off %s light for Relay %" PRIu8, light_mode_group_1.c_str(), light_index+1); auto call = id(gb_lights_1)[light_index]->turn_off(); if (LIGHT_TRANSITION_TURN_OFF > 0) call.set_transition_length(LIGHT_TRANSITION_TURN_OFF); // in ms @@ -595,11 +593,11 @@ script: break; case 2: if (light_index >= id(gb_lights_2).size()) { - ESP_LOGE(TAG, "Invalid set with %s light for Relay %" PRIu8, light_mode_group_2.c_str(), light_index+1); + ESP_LOGE("${TAG_CORE_HW_LEDS}", "Invalid set with %s light for Relay %" PRIu8, light_mode_group_2.c_str(), light_index+1); return; } if (state and !id(gb_lights_2)[light_index]->current_values.is_on()) { - ESP_LOGI(TAG, "Turn-on %s light for Relay %" PRIu8, light_mode_group_2.c_str(), light_index+1); + ESP_LOGI("${TAG_CORE_HW_LEDS}", "Turn-on %s light for Relay %" PRIu8, light_mode_group_2.c_str(), light_index+1); auto call = id(gb_lights_2)[light_index]->turn_on(); if (LIGHT_TRANSITION_TURN_ON > 0) call.set_transition_length(LIGHT_TRANSITION_TURN_ON); // in ms @@ -607,7 +605,7 @@ script: call.set_brightness(LIGHT_BRIGHTNESS_TURN_ON / 100.0f); call.perform(); } else if (!state and id(gb_lights_2)[light_index]->current_values.is_on()) { - ESP_LOGI(TAG, "Turn-off %s light for Relay %" PRIu8, light_mode_group_2.c_str(), light_index+1); + ESP_LOGI("${TAG_CORE_HW_LEDS}", "Turn-off %s light for Relay %" PRIu8, light_mode_group_2.c_str(), light_index+1); auto call = id(gb_lights_2)[light_index]->turn_off(); if (LIGHT_TRANSITION_TURN_OFF > 0) call.set_transition_length(LIGHT_TRANSITION_TURN_OFF); // in ms diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_touch.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_touch.yaml index 0e1377b..dfe940e 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_touch.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_touch.yaml @@ -14,6 +14,8 @@ substitutions: TOUCH_POSITION_MAX_VALUE: '10' # Maximum touch position value returned by the touch pad via uart + TAG_CORE_HW_TOUCH: core.hw.touch + binary_sensor: - &bs_button_event_template id: bs_button_event_template @@ -291,14 +293,12 @@ script: - id: !extend dump_config then: - lambda: |- - static const char *TAG = "script.dump_config.core.hw.touch"; - // Touch panel's state if (sw_touch_panel_power->state) - ESP_LOGCONFIG(TAG, "Touch panel - Power: On"); + ESP_LOGCONFIG("${TAG_CORE_HW_TOUCH}", "Touch panel - Power: On"); else - ESP_LOGW(TAG, "Touch panel - Power: OFF"); - ESP_LOGCONFIG(TAG, "Touch event duration: %.0fms", nr_touch_duration->state); + ESP_LOGW("${TAG_CORE_HW_TOUCH}", "Touch panel - Power: OFF"); + ESP_LOGCONFIG("${TAG_CORE_HW_TOUCH}", "Touch event duration: %.0fms", nr_touch_duration->state); - id: !extend dump_config_list_packages then: @@ -380,13 +380,11 @@ tx_ultimate_easy: on_long_touch_release: - lambda: |- - static const char *TAG = "tx_ultimate_easy.on_long_touch_release.core.hw.touch"; - const uint8_t touch_position = static_cast(touch.x); if (touch_position > ${TOUCH_POSITION_MAX_VALUE}) { // Check for valid range - ESP_LOGE(TAG, "Invalid long-touch position: %" PRIu8, touch_position); + ESP_LOGE("${TAG_CORE_HW_TOUCH}", "Invalid long-touch position: %" PRIu8, touch_position); } else { - ESP_LOGI(TAG, "Long-touch released at position %" PRIu8, touch_position); + ESP_LOGI("${TAG_CORE_HW_TOUCH}", "Long-touch released at position %" PRIu8, touch_position); } on_multi_touch_release: @@ -401,29 +399,24 @@ tx_ultimate_easy: action: release position: !lambda return std::to_string(touch.x); - lambda: |- - static const char *TAG = "tx_ultimate_easy.on_multi_touch_release.core.hw.touch"; - - ESP_LOGI(TAG, "Multi-touch released"); + ESP_LOGI("${TAG_CORE_HW_TOUCH}", "Multi-touch released"); touch_on_multi_touch_release->execute(); on_press: then: - lambda: |- - static const char *TAG = "tx_ultimate_easy.on_press.core.hw.touch"; - const uint8_t position = static_cast(touch.x); if (position > ${TOUCH_POSITION_MAX_VALUE}) { // Check for valid range - ESP_LOGE(TAG, "Invalid touch position: %" PRIu8, position); + ESP_LOGE("${TAG_CORE_HW_TOUCH}", "Invalid touch position: %" PRIu8, position); } else { - ESP_LOGI(TAG, "Pressed at position %" PRIu8, position); + ESP_LOGI("${TAG_CORE_HW_TOUCH}", "Pressed at position %" PRIu8, position); touch_on_press->execute(static_cast(touch.button), position); } on_release: then: - lambda: |- - static const char *TAG = "tx_ultimate_easy.on_release.core.hw.touch"; - ESP_LOGI(TAG, "Released"); + ESP_LOGI("${TAG_CORE_HW_TOUCH}", "Released"); - script.execute: touch_on_release on_swipe_left: @@ -447,8 +440,8 @@ tx_ultimate_easy: return "down"; position: !lambda return std::to_string(touch.x); - lambda: |- - static const char *TAG = "tx_ultimate_easy.on_swipe_left.core.hw.touch"; - ESP_LOGI(TAG, "Swipe %s", (sl_tx_model_format->state == "${TX_MODEL_FORMAT_EU_TEXT}") ? "left" : "down"); + ESP_LOGI("${TAG_CORE_HW_TOUCH}", "Swipe %s", + (sl_tx_model_format->state == "${TX_MODEL_FORMAT_EU_TEXT}") ? "left" : "down"); touch_swipe_left->execute(); on_swipe_right: @@ -472,16 +465,15 @@ tx_ultimate_easy: return "up"; position: !lambda return std::to_string(touch.x); - lambda: |- - static const char *TAG = "tx_ultimate_easy.on_swipe_right.core.hw.touch"; - ESP_LOGI(TAG, "Swipe %s", (sl_tx_model_format->state == "${TX_MODEL_FORMAT_EU_TEXT}") ? "right" : "up"); + ESP_LOGI("${TAG_CORE_HW_TOUCH}", "Swipe %s", + (sl_tx_model_format->state == "${TX_MODEL_FORMAT_EU_TEXT}") ? "right" : "up"); touch_swipe_right->execute(); on_touch_event: - lambda: |- - static const char *TAG = "tx_ultimate_easy.on_touch_event.core.hw.touch"; - ESP_LOGD(TAG, "Touch event:"); - ESP_LOGD(TAG, " State: %i (%s)", touch.state, touch.state_str.c_str()); - ESP_LOGD(TAG, " Position: %i", touch.x); + ESP_LOGD("${TAG_CORE_HW_TOUCH}", "Touch event:"); + ESP_LOGD("${TAG_CORE_HW_TOUCH}", " State: %i (%s)", touch.state, touch.state_str.c_str()); + ESP_LOGD("${TAG_CORE_HW_TOUCH}", " Position: %i", touch.x); uart: - id: uart_touch diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_audio.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_audio.yaml index 0894e3a..e53dc0f 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_audio.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_audio.yaml @@ -14,6 +14,8 @@ substitutions: SWITCH_AUDIO_AMPLIFIER_RESTORE_MODE: RESTORE_DEFAULT_ON + TAG_STD_HW_AUDIO: std.hw.audio + esphome: platformio_options: build_flags: diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml index 9e9839e..6d036f5 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml @@ -19,6 +19,8 @@ substitutions: RELAY_RESTORE_MODE: RESTORE_DEFAULT_OFF LIGHT_RELAYS_RESTORE_MODE: RESTORE_DEFAULT_OFF + TAG_STD_HW_RELAYS: std.hw.relays + esphome: platformio_options: build_flags: @@ -466,8 +468,6 @@ script: mode: restart then: - lambda: |- - static const char *TAG = "script.boot_initialize_relays_group_assignments.standard.hw.relays"; - // LED Group Assignment: // - For US model: gb_lights_1 = left side LEDs, gb_lights_2 = right side LEDs // - For EU model: gb_lights_1 = bottom LEDs, gb_lights_2 = top LEDs @@ -514,7 +514,7 @@ script: } break; default: - ESP_LOGE(TAG, "Invalid gang count: %" PRIu8, id(gang_count)); + ESP_LOGE("${TAG_STD_HW_RELAYS}", "Invalid gang count: %" PRIu8, id(gang_count)); break; } id(boot_initialization_relays_group_assignments) = true; @@ -637,8 +637,6 @@ script: - id: !extend button_click_event then: - lambda: |- - static const char *TAG = "script.button_click_event.standard.hw.relays"; - // Handle only single clicks if (click_count != 1) return; @@ -657,13 +655,13 @@ script: std::string select_state; bool toggle_relay = false; if (api_disconnected) { - ESP_LOGW(TAG, "Toggle relays:"); - ESP_LOGW(TAG, " API state: DISCONNECTED"); - ESP_LOGW(TAG, " Button: %" PRIu8, button); + ESP_LOGW("${TAG_STD_HW_RELAYS}", "Toggle relays:"); + ESP_LOGW("${TAG_STD_HW_RELAYS}", " API state: DISCONNECTED"); + ESP_LOGW("${TAG_STD_HW_RELAYS}", " Button: %" PRIu8, button); } else { - ESP_LOGVV(TAG, "Toggle relays:"); - ESP_LOGVV(TAG, " API state: Connected"); - ESP_LOGVV(TAG, " Button: %" PRIu8, button); + ESP_LOGVV("${TAG_STD_HW_RELAYS}", "Toggle relays:"); + ESP_LOGVV("${TAG_STD_HW_RELAYS}", " API state: Connected"); + ESP_LOGVV("${TAG_STD_HW_RELAYS}", " Button: %" PRIu8, button); } switch (button) { case ${BUTTON_1_ID}: @@ -696,57 +694,59 @@ script: break; } if (toggle_relay) { - ESP_LOGI(TAG, "Toggle relay %" PRIu8 ": (%s)", button, select_state.c_str()); + ESP_LOGI("${TAG_STD_HW_RELAYS}", "Toggle relay %" PRIu8 ": (%s)", button, select_state.c_str()); } else if (not select_state.empty()) { - ESP_LOGV(TAG, "Ignoring toggle for relay %" PRIu8 ": action is '%s'", + ESP_LOGV("${TAG_STD_HW_RELAYS}", "Ignoring toggle for relay %" PRIu8 ": action is '%s'", button, select_state.c_str()); } else { - ESP_LOGE(TAG, "No button action selected for button %" PRIu8, button); + ESP_LOGE("${TAG_STD_HW_RELAYS}", "No button action selected for button %" PRIu8, button); } - id: !extend dump_config then: - lambda: |- - static const char *TAG = "script.dump_config.standard.hw.relays"; - std::string gang_count_plural_suffix = id(gang_count) > 1 ? "s" : ""; // Relay's modes - ESP_LOGCONFIG(TAG, "Relay%s mode%s:", gang_count_plural_suffix.c_str(), gang_count_plural_suffix.c_str()); - ESP_LOGCONFIG(TAG, " Relay 1: %s", sl_relay_1_mode->has_state() ? + ESP_LOGCONFIG("${TAG_STD_HW_RELAYS}", "Relay%s mode%s:", gang_count_plural_suffix.c_str(), + gang_count_plural_suffix.c_str()); + ESP_LOGCONFIG("${TAG_STD_HW_RELAYS}", " Relay 1: %s", sl_relay_1_mode->has_state() ? sl_relay_1_mode->state.c_str() : "Unknown"); if (id(gang_count) >= 2) - ESP_LOGCONFIG(TAG, " Relay 2: %s", sl_relay_2_mode->has_state() ? + ESP_LOGCONFIG("${TAG_STD_HW_RELAYS}", " Relay 2: %s", sl_relay_2_mode->has_state() ? sl_relay_2_mode->state.c_str() : "Unknown"); if (id(gang_count) >= 3) - ESP_LOGCONFIG(TAG, " Relay 3: %s", sl_relay_3_mode->has_state() ? + ESP_LOGCONFIG("${TAG_STD_HW_RELAYS}", " Relay 3: %s", sl_relay_3_mode->has_state() ? sl_relay_3_mode->state.c_str() : "Unknown"); if (id(gang_count) >= 4) - ESP_LOGCONFIG(TAG, " Relay 4: %s", sl_relay_4_mode->has_state() ? + ESP_LOGCONFIG("${TAG_STD_HW_RELAYS}", " Relay 4: %s", sl_relay_4_mode->has_state() ? sl_relay_4_mode->state.c_str() : "Unknown"); // Relay's states - ESP_LOGCONFIG(TAG, "Relay%s state%s:", gang_count_plural_suffix.c_str(), gang_count_plural_suffix.c_str()); - ESP_LOGCONFIG(TAG, " Relay 1: %s", sw_relay_1->state ? "ON" : "OFF"); + ESP_LOGCONFIG("${TAG_STD_HW_RELAYS}", "Relay%s state%s:", gang_count_plural_suffix.c_str(), + gang_count_plural_suffix.c_str()); + ESP_LOGCONFIG("${TAG_STD_HW_RELAYS}", " Relay 1: %s", sw_relay_1->state ? "ON" : "OFF"); if (id(gang_count) >= 2) - ESP_LOGCONFIG(TAG, " Relay 2: %s", sw_relay_2->state ? "ON" : "OFF"); + ESP_LOGCONFIG("${TAG_STD_HW_RELAYS}", " Relay 2: %s", sw_relay_2->state ? "ON" : "OFF"); if (id(gang_count) >= 3) - ESP_LOGCONFIG(TAG, " Relay 3: %s", sw_relay_3->state ? "ON" : "OFF"); + ESP_LOGCONFIG("${TAG_STD_HW_RELAYS}", " Relay 3: %s", sw_relay_3->state ? "ON" : "OFF"); if (id(gang_count) >= 4) - ESP_LOGCONFIG(TAG, " Relay 4: %s", sw_relay_4->state ? "ON" : "OFF"); - ESP_LOGCONFIG(TAG, "Expose Relay's LEDs to Home Assistant: %s", YESNO(sw_expose_relays_leds_to_ha->state)); + ESP_LOGCONFIG("${TAG_STD_HW_RELAYS}", " Relay 4: %s", sw_relay_4->state ? "ON" : "OFF"); + ESP_LOGCONFIG("${TAG_STD_HW_RELAYS}", "Expose Relay's LEDs to Home Assistant: %s", + YESNO(sw_expose_relays_leds_to_ha->state)); // Boot initialization statuses - ESP_LOGCONFIG(TAG, "Boot initialization flags:"); - ESP_LOGCONFIG(TAG, " Overall: %s", YESNO(id(boot_initialization_relays))); - ESP_LOGCONFIG(TAG, " Groups: %s", YESNO(id(boot_initialization_relays_group_assignments))); - ESP_LOGCONFIG(TAG, " Light modes: %s", YESNO(id(boot_initialization_relays_light_modes))); - ESP_LOGCONFIG(TAG, " Relay modes: %s", YESNO(id(boot_initialization_relays_relay_modes))); - ESP_LOGCONFIG(TAG, " Status LEDs: %s", YESNO(id(boot_initialization_relays_relay_leds))); + ESP_LOGCONFIG("${TAG_STD_HW_RELAYS}", "Boot initialization flags:"); + ESP_LOGCONFIG("${TAG_STD_HW_RELAYS}", " Overall: %s", YESNO(id(boot_initialization_relays))); + ESP_LOGCONFIG("${TAG_STD_HW_RELAYS}", " Groups: %s", + YESNO(id(boot_initialization_relays_group_assignments))); + ESP_LOGCONFIG("${TAG_STD_HW_RELAYS}", " Light modes: %s", YESNO(id(boot_initialization_relays_light_modes))); + ESP_LOGCONFIG("${TAG_STD_HW_RELAYS}", " Relay modes: %s", YESNO(id(boot_initialization_relays_relay_modes))); + ESP_LOGCONFIG("${TAG_STD_HW_RELAYS}", " Status LEDs: %s", YESNO(id(boot_initialization_relays_relay_leds))); // Substitutions - ESP_LOGCONFIG(TAG, "Restore modes:"); - ESP_LOGCONFIG(TAG, " Relays: ${RELAY_RESTORE_MODE}"); - ESP_LOGCONFIG(TAG, " Status LEDs: ${LIGHT_RELAYS_RESTORE_MODE}"); + ESP_LOGCONFIG("${TAG_STD_HW_RELAYS}", "Restore modes:"); + ESP_LOGCONFIG("${TAG_STD_HW_RELAYS}", " Relays: ${RELAY_RESTORE_MODE}"); + ESP_LOGCONFIG("${TAG_STD_HW_RELAYS}", " Status LEDs: ${LIGHT_RELAYS_RESTORE_MODE}"); - id: !extend dump_config_list_packages then: @@ -765,10 +765,8 @@ script: then: - script.wait: boot_initialize_relays - lambda: |- - static const char *TAG = "script.show_relay_status.standard.hw.relays"; - if (not id(boot_initialization_relays)) { - ESP_LOGW(TAG, "Relays not initialized yet"); + ESP_LOGW("${TAG_STD_HW_RELAYS}", "Relays not initialized yet"); if (not boot_initialize_relays->is_running()) boot_initialize_relays->execute(); return; @@ -811,8 +809,8 @@ script: break; } - ESP_LOGV(TAG, "Relay %" PRIu8 " is %s", i + 1, relay_state ? "ON" : "OFF"); - ESP_LOGV(TAG, "Light Mode for Relay %" PRIu8 ": %s", i + 1, light_mode.c_str()); + ESP_LOGV("${TAG_STD_HW_RELAYS}", "Relay %" PRIu8 " is %s", i + 1, relay_state ? "ON" : "OFF"); + ESP_LOGV("${TAG_STD_HW_RELAYS}", "Light Mode for Relay %" PRIu8 ": %s", i + 1, light_mode.c_str()); // Use light_set_state for light updates if (relay_state) { diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_speaker.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_speaker.yaml index ceee20f..86a19e2 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_speaker.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_speaker.yaml @@ -16,6 +16,8 @@ substitutions: # d=32 (32nd note), o=5 (octave 5), b=100 (tempo 100) tone_volume_change: "scale_up:d=32,o=5,b=100:c,c#,d#,e,f#,g#,a#,b" # Use "none" to disable sound + TAG_STD_HW_SPEAKER: std.hw.speaker + packages: standard_hw_audio: !include TX-Ultimate-Easy-ESPHome_standard_hw_audio.yaml @@ -29,11 +31,9 @@ api: tone: string then: - lambda: |- - static const char *TAG = "api.actions.rtttl_play.standard.hw.speaker"; - rtttl_speaker->stop(); if (speaker_volume->state > 0) { - ESP_LOGI(TAG, "Play tone: '%s'", tone.c_str()); + ESP_LOGI("${TAG_STD_HW_SPEAKER}", "Play tone: '%s'", tone.c_str()); rtttl_speaker->play(tone.c_str()); } diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_vibration.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_vibration.yaml index bb55b43..d66124f 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_vibration.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_vibration.yaml @@ -14,6 +14,8 @@ substitutions: vibration_max_duration: 2s + TAG_STD_HW_VIBRATION: std.hw.vibration + binary_sensor: - id: bs_vibrating name: Vibrating @@ -61,19 +63,17 @@ script: - id: !extend dump_config then: - lambda: |- - static const char *TAG = "script.dump_config.standard.hw.vibration"; - // Configuration - ESP_LOGCONFIG(TAG, "Touch - Vibration feedback: %s", + ESP_LOGCONFIG("${TAG_STD_HW_VIBRATION}", "Touch - Vibration feedback: %s", sl_touch_vibration_feedback->state.c_str()); - ESP_LOGCONFIG(TAG, "Vibrate duration: %.0fms", nr_vibrating_duration->state); - ESP_LOGCONFIG(TAG, "Vibrate max duration: ${vibration_max_duration}"); + ESP_LOGCONFIG("${TAG_STD_HW_VIBRATION}", "Vibrate duration: %.0fms", nr_vibrating_duration->state); + ESP_LOGCONFIG("${TAG_STD_HW_VIBRATION}", "Vibrate max duration: ${vibration_max_duration}"); // State if (bs_vibrating->state) - ESP_LOGW(TAG, "Vibrating now: YES"); + ESP_LOGW("${TAG_STD_HW_VIBRATION}", "Vibrating now: YES"); else - ESP_LOGCONFIG(TAG, "Vibrating now: No"); + ESP_LOGCONFIG("${TAG_STD_HW_VIBRATION}", "Vibrating now: No"); - id: !extend dump_config_list_packages then: diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_media_player.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_media_player.yaml index 95bebb6..54474e9 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_media_player.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_media_player.yaml @@ -11,6 +11,9 @@ ##### - For normal system use, modifications to this file are NOT required. ##### #################################################################################################### --- +substitutions: + TAG_STD_MEDIA_PLAYER: "std.media-player" + packages: standard_hw_speaker: !include TX-Ultimate-Easy-ESPHome_standard_hw_speaker.yaml @@ -50,13 +53,12 @@ media_player: on_state: then: - lambda: |- - static const char *TAG = "media_player.on_state.standard.hw.media_player"; - // Check if volume has changed and update number entity id(persistent_media_player_volume) = static_cast(mp_media_player->volume * 100.0f); if (id(persistent_media_player_volume) != id(last_media_player_volume)) { id(last_media_player_volume) = id(persistent_media_player_volume); - ESP_LOGD(TAG, "Media player volume changed to %" PRIu8 "%%, updating number entity", id(persistent_media_player_volume)); + ESP_LOGD("${TAG_STD_MEDIA_PLAYER}", "Media player volume changed to %" PRIu8 "%%, updating number entity", + id(persistent_media_player_volume)); } script: @@ -67,12 +69,11 @@ script: - id: !extend dump_config then: - lambda: |- - static const char *TAG = "script.boot_sequence.standard.hw.media_player"; - // Volume sync configuration - ESP_LOGCONFIG(TAG, "Media Player Volume Sync:"); - ESP_LOGCONFIG(TAG, " Persistent volume: %" PRIu8 "%", id(persistent_media_player_volume)); - ESP_LOGCONFIG(TAG, " Current volume: %.1f%%", mp_media_player->volume * 100.0f); + ESP_LOGCONFIG("${TAG_STD_MEDIA_PLAYER}", "Media Player Volume Sync:"); + ESP_LOGCONFIG("${TAG_STD_MEDIA_PLAYER}", " Persistent volume: %" PRIu8 "%", + id(persistent_media_player_volume)); + ESP_LOGCONFIG("${TAG_STD_MEDIA_PLAYER}", " Current volume: %.1f%%", mp_media_player->volume * 100.0f); - id: !extend dump_config_list_packages then: @@ -97,10 +98,9 @@ script: id: mp_media_player volume: !lambda return id(persistent_media_player_volume) / 100.0f; - lambda: |- - static const char *TAG = "script.sync_initial_volume.standard.hw.media_player"; - id(last_media_player_volume) = id(persistent_media_player_volume); - ESP_LOGI(TAG, "Initial volume sync: %.1f%% (restored from persistence)", id(persistent_media_player_volume)); + ESP_LOGI("${TAG_STD_MEDIA_PLAYER}", "Initial volume sync: %.1f%% (restored from persistence)", + id(persistent_media_player_volume)); speaker: - platform: mixer From 1552aaec6c35c064a930ca2c783f0633fbf38358 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Tue, 5 Aug 2025 11:39:41 +0200 Subject: [PATCH 09/50] Lint --- .github/workflows/versioning.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/versioning.yml b/.github/workflows/versioning.yml index 17ba8dc..5bb683c 100644 --- a/.github/workflows/versioning.yml +++ b/.github/workflows/versioning.yml @@ -54,11 +54,11 @@ jobs: repo: context.repo.repo, commit_sha: context.sha }); - + if (associatedPRs.length > 0) { // Get the most recent merged PR const pr = associatedPRs.find(pr => pr.state === 'closed' && pr.merged_at) || associatedPRs[0]; - + return { title: pr.title, body: pr.body || 'No description provided', @@ -72,7 +72,7 @@ jobs: repo: context.repo.repo, ref: context.sha }); - + return { title: 'Direct push to main', body: commit.data.commit.message, @@ -99,23 +99,23 @@ jobs: id: tag_message run: | NEW_VERSION=$(cat ./versioning/VERSION) - + if [ "${{ github.event_name }}" == "push" ] && [ "${{ steps.pr_info.outcome }}" == "success" ]; then # Use PR information directly in heredoc to avoid quoting issues cat > tag_message.txt << EOF # v${NEW_VERSION} - ${{ fromJson(steps.pr_info.outputs.result).title }} - + ${{ fromJson(steps.pr_info.outputs.result).body }} EOF else # Manual dispatch or PR info unavailable - simpler message cat > tag_message.txt << EOF # v${NEW_VERSION} - Manual tag update - + Tag updated via manual workflow dispatch. EOF fi - + # Store the message for use in subsequent steps echo "TAG_MESSAGE_FILE=tag_message.txt" >> $GITHUB_ENV From bda597f74801b22f254048daacde90efd279cbaa Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Tue, 5 Aug 2025 17:51:18 +0200 Subject: [PATCH 10/50] nvs optimization - relays (except lights) --- ...TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml | 64 ----- ...imate-Easy-ESPHome_standard_hw_relays.yaml | 249 ++++++++++++++++-- .../tx_ultimate_easy/tx_ultimate_easy.h | 7 + 3 files changed, 238 insertions(+), 82 deletions(-) diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml index 90dddea..08197a7 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml @@ -550,70 +550,6 @@ script: // Identify itself ESP_LOGCONFIG(ESPHOME_PROJECT_NAME, " - Core - Hardware - LEDs"); - - id: light_set_state - mode: parallel - parameters: - light_group: uint8_t - light_index: uint8_t - state: bool - then: - - lambda: |- - static const uint32_t LIGHT_TRANSITION_TURN_ON = ${LIGHT_TRANSITION_TURN_ON}; - static const uint32_t LIGHT_TRANSITION_TURN_OFF = ${LIGHT_TRANSITION_TURN_OFF}; - static const uint8_t LIGHT_BRIGHTNESS_TURN_ON = ${LIGHT_BRIGHTNESS_TURN_ON}; - - const std::string light_mode_group_1 = id(is_us_model) - ? "${LIGHT_MODE_LEFT_TEXT}" - : "${LIGHT_MODE_BOTTOM_TEXT}"; - const std::string light_mode_group_2 = id(is_us_model) - ? "${LIGHT_MODE_RIGHT_TEXT}" - : "${LIGHT_MODE_TOP_TEXT}"; - - switch (light_group) { - case 1: - if (light_index >= id(gb_lights_1).size()) { - ESP_LOGE("${TAG_CORE_HW_LEDS}", "Invalid set with %s light for Relay %" PRIu8, light_mode_group_1.c_str(), light_index+1); - return; - } - if (state and !id(gb_lights_1)[light_index]->current_values.is_on()) { - ESP_LOGI("${TAG_CORE_HW_LEDS}", "Turn-on %s light for Relay %" PRIu8, light_mode_group_1.c_str(), light_index+1); - auto call = id(gb_lights_1)[light_index]->turn_on(); - if (LIGHT_TRANSITION_TURN_ON > 0) - call.set_transition_length(LIGHT_TRANSITION_TURN_ON); // in ms - if (LIGHT_BRIGHTNESS_TURN_ON > 0 and LIGHT_BRIGHTNESS_TURN_ON <= 100) - call.set_brightness(LIGHT_BRIGHTNESS_TURN_ON / 100.0f); - call.perform(); - } else if (!state and id(gb_lights_1)[light_index]->current_values.is_on()) { - ESP_LOGI("${TAG_CORE_HW_LEDS}", "Turn-off %s light for Relay %" PRIu8, light_mode_group_1.c_str(), light_index+1); - auto call = id(gb_lights_1)[light_index]->turn_off(); - if (LIGHT_TRANSITION_TURN_OFF > 0) - call.set_transition_length(LIGHT_TRANSITION_TURN_OFF); // in ms - call.perform(); - } - break; - case 2: - if (light_index >= id(gb_lights_2).size()) { - ESP_LOGE("${TAG_CORE_HW_LEDS}", "Invalid set with %s light for Relay %" PRIu8, light_mode_group_2.c_str(), light_index+1); - return; - } - if (state and !id(gb_lights_2)[light_index]->current_values.is_on()) { - ESP_LOGI("${TAG_CORE_HW_LEDS}", "Turn-on %s light for Relay %" PRIu8, light_mode_group_2.c_str(), light_index+1); - auto call = id(gb_lights_2)[light_index]->turn_on(); - if (LIGHT_TRANSITION_TURN_ON > 0) - call.set_transition_length(LIGHT_TRANSITION_TURN_ON); // in ms - if (LIGHT_BRIGHTNESS_TURN_ON > 0 and LIGHT_BRIGHTNESS_TURN_ON <= 100) - call.set_brightness(LIGHT_BRIGHTNESS_TURN_ON / 100.0f); - call.perform(); - } else if (!state and id(gb_lights_2)[light_index]->current_values.is_on()) { - ESP_LOGI("${TAG_CORE_HW_LEDS}", "Turn-off %s light for Relay %" PRIu8, light_mode_group_2.c_str(), light_index+1); - auto call = id(gb_lights_2)[light_index]->turn_off(); - if (LIGHT_TRANSITION_TURN_OFF > 0) - call.set_transition_length(LIGHT_TRANSITION_TURN_OFF); // in ms - call.perform(); - } - break; - } - select: # EU relay light mode template - &relay_light_mode_eu diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml index 6d036f5..77b2f73 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml @@ -16,8 +16,7 @@ substitutions: RELAY_MODE_TEXT_LIGHT: "Light" RELAY_MODE_TEXT_NOT_USED: "Not in use" - RELAY_RESTORE_MODE: RESTORE_DEFAULT_OFF - LIGHT_RELAYS_RESTORE_MODE: RESTORE_DEFAULT_OFF + LIGHT_RELAYS_RESTORE_MODE: ALWAYS_OFF TAG_STD_HW_RELAYS: std.hw.relays @@ -48,6 +47,26 @@ globals: restore_value: false initial_value: 'false' + # Packed relay modes - replaces 4 separate selects + - id: relay_modes_packed + type: uint16_t + restore_value: true + initial_value: '0' # Binary: 0000000000000000 = all relay modes set to option 0 (default action - switch) + # Bit layout: [unused][relay4(4)][relay3(4)][relay2(4)][relay1(4)] + # 16-bit value with 4 bits per relay: 0=Switch, 1=Light, 2=Not in use, 3-15=Reserved + + # Packed switch states - replaces 5 separate switches + - id: relay_switches_packed + type: uint8_t + restore_value: true + initial_value: '0' # All switches default to OFF + # Bit layout: [unused(3)][sw_relay4][sw_relay3][sw_relay2][sw_relay1][sw_expose_leds] + +# - id: attr_light_output_1 +# type: tx_ultimate_easy::LightAttributes +# restore_value: true +# initial_value: + light: # These lights are used for the physical relays to be shown as lights - id: light_output_1 @@ -55,7 +74,7 @@ light: output: output_relay_1 platform: binary internal: true - restore_mode: ${RELAY_RESTORE_MODE} + restore_mode: ${LIGHT_RELAYS_RESTORE_MODE} on_turn_on: then: - if: @@ -743,11 +762,6 @@ script: ESP_LOGCONFIG("${TAG_STD_HW_RELAYS}", " Relay modes: %s", YESNO(id(boot_initialization_relays_relay_modes))); ESP_LOGCONFIG("${TAG_STD_HW_RELAYS}", " Status LEDs: %s", YESNO(id(boot_initialization_relays_relay_leds))); - // Substitutions - ESP_LOGCONFIG("${TAG_STD_HW_RELAYS}", "Restore modes:"); - ESP_LOGCONFIG("${TAG_STD_HW_RELAYS}", " Relays: ${RELAY_RESTORE_MODE}"); - ESP_LOGCONFIG("${TAG_STD_HW_RELAYS}", " Status LEDs: ${LIGHT_RELAYS_RESTORE_MODE}"); - - id: !extend dump_config_list_packages then: - script.wait: dump_config @@ -760,6 +774,126 @@ script: // Identify itself ESP_LOGCONFIG(ESPHOME_PROJECT_NAME, " - Standard - Hardware - Relays"); + - id: light_set_state + mode: parallel + parameters: + light_group: uint8_t + light_index: uint8_t + state: bool + then: + - lambda: |- + static const uint32_t LIGHT_TRANSITION_TURN_ON = ${LIGHT_TRANSITION_TURN_ON}; + static const uint32_t LIGHT_TRANSITION_TURN_OFF = ${LIGHT_TRANSITION_TURN_OFF}; + static const uint8_t LIGHT_BRIGHTNESS_TURN_ON = ${LIGHT_BRIGHTNESS_TURN_ON}; + + const std::string light_mode_group_1 = id(is_us_model) + ? "${LIGHT_MODE_LEFT_TEXT}" + : "${LIGHT_MODE_BOTTOM_TEXT}"; + const std::string light_mode_group_2 = id(is_us_model) + ? "${LIGHT_MODE_RIGHT_TEXT}" + : "${LIGHT_MODE_TOP_TEXT}"; + + switch (light_group) { + case 1: + if (light_index >= id(gb_lights_1).size()) { + ESP_LOGE("${TAG_CORE_HW_LEDS}", "Invalid set with %s light for Relay %" PRIu8, light_mode_group_1.c_str(), light_index+1); + return; + } + if (state and !id(gb_lights_1)[light_index]->current_values.is_on()) { + ESP_LOGI("${TAG_CORE_HW_LEDS}", "Turn-on %s light for Relay %" PRIu8, light_mode_group_1.c_str(), light_index+1); + auto call = id(gb_lights_1)[light_index]->turn_on(); + if (LIGHT_TRANSITION_TURN_ON > 0) + call.set_transition_length(LIGHT_TRANSITION_TURN_ON); // in ms + if (LIGHT_BRIGHTNESS_TURN_ON > 0 and LIGHT_BRIGHTNESS_TURN_ON <= 100) + call.set_brightness(LIGHT_BRIGHTNESS_TURN_ON / 100.0f); + call.perform(); + } else if (!state and id(gb_lights_1)[light_index]->current_values.is_on()) { + ESP_LOGI("${TAG_CORE_HW_LEDS}", "Turn-off %s light for Relay %" PRIu8, light_mode_group_1.c_str(), light_index+1); + auto call = id(gb_lights_1)[light_index]->turn_off(); + if (LIGHT_TRANSITION_TURN_OFF > 0) + call.set_transition_length(LIGHT_TRANSITION_TURN_OFF); // in ms + call.perform(); + } + break; + case 2: + if (light_index >= id(gb_lights_2).size()) { + ESP_LOGE("${TAG_CORE_HW_LEDS}", "Invalid set with %s light for Relay %" PRIu8, light_mode_group_2.c_str(), light_index+1); + return; + } + if (state and !id(gb_lights_2)[light_index]->current_values.is_on()) { + ESP_LOGI("${TAG_CORE_HW_LEDS}", "Turn-on %s light for Relay %" PRIu8, light_mode_group_2.c_str(), light_index+1); + auto call = id(gb_lights_2)[light_index]->turn_on(); + if (LIGHT_TRANSITION_TURN_ON > 0) + call.set_transition_length(LIGHT_TRANSITION_TURN_ON); // in ms + if (LIGHT_BRIGHTNESS_TURN_ON > 0 and LIGHT_BRIGHTNESS_TURN_ON <= 100) + call.set_brightness(LIGHT_BRIGHTNESS_TURN_ON / 100.0f); + call.perform(); + } else if (!state and id(gb_lights_2)[light_index]->current_values.is_on()) { + ESP_LOGI("${TAG_CORE_HW_LEDS}", "Turn-off %s light for Relay %" PRIu8, light_mode_group_2.c_str(), light_index+1); + auto call = id(gb_lights_2)[light_index]->turn_off(); + if (LIGHT_TRANSITION_TURN_OFF > 0) + call.set_transition_length(LIGHT_TRANSITION_TURN_OFF); // in ms + call.perform(); + } + break; + } + + - id: !extend restore_from_nvs + then: + # Relay modes + - select.set_index: + id: sl_relay_1_mode + index: !lambda return std::min((uint8_t)(id(relay_modes_packed) & 0x0F), (uint8_t)2); + - select.set_index: + id: sl_relay_2_mode + index: !lambda return std::min((uint8_t)((id(relay_modes_packed) >> 4) & 0x0F), (uint8_t)2); + - select.set_index: + id: sl_relay_3_mode + index: !lambda return std::min((uint8_t)((id(relay_modes_packed) >> 8) & 0x0F), (uint8_t)2); + - select.set_index: + id: sl_relay_4_mode + index: !lambda return std::min((uint8_t)((id(relay_modes_packed) >> 12) & 0x0F), (uint8_t)2); + + # Switch states (new - your improved version) + - if: + condition: + lambda: return (id(relay_switches_packed) & 0x01) == 0; + then: + - switch.turn_off: sw_expose_relays_leds_to_ha + else: + - switch.turn_on: sw_expose_relays_leds_to_ha + - if: + condition: + lambda: return (id(relay_switches_packed) & 0x02) == 0; + then: + - switch.turn_off: sw_relay_1 + else: + - switch.turn_on: sw_relay_1 + - if: + condition: + lambda: return (id(relay_switches_packed) & 0x04) == 0; + then: + - switch.turn_off: sw_relay_2 + else: + - switch.turn_on: sw_relay_2 + - if: + condition: + lambda: return (id(relay_switches_packed) & 0x08) == 0; + then: + - switch.turn_off: sw_relay_3 + else: + - switch.turn_on: sw_relay_3 + - if: + condition: + lambda: return (id(relay_switches_packed) & 0x10) == 0; + then: + - switch.turn_off: sw_relay_4 + else: + - switch.turn_on: sw_relay_4 + + # update lights based on new relay statuses (catch up, as this should be done when each relay is updated) + - script.execute: show_relay_status + - id: show_relay_status mode: restart then: @@ -827,8 +961,7 @@ script: } select: - - &relay_select_mode_base - id: sl_relay_1_mode + - id: sl_relay_1_mode name: Relay 1 display mode platform: template options: @@ -847,21 +980,81 @@ select: - binary_sensor.template.publish: id: bs_pending_restart state: true + - lambda: |- + id(relay_modes_packed) = (id(relay_modes_packed) & ~(0x0F << 0)) | + ((id(sl_relay_1_mode).active_index().value_or(1) & 0x0F) << 0); - id: sl_relay_2_mode name: Relay 2 display mode - <<: *relay_select_mode_base + 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 + on_value: + then: + - binary_sensor.template.publish: + id: bs_pending_restart + state: true + - lambda: |- + id(relay_modes_packed) = (id(relay_modes_packed) & ~(0x0F << 4)) | + ((id(sl_relay_2_mode).active_index().value_or(1) & 0x0F) << 4); - id: sl_relay_3_mode name: Relay 3 display mode - <<: *relay_select_mode_base + 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 + on_value: + then: + - binary_sensor.template.publish: + id: bs_pending_restart + state: true + - lambda: |- + id(relay_modes_packed) = (id(relay_modes_packed) & ~(0x0F << 8)) | + ((id(sl_relay_3_mode).active_index().value_or(1) & 0x0F) << 8); - id: sl_relay_4_mode name: Relay 4 display mode - <<: *relay_select_mode_base + 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 + on_value: + then: + - binary_sensor.template.publish: + id: bs_pending_restart + state: true + - lambda: |- + id(relay_modes_packed) = (id(relay_modes_packed) & ~(0x0F << 12)) | + ((id(sl_relay_4_mode).active_index().value_or(1) & 0x0F) << 12); switch: - id: sw_expose_relays_leds_to_ha name: Expose Relay's LEDs to Home Assistant platform: template - restore_mode: RESTORE_DEFAULT_OFF + restore_mode: ALWAYS_OFF optimistic: true internal: false entity_category: config @@ -870,17 +1063,21 @@ switch: - binary_sensor.template.publish: id: bs_pending_restart state: true + - lambda: |- + id(relay_switches_packed) |= 0x01; // Set bit 0 on_turn_off: then: - binary_sensor.template.publish: id: bs_pending_restart state: true + - lambda: |- + id(relay_switches_packed) &= ~0x01; // Clear bit 0 - id: sw_relay_1 name: Relay 1 output: output_relay_1 platform: output - restore_mode: ${RELAY_RESTORE_MODE} + restore_mode: ALWAYS_OFF internal: true on_turn_on: then: @@ -890,6 +1087,8 @@ switch: - light.is_off: light_output_1 then: light.turn_on: light_output_1 + - lambda: |- + id(relay_switches_packed) |= 0x02; // Set bit 1 on_turn_off: then: - script.execute: show_relay_status @@ -898,11 +1097,13 @@ switch: - light.is_on: light_output_1 then: - light.turn_off: light_output_1 + - lambda: |- + id(relay_switches_packed) &= ~0x02; // Clear bit 1 - id: sw_relay_2 name: Relay 2 output: output_relay_2 platform: output - restore_mode: ${RELAY_RESTORE_MODE} + restore_mode: ALWAYS_OFF internal: true on_turn_on: then: @@ -912,6 +1113,8 @@ switch: - light.is_off: light_output_2 then: light.turn_on: light_output_2 + - lambda: |- + id(relay_switches_packed) |= 0x04; // Set bit 2 on_turn_off: then: - script.execute: show_relay_status @@ -920,11 +1123,13 @@ switch: - light.is_on: light_output_2 then: - light.turn_off: light_output_2 + - lambda: |- + id(relay_switches_packed) &= ~0x04; // Clear bit 2 - id: sw_relay_3 name: Relay 3 output: output_relay_3 platform: output - restore_mode: ${RELAY_RESTORE_MODE} + restore_mode: ALWAYS_OFF internal: true on_turn_on: then: @@ -934,6 +1139,8 @@ switch: - light.is_off: light_output_3 then: - light.turn_on: light_output_3 + - lambda: |- + id(relay_switches_packed) |= 0x08; // Set bit 3 on_turn_off: then: - script.execute: show_relay_status @@ -942,11 +1149,13 @@ switch: - light.is_on: light_output_3 then: - light.turn_off: light_output_3 + - lambda: |- + id(relay_switches_packed) &= ~0x08; // Clear bit 3 - id: sw_relay_4 name: Relay 4 output: output_relay_4 platform: output - restore_mode: ${RELAY_RESTORE_MODE} + restore_mode: ALWAYS_OFF internal: true on_turn_on: then: @@ -956,6 +1165,8 @@ switch: - light.is_off: light_output_4 then: - light.turn_on: light_output_4 + - lambda: |- + id(relay_switches_packed) |= 0x10; // Set bit 4 on_turn_off: then: - script.execute: show_relay_status @@ -964,4 +1175,6 @@ switch: - light.is_on: light_output_4 then: - light.turn_off: light_output_4 + - lambda: |- + id(relay_switches_packed) &= ~0x10; // Clear bit 4 ... diff --git a/components/tx_ultimate_easy/tx_ultimate_easy.h b/components/tx_ultimate_easy/tx_ultimate_easy.h index cfb9e8b..c982537 100644 --- a/components/tx_ultimate_easy/tx_ultimate_easy.h +++ b/components/tx_ultimate_easy/tx_ultimate_easy.h @@ -34,6 +34,13 @@ namespace esphome { // Log tag static const char *TAG = "tx_ultimate_easy"; + struct LightAttributes { + uint8_t brightness; + uint8_t red; + uint8_t green; + uint8_t blue; + }; + struct TouchPoint { uint8_t button = 0; int8_t x = -1; From 33bbad6adb98ae336d0d0ca7c0311d2fc4e0ab70 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Tue, 5 Aug 2025 18:07:39 +0200 Subject: [PATCH 11/50] Lint --- ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml index 77b2f73..d2a52bd 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml @@ -65,7 +65,7 @@ globals: # - id: attr_light_output_1 # type: tx_ultimate_easy::LightAttributes # restore_value: true -# initial_value: +# initial_value: light: # These lights are used for the physical relays to be shown as lights From c9a85504f152c291cd7ef78fe35751743d918bf4 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Tue, 5 Aug 2025 19:46:12 +0200 Subject: [PATCH 12/50] Initial nvs optimization for relay lights --- ...TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml | 44 +++++++++-- ...imate-Easy-ESPHome_standard_hw_relays.yaml | 25 ++++++- .../tx_ultimate_easy/tx_ultimate_easy.cpp | 23 ++++++ .../tx_ultimate_easy/tx_ultimate_easy.h | 75 +++++++++++++++++-- 4 files changed, 148 insertions(+), 19 deletions(-) diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml index 08197a7..a4567d2 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml @@ -29,6 +29,8 @@ substitutions: LIGHT_SIDES_RESTORE_MODE: RESTORE_DEFAULT_OFF LIGHT_INDIVIDUAL_RESTORE_MODE: RESTORE_DEFAULT_OFF + LIGHT_ATTRS_DEFAULT: '0x64FFFFFF' # brightness=100, RGB=255,255,255 + TAG_CORE_HW_LEDS: core.hw.leds esphome: @@ -36,14 +38,6 @@ esphome: build_flags: - -D TX_ULTIMATE_EASY_CORE_HW_LEDS -globals: - - id: gb_lights_1 - type: std::vector - restore_value: false - - id: gb_lights_2 - type: std::vector - restore_value: false - light: # These lights are controlling the LEDs on the device - id: light_full @@ -550,6 +544,40 @@ script: // Identify itself ESP_LOGCONFIG(ESPHOME_PROJECT_NAME, " - Core - Hardware - LEDs"); + - id: light_attributes_restore + mode: parallel + parameters: + light_ptr: light::LightState* + attr_global_ptr: uint32_t* + then: + - lambda: |- + tx_ultimate_easy::LightAttributes attrs = tx_ultimate_easy::unpack_light_attributes(*attr_global_ptr); + auto call = light_ptr->make_call(); + call.set_brightness(attrs.brightness / 100.0f); + call.set_rgb(attrs.red / 255.0f, attrs.green / 255.0f, attrs.blue / 255.0f); + call.perform(); + ESP_LOGI("${TAG_CORE_HW_LEDS}", "Restored light attributes: brightness=%d%%, RGB=(%d,%d,%d)", + attrs.brightness, attrs.red, attrs.green, attrs.blue); + + - id: light_attributes_save + mode: parallel + parameters: + light_ptr: light::LightState* + attr_global_ptr: uint32_t* + then: + - lambda: |- + auto current = light_ptr->current_values; + tx_ultimate_easy::LightAttributes attrs = { + .brightness = uint8_t(current.get_brightness() * 100.0f), + .red = uint8_t(current.get_red() * 255.0f), + .green = uint8_t(current.get_green() * 255.0f), + .blue = uint8_t(current.get_blue() * 255.0f) + }; + + *attr_global_ptr = tx_ultimate_easy::pack_light_attributes(attrs); + ESP_LOGD("${TAG_CORE_HW_LEDS}", "Saved light attributes: brightness=%d%%, RGB=(%d,%d,%d)", + attrs.brightness, attrs.red, attrs.green, attrs.blue); + select: # EU relay light mode template - &relay_light_mode_eu diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml index d2a52bd..9ab29e8 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml @@ -47,6 +47,13 @@ globals: restore_value: false initial_value: 'false' + - id: gb_lights_1 + type: std::vector + restore_value: false + - id: gb_lights_2 + type: std::vector + restore_value: false + # Packed relay modes - replaces 4 separate selects - id: relay_modes_packed type: uint16_t @@ -62,10 +69,10 @@ globals: initial_value: '0' # All switches default to OFF # Bit layout: [unused(3)][sw_relay4][sw_relay3][sw_relay2][sw_relay1][sw_expose_leds] -# - id: attr_light_output_1 -# type: tx_ultimate_easy::LightAttributes -# restore_value: true -# initial_value: + - id: attr_light_output_1 + type: uint32_t + restore_value: true + initial_value: '${LIGHT_ATTRS_DEFAULT}' light: # These lights are used for the physical relays to be shown as lights @@ -89,6 +96,12 @@ light: - switch.is_on: sw_relay_1 then: - switch.turn_off: sw_relay_1 + on_state: + then: + - script.execute: + id: light_attributes_save + light_ptr: !lambda return id(light_output_1); + attr_global_ptr: !lambda return &id(attr_light_output_1); - id: light_output_2 name: Light output 2 output: output_relay_2 @@ -770,6 +783,10 @@ script: #if !defined(TX_ULTIMATE_EASY_CORE) #error "The package TX-Ultimate-Easy-ESPHome_core.yaml is required." #endif + #if !defined(TX_ULTIMATE_EASY_CORE_HW_LEDS) + #error "The package TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml is required." + #endif // TX_ULTIMATE_EASY_CORE_HW_LEDS + // Identify itself ESP_LOGCONFIG(ESPHOME_PROJECT_NAME, " - Standard - Hardware - Relays"); diff --git a/components/tx_ultimate_easy/tx_ultimate_easy.cpp b/components/tx_ultimate_easy/tx_ultimate_easy.cpp index 82f560b..d820448 100644 --- a/components/tx_ultimate_easy/tx_ultimate_easy.cpp +++ b/components/tx_ultimate_easy/tx_ultimate_easy.cpp @@ -223,5 +223,28 @@ namespace esphome { return tp; } + // LightAttributes + + // Constant definitions + const LightAttributes LIGHT_ATTRS_DEFAULT = {100, 255, 255, 255}; + const uint32_t LIGHT_ATTRS_DEFAULT_PACKED = 0x64FFFFFF; + + // Function implementations + uint32_t pack_light_attributes(const LightAttributes& attr) { + return (uint32_t(attr.brightness) << 24) | + (uint32_t(attr.red) << 16) | + (uint32_t(attr.green) << 8) | + (uint32_t(attr.blue)); + } + + LightAttributes unpack_light_attributes(uint32_t packed) { + return { + .brightness = uint8_t((packed >> 24) & 0xFF), + .red = uint8_t((packed >> 16) & 0xFF), + .green = uint8_t((packed >> 8) & 0xFF), + .blue = uint8_t(packed & 0xFF) + }; + } + } // namespace tx_ultimate_easy } // namespace esphome diff --git a/components/tx_ultimate_easy/tx_ultimate_easy.h b/components/tx_ultimate_easy/tx_ultimate_easy.h index c982537..5175398 100644 --- a/components/tx_ultimate_easy/tx_ultimate_easy.h +++ b/components/tx_ultimate_easy/tx_ultimate_easy.h @@ -34,13 +34,6 @@ namespace esphome { // Log tag static const char *TAG = "tx_ultimate_easy"; - struct LightAttributes { - uint8_t brightness; - uint8_t red; - uint8_t green; - uint8_t blue; - }; - struct TouchPoint { uint8_t button = 0; int8_t x = -1; @@ -89,5 +82,73 @@ namespace esphome { }; // class TxUltimateEasy + /** + * @brief Light attributes structure for storing color and brightness data. + * + * This structure represents the visual attributes of a light without the state (on/off). + * It's designed to be packed efficiently into a 32-bit integer for NVS storage. + * RGB components use the full 8-bit range (0-255) while brightness uses percentage (0-100). + */ + struct LightAttributes { + uint8_t brightness; ///< Light brightness percentage (0-100, where 100 = full brightness) + uint8_t red; ///< Red color component (0-255) + uint8_t green; ///< Green color component (0-255) + uint8_t blue; ///< Blue color component (0-255) + }; + + /** + * @brief Packs LightAttributes structure into a 32-bit integer for storage. + * + * Bit layout: [brightness(8)][red(8)][green(8)][blue(8)] + * This allows efficient storage in ESPHome globals with restore_value support. + * Note: Brightness values > 100 will be clamped to 100 during packing. + * + * @param attr The LightAttributes structure to pack + * @return uint32_t Packed representation suitable for NVS storage + * + * @example + * LightAttributes attrs = {75, 255, 128, 64}; + * uint32_t packed = pack_light_attributes(attrs); // Returns 0x4BFF8040 + */ + uint32_t pack_light_attributes(const LightAttributes& attr); + + /** + * @brief Unpacks a 32-bit integer back into LightAttributes structure. + * + * Reverses the packing operation to extract individual color and brightness values. + * Brightness values > 100 in the packed data will be clamped to 100. + * + * @param packed The packed 32-bit representation + * @return LightAttributes The unpacked structure with individual components + * + * @example + * uint32_t packed = 0x4BFF8040; + * LightAttributes attrs = unpack_light_attributes(packed); + * // attrs.brightness = 75, attrs.red = 255, attrs.green = 128, attrs.blue = 64 + */ + LightAttributes unpack_light_attributes(uint32_t packed); + + /** + * @brief Default light attributes for initialization. + * + * Provides sensible defaults for new lights: + * - Brightness: 100 (full brightness) + * - Red: 255 (full intensity) + * - Green: 255 (full intensity) + * - Blue: 255 (full intensity) + * Result: Pure white color at full brightness + */ + extern const LightAttributes LIGHT_ATTRS_DEFAULT; + + /** + * @brief Pre-packed version of DEFAULT_LIGHT_ATTRS for convenience. + * + * This constant contains LIGHT_ATTRS_DEFAULT already packed into uint32_t format, + * suitable for direct use in ESPHome YAML initial_value fields. + * + * Value: 0x64FFFFFF (brightness=100, RGB=255,255,255) + */ + extern const uint32_t LIGHT_ATTRS_DEFAULT_PACKED; + } // namespace tx_ultimate_easy } // namespace esphome From fa2e8f3b6213365be20b6a56df49e583dc1c2546 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Tue, 5 Aug 2025 20:01:32 +0200 Subject: [PATCH 13/50] Update dependencies --- ESPHome/TX-Ultimate-Easy-ESPHome_core_common.yaml | 14 +++++++++++++- .../TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml | 4 ++-- ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml | 4 ++-- .../TX-Ultimate-Easy-ESPHome_core_hw_touch.yaml | 14 ++------------ ...TX-Ultimate-Easy-ESPHome_standard_hw_audio.yaml | 4 ++-- ...X-Ultimate-Easy-ESPHome_standard_hw_relays.yaml | 4 ++-- ...-Ultimate-Easy-ESPHome_standard_hw_speaker.yaml | 4 ++-- ...ltimate-Easy-ESPHome_standard_hw_vibration.yaml | 4 ++-- ...ltimate-Easy-ESPHome_standard_media_player.yaml | 4 ++-- 9 files changed, 29 insertions(+), 27 deletions(-) diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core_common.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core_common.yaml index 09e699e..ace3829 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core_common.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core_common.yaml @@ -76,7 +76,7 @@ esphome: version: ${version} platformio_options: build_flags: - - -D TX_ULTIMATE_EASY_CORE + - -D TX_ULTIMATE_EASY_CORE_COMMON on_boot: - priority: 750 @@ -95,6 +95,15 @@ esphome: then: - script.execute: boot_done +external_components: + - source: + type: git + url: https://github.com/edwardtfn/TX-Ultimate-Easy + ref: v${version} + refresh: 1h + components: + - tx_ultimate_easy + globals: - id: is_us_model type: bool @@ -332,6 +341,9 @@ text_sensor: } return result; +tx_ultimate_easy: + id: tx_ultimate + wifi: id: wifi_component ... diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml index c70670d..b5b22fd 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml @@ -241,8 +241,8 @@ script: - 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." + #if !defined(TX_ULTIMATE_EASY_CORE_COMMON) + #error "The package TX-Ultimate-Easy-ESPHome_core_common.yaml is required." #endif // Identify itself diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml index a4567d2..085f35b 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml @@ -537,8 +537,8 @@ script: - 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." + #if !defined(TX_ULTIMATE_EASY_CORE_COMMON) + #error "The package TX-Ultimate-Easy-ESPHome_core_common.yaml is required." #endif // Identify itself diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_touch.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_touch.yaml index dfe940e..5d1d7a2 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_touch.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_touch.yaml @@ -205,15 +205,6 @@ esphome: build_flags: - -D TX_ULTIMATE_EASY_CORE_HW_TOUCH -external_components: - - source: - type: git - url: https://github.com/edwardtfn/TX-Ultimate-Easy - ref: v${version} - refresh: 1h - components: - - tx_ultimate_easy - number: - id: nr_touch_duration name: Touch event duration @@ -305,8 +296,8 @@ script: - 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." + #if !defined(TX_ULTIMATE_EASY_CORE_COMMON) + #error "The package TX-Ultimate-Easy-ESPHome_core_common.yaml is required." #endif // Identify itself @@ -375,7 +366,6 @@ switch: entity_category: config tx_ultimate_easy: - id: tx_ultimate uart: uart_touch on_long_touch_release: diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_audio.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_audio.yaml index e53dc0f..a11e595 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_audio.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_audio.yaml @@ -32,8 +32,8 @@ script: - 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." + #if !defined(TX_ULTIMATE_EASY_CORE_COMMON) + #error "The package TX-Ultimate-Easy-ESPHome_core_common.yaml is required." #endif // Identify itself diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml index 9ab29e8..b200d5d 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml @@ -780,8 +780,8 @@ script: - 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." + #if !defined(TX_ULTIMATE_EASY_CORE_COMMON) + #error "The package TX-Ultimate-Easy-ESPHome_core_common.yaml is required." #endif #if !defined(TX_ULTIMATE_EASY_CORE_HW_LEDS) #error "The package TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml is required." diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_speaker.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_speaker.yaml index 86a19e2..3cd8678 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_speaker.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_speaker.yaml @@ -78,8 +78,8 @@ script: - 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." + #if !defined(TX_ULTIMATE_EASY_CORE_COMMON) + #error "The package TX-Ultimate-Easy-ESPHome_core_common.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." diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_vibration.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_vibration.yaml index d66124f..db1895e 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_vibration.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_vibration.yaml @@ -80,8 +80,8 @@ script: - 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." + #if !defined(TX_ULTIMATE_EASY_CORE_COMMON) + #error "The package TX-Ultimate-Easy-ESPHome_core_common.yaml is required." #endif // Identify itself diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_media_player.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_media_player.yaml index 54474e9..3d51e86 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_media_player.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_media_player.yaml @@ -80,8 +80,8 @@ script: - 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." + #if !defined(TX_ULTIMATE_EASY_CORE_COMMON) + #error "The package TX-Ultimate-Easy-ESPHome_core_common.yaml is required." #endif // TX_ULTIMATE_EASY_CORE #if !defined(TX_ULTIMATE_EASY_STANDARD_HW_SPEAKER) #error "The package TX-Ultimate-Easy-ESPHome_standard_hw_speaker.yaml is required." From 91f24db3b1adb0346249376f12b851fd9603a238 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Tue, 5 Aug 2025 21:15:35 +0200 Subject: [PATCH 14/50] Clarify touch support Solves #98 --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8368b31..b17d138 100644 --- a/README.md +++ b/README.md @@ -348,7 +348,7 @@ If the device isn't discovered automatically: - Button actions 3. Optional: Configure advanced features - LED behaviors - - Touch sensitivity + - Touch duration - Haptic feedback - Audio feedback 4. Test your configuration: @@ -374,7 +374,7 @@ After installation, you can: TX Ultimate Easy offers extensive configuration options: -- Touch panel sensitivity and gestures +- Touch panel gestures - LED colors, patterns, and behaviors - Relay modes and functions - Audio and haptic feedback settings From 93120a8b511a34be528de0e8a216e75027bcb3b8 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Tue, 5 Aug 2025 21:27:02 +0200 Subject: [PATCH 15/50] Fix log reference --- .../TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml index b200d5d..eda71e9 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml @@ -813,11 +813,11 @@ script: switch (light_group) { case 1: if (light_index >= id(gb_lights_1).size()) { - ESP_LOGE("${TAG_CORE_HW_LEDS}", "Invalid set with %s light for Relay %" PRIu8, light_mode_group_1.c_str(), light_index+1); + ESP_LOGE("${TAG_STD_HW_RELAYS}", "Invalid set with %s light for Relay %" PRIu8, light_mode_group_1.c_str(), light_index+1); return; } if (state and !id(gb_lights_1)[light_index]->current_values.is_on()) { - ESP_LOGI("${TAG_CORE_HW_LEDS}", "Turn-on %s light for Relay %" PRIu8, light_mode_group_1.c_str(), light_index+1); + ESP_LOGI("${TAG_STD_HW_RELAYS}", "Turn-on %s light for Relay %" PRIu8, light_mode_group_1.c_str(), light_index+1); auto call = id(gb_lights_1)[light_index]->turn_on(); if (LIGHT_TRANSITION_TURN_ON > 0) call.set_transition_length(LIGHT_TRANSITION_TURN_ON); // in ms @@ -825,7 +825,7 @@ script: call.set_brightness(LIGHT_BRIGHTNESS_TURN_ON / 100.0f); call.perform(); } else if (!state and id(gb_lights_1)[light_index]->current_values.is_on()) { - ESP_LOGI("${TAG_CORE_HW_LEDS}", "Turn-off %s light for Relay %" PRIu8, light_mode_group_1.c_str(), light_index+1); + ESP_LOGI("${TAG_STD_HW_RELAYS}", "Turn-off %s light for Relay %" PRIu8, light_mode_group_1.c_str(), light_index+1); auto call = id(gb_lights_1)[light_index]->turn_off(); if (LIGHT_TRANSITION_TURN_OFF > 0) call.set_transition_length(LIGHT_TRANSITION_TURN_OFF); // in ms @@ -834,11 +834,11 @@ script: break; case 2: if (light_index >= id(gb_lights_2).size()) { - ESP_LOGE("${TAG_CORE_HW_LEDS}", "Invalid set with %s light for Relay %" PRIu8, light_mode_group_2.c_str(), light_index+1); + ESP_LOGE("${TAG_STD_HW_RELAYS}", "Invalid set with %s light for Relay %" PRIu8, light_mode_group_2.c_str(), light_index+1); return; } if (state and !id(gb_lights_2)[light_index]->current_values.is_on()) { - ESP_LOGI("${TAG_CORE_HW_LEDS}", "Turn-on %s light for Relay %" PRIu8, light_mode_group_2.c_str(), light_index+1); + ESP_LOGI("${TAG_STD_HW_RELAYS}", "Turn-on %s light for Relay %" PRIu8, light_mode_group_2.c_str(), light_index+1); auto call = id(gb_lights_2)[light_index]->turn_on(); if (LIGHT_TRANSITION_TURN_ON > 0) call.set_transition_length(LIGHT_TRANSITION_TURN_ON); // in ms @@ -846,7 +846,7 @@ script: call.set_brightness(LIGHT_BRIGHTNESS_TURN_ON / 100.0f); call.perform(); } else if (!state and id(gb_lights_2)[light_index]->current_values.is_on()) { - ESP_LOGI("${TAG_CORE_HW_LEDS}", "Turn-off %s light for Relay %" PRIu8, light_mode_group_2.c_str(), light_index+1); + ESP_LOGI("${TAG_STD_HW_RELAYS}", "Turn-off %s light for Relay %" PRIu8, light_mode_group_2.c_str(), light_index+1); auto call = id(gb_lights_2)[light_index]->turn_off(); if (LIGHT_TRANSITION_TURN_OFF > 0) call.set_transition_length(LIGHT_TRANSITION_TURN_OFF); // in ms From ce54db0f6de8be1a3e0c02b0e2bbb1d8fcd7bb03 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Tue, 5 Aug 2025 21:36:48 +0200 Subject: [PATCH 16/50] Set version for test --- versioning/VERSION_YAML | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/versioning/VERSION_YAML b/versioning/VERSION_YAML index 34cd29d..0a1a61a 100644 --- a/versioning/VERSION_YAML +++ b/versioning/VERSION_YAML @@ -1 +1 @@ -version: 2025.8.2 +version: 2025.8.2dev From 271aa74f994ccd91b1a131a51ef1244a8e25f5af Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Wed, 6 Aug 2025 13:43:36 +0200 Subject: [PATCH 17/50] Do not update lights when the action is disabled Solves #109 --- ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml index eda71e9..f071157 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml @@ -964,16 +964,13 @@ script: ESP_LOGV("${TAG_STD_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_DISABLED_TEXT}") { if (light_mode == light_mode_group_1 || light_mode == "${LIGHT_MODE_BOTH_TEXT}") { - light_set_state->execute(1, i, true); // Left/bottom light + light_set_state->execute(1, i, relay_state); // 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 + light_set_state->execute(2, i, relay_state); // 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 } } From 6a1a5d5ae2e1b4d0de146f04748d096489637a96 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Wed, 6 Aug 2025 14:27:35 +0200 Subject: [PATCH 18/50] Extend nvs optimization to relay's lights --- ...imate-Easy-ESPHome_standard_hw_relays.yaml | 287 +++++++++++++++++- 1 file changed, 280 insertions(+), 7 deletions(-) diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml index f071157..7772766 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml @@ -69,10 +69,89 @@ globals: initial_value: '0' # All switches default to OFF # Bit layout: [unused(3)][sw_relay4][sw_relay3][sw_relay2][sw_relay1][sw_expose_leds] - - id: attr_light_output_1 + - &global_attr_light + id: attr_light_eu_rl_1_1_bottom type: uint32_t restore_value: true initial_value: '${LIGHT_ATTRS_DEFAULT}' + - id: attr_light_eu_rl_1_1_top + <<: *global_attr_light + - id: attr_light_eu_rl_1_2_bottom + <<: *global_attr_light + - id: attr_light_eu_rl_1_2_top + <<: *global_attr_light + - id: attr_light_eu_rl_2_2_bottom + <<: *global_attr_light + - id: attr_light_eu_rl_2_2_top + <<: *global_attr_light + - id: attr_light_eu_rl_1_3_bottom + <<: *global_attr_light + - id: attr_light_eu_rl_1_3_top + <<: *global_attr_light + - id: attr_light_eu_rl_2_3_bottom + <<: *global_attr_light + - id: attr_light_eu_rl_2_3_top + <<: *global_attr_light + - id: attr_light_eu_rl_3_3_bottom + <<: *global_attr_light + - id: attr_light_eu_rl_3_3_top + <<: *global_attr_light + - id: attr_light_eu_rl_1_4_bottom + <<: *global_attr_light + - id: attr_light_eu_rl_1_4_top + <<: *global_attr_light + - id: attr_light_eu_rl_2_4_bottom + <<: *global_attr_light + - id: attr_light_eu_rl_2_4_top + <<: *global_attr_light + - id: attr_light_eu_rl_3_4_bottom + <<: *global_attr_light + - id: attr_light_eu_rl_3_4_top + <<: *global_attr_light + - id: attr_light_eu_rl_4_4_bottom + <<: *global_attr_light + - id: attr_light_eu_rl_4_4_top + <<: *global_attr_light + - id: attr_light_us_rl_1_1_left + <<: *global_attr_light + - id: attr_light_us_rl_1_1_right + <<: *global_attr_light + - id: attr_light_us_rl_1_2_left + <<: *global_attr_light + - id: attr_light_us_rl_1_2_right + <<: *global_attr_light + - id: attr_light_us_rl_2_2_left + <<: *global_attr_light + - id: attr_light_us_rl_2_2_right + <<: *global_attr_light + - id: attr_light_us_rl_1_3_left + <<: *global_attr_light + - id: attr_light_us_rl_1_3_right + <<: *global_attr_light + - id: attr_light_us_rl_2_3_left + <<: *global_attr_light + - id: attr_light_us_rl_2_3_right + <<: *global_attr_light + - id: attr_light_us_rl_3_3_left + <<: *global_attr_light + - id: attr_light_us_rl_3_3_right + <<: *global_attr_light + - id: attr_light_us_rl_1_4_left + <<: *global_attr_light + - id: attr_light_us_rl_1_4_right + <<: *global_attr_light + - id: attr_light_us_rl_2_4_left + <<: *global_attr_light + - id: attr_light_us_rl_2_4_right + <<: *global_attr_light + - id: attr_light_us_rl_3_4_left + <<: *global_attr_light + - id: attr_light_us_rl_3_4_right + <<: *global_attr_light + - id: attr_light_us_rl_4_4_left + <<: *global_attr_light + - id: attr_light_us_rl_4_4_right + <<: *global_attr_light light: # These lights are used for the physical relays to be shown as lights @@ -96,12 +175,6 @@ light: - switch.is_on: sw_relay_1 then: - switch.turn_off: sw_relay_1 - on_state: - then: - - script.execute: - id: light_attributes_save - light_ptr: !lambda return id(light_output_1); - attr_global_ptr: !lambda return &id(attr_light_output_1); - id: light_output_2 name: Light output 2 output: output_relay_2 @@ -173,6 +246,11 @@ light: - id: light_full from: 9 to: 9 + on_state: + - script.execute: + id: light_attributes_save + light_ptr: !lambda return id(light_eu_rl_1_1_bottom); + attr_global_ptr: !lambda return &id(attr_light_eu_rl_1_1_bottom); - id: light_eu_rl_1_1_top name: Light - Relay 1 of 1 (top) <<: *light_partition_relays @@ -180,6 +258,11 @@ light: - id: light_full from: 23 to: 23 + on_state: + - script.execute: + id: light_attributes_save + light_ptr: !lambda return id(light_eu_rl_1_1_top); + attr_global_ptr: !lambda return &id(attr_light_eu_rl_1_1_top); - id: light_eu_rl_1_2_bottom name: Light - Relay 1 of 2 (bottom) <<: *light_partition_relays @@ -187,6 +270,11 @@ light: - id: light_full from: 11 to: 11 + on_state: + - script.execute: + id: light_attributes_save + light_ptr: !lambda return id(light_eu_rl_1_2_bottom); + attr_global_ptr: !lambda return &id(attr_light_eu_rl_1_2_bottom); - id: light_eu_rl_1_2_top name: Light - Relay 1 of 2 (top) <<: *light_partition_relays @@ -194,6 +282,11 @@ light: - id: light_full from: 21 to: 21 + on_state: + - script.execute: + id: light_attributes_save + light_ptr: !lambda return id(light_eu_rl_1_2_top); + attr_global_ptr: !lambda return &id(attr_light_eu_rl_1_2_top); - id: light_eu_rl_2_2_bottom name: Light - Relay 2 of 2 (bottom) <<: *light_partition_relays @@ -201,6 +294,11 @@ light: - id: light_full from: 7 to: 7 + on_state: + - script.execute: + id: light_attributes_save + light_ptr: !lambda return id(light_eu_rl_2_2_bottom); + attr_global_ptr: !lambda return &id(attr_light_eu_rl_2_2_bottom); - id: light_eu_rl_2_2_top name: Light - Relay 2 of 2 (top) <<: *light_partition_relays @@ -208,6 +306,11 @@ light: - id: light_full from: 25 to: 25 + on_state: + - script.execute: + id: light_attributes_save + light_ptr: !lambda return id(light_eu_rl_2_2_top); + attr_global_ptr: !lambda return &id(attr_light_eu_rl_2_2_top); - id: light_eu_rl_1_3_bottom name: Light - Relay 1 of 3 (bottom) <<: *light_partition_relays @@ -215,6 +318,11 @@ light: - id: light_full from: 12 to: 12 + on_state: + - script.execute: + id: light_attributes_save + light_ptr: !lambda return id(light_eu_rl_1_3_bottom); + attr_global_ptr: !lambda return &id(attr_light_eu_rl_1_3_bottom); - id: light_eu_rl_1_3_top name: Light - Relay 1 of 3 (top) <<: *light_partition_relays @@ -222,6 +330,11 @@ light: - id: light_full from: 20 to: 20 + on_state: + - script.execute: + id: light_attributes_save + light_ptr: !lambda return id(light_eu_rl_1_3_top); + attr_global_ptr: !lambda return &id(attr_light_eu_rl_1_3_top); - id: light_eu_rl_2_3_bottom name: Light - Relay 2 of 3 (bottom) <<: *light_partition_relays @@ -229,6 +342,11 @@ light: - id: light_full from: 9 to: 9 + on_state: + - script.execute: + id: light_attributes_save + light_ptr: !lambda return id(light_eu_rl_2_3_bottom); + attr_global_ptr: !lambda return &id(attr_light_eu_rl_2_3_bottom); - id: light_eu_rl_2_3_top name: Light - Relay 2 of 3 (top) <<: *light_partition_relays @@ -236,6 +354,11 @@ light: - id: light_full from: 23 to: 23 + on_state: + - script.execute: + id: light_attributes_save + light_ptr: !lambda return id(light_eu_rl_2_3_top); + attr_global_ptr: !lambda return &id(attr_light_eu_rl_2_3_top); - id: light_eu_rl_3_3_bottom name: Light - Relay 3 of 3 (bottom) <<: *light_partition_relays @@ -243,6 +366,11 @@ light: - id: light_full from: 6 to: 6 + on_state: + - script.execute: + id: light_attributes_save + light_ptr: !lambda return id(light_eu_rl_3_3_bottom); + attr_global_ptr: !lambda return &id(attr_light_eu_rl_3_3_bottom); - id: light_eu_rl_3_3_top name: Light - Relay 3 of 3 (top) <<: *light_partition_relays @@ -250,6 +378,11 @@ light: - id: light_full from: 26 to: 26 + on_state: + - script.execute: + id: light_attributes_save + light_ptr: !lambda return id(light_eu_rl_3_3_top); + attr_global_ptr: !lambda return &id(attr_light_eu_rl_3_3_top); - id: light_eu_rl_1_4_bottom name: Light - Relay 1 of 4 (bottom) <<: *light_partition_relays @@ -257,6 +390,11 @@ light: - id: light_full from: 12 to: 12 + on_state: + - script.execute: + id: light_attributes_save + light_ptr: !lambda return id(light_eu_rl_1_4_bottom); + attr_global_ptr: !lambda return &id(attr_light_eu_rl_1_4_bottom); - id: light_eu_rl_1_4_top name: Light - Relay 1 of 4 (top) <<: *light_partition_relays @@ -264,6 +402,11 @@ light: - id: light_full from: 20 to: 20 + on_state: + - script.execute: + id: light_attributes_save + light_ptr: !lambda return id(light_eu_rl_1_4_top); + attr_global_ptr: !lambda return &id(attr_light_eu_rl_1_4_top); - id: light_eu_rl_2_4_bottom name: Light - Relay 2 of 4 (bottom) <<: *light_partition_relays @@ -271,6 +414,11 @@ light: - id: light_full from: 10 to: 10 + on_state: + - script.execute: + id: light_attributes_save + light_ptr: !lambda return id(light_eu_rl_2_4_bottom); + attr_global_ptr: !lambda return &id(attr_light_eu_rl_2_4_bottom); - id: light_eu_rl_2_4_top name: Light - Relay 2 of 4 (top) <<: *light_partition_relays @@ -278,6 +426,11 @@ light: - id: light_full from: 22 to: 22 + on_state: + - script.execute: + id: light_attributes_save + light_ptr: !lambda return id(light_eu_rl_2_4_top); + attr_global_ptr: !lambda return &id(attr_light_eu_rl_2_4_top); - id: light_eu_rl_3_4_bottom name: Light - Relay 3 of 4 (bottom) <<: *light_partition_relays @@ -285,6 +438,11 @@ light: - id: light_full from: 8 to: 8 + on_state: + - script.execute: + id: light_attributes_save + light_ptr: !lambda return id(light_eu_rl_3_4_bottom); + attr_global_ptr: !lambda return &id(attr_light_eu_rl_3_4_bottom); - id: light_eu_rl_3_4_top name: Light - Relay 3 of 4 (top) <<: *light_partition_relays @@ -292,6 +450,11 @@ light: - id: light_full from: 24 to: 24 + on_state: + - script.execute: + id: light_attributes_save + light_ptr: !lambda return id(light_eu_rl_3_4_top); + attr_global_ptr: !lambda return &id(attr_light_eu_rl_3_4_top); - id: light_eu_rl_4_4_bottom name: Light - Relay 4 of 4 (bottom) <<: *light_partition_relays @@ -299,6 +462,11 @@ light: - id: light_full from: 6 to: 6 + on_state: + - script.execute: + id: light_attributes_save + light_ptr: !lambda return id(light_eu_rl_4_4_bottom); + attr_global_ptr: !lambda return &id(attr_light_eu_rl_4_4_bottom); - id: light_eu_rl_4_4_top name: Light - Relay 4 of 4 (top) <<: *light_partition_relays @@ -306,6 +474,11 @@ light: - id: light_full from: 26 to: 26 + on_state: + - script.execute: + id: light_attributes_save + light_ptr: !lambda return id(light_eu_rl_4_4_top); + attr_global_ptr: !lambda return &id(attr_light_eu_rl_4_4_top); - id: light_us_rl_1_1_left name: Light - Relay 1 of 1 (left) <<: *light_partition_relays @@ -313,6 +486,11 @@ light: - id: light_full from: 15 to: 24 + on_state: + - script.execute: + id: light_attributes_save + light_ptr: !lambda return id(light_us_rl_1_1_left); + attr_global_ptr: !lambda return &id(attr_light_us_rl_1_1_left); - id: light_us_rl_1_1_right name: Light - Relay 1 of 1 (right) <<: *light_partition_relays @@ -323,6 +501,11 @@ light: - id: light_full from: 0 to: 8 + on_state: + - script.execute: + id: light_attributes_save + light_ptr: !lambda return id(light_us_rl_1_1_right); + attr_global_ptr: !lambda return &id(attr_light_us_rl_1_1_right); - id: light_us_rl_1_2_left name: Light - Relay 1 of 2 (left) <<: *light_partition_relays @@ -330,6 +513,11 @@ light: - id: light_full from: 20 to: 24 + on_state: + - script.execute: + id: light_attributes_save + light_ptr: !lambda return id(light_us_rl_1_2_left); + attr_global_ptr: !lambda return &id(attr_light_us_rl_1_2_left); - id: light_us_rl_1_2_right name: Light - Relay 1 of 2 (right) <<: *light_partition_relays @@ -340,6 +528,11 @@ light: - id: light_full from: 0 to: 3 + on_state: + - script.execute: + id: light_attributes_save + light_ptr: !lambda return id(light_us_rl_1_2_right); + attr_global_ptr: !lambda return &id(attr_light_us_rl_1_2_right); - id: light_us_rl_2_2_left name: Light - Relay 2 of 2 (left) <<: *light_partition_relays @@ -347,6 +540,11 @@ light: - id: light_full from: 15 to: 19 + on_state: + - script.execute: + id: light_attributes_save + light_ptr: !lambda return id(light_us_rl_2_2_left); + attr_global_ptr: !lambda return &id(attr_light_us_rl_2_2_left); - id: light_us_rl_2_2_right name: Light - Relay 2 of 2 (right) <<: *light_partition_relays @@ -354,6 +552,11 @@ light: - id: light_full from: 4 to: 8 + on_state: + - script.execute: + id: light_attributes_save + light_ptr: !lambda return id(light_us_rl_2_2_right); + attr_global_ptr: !lambda return &id(attr_light_us_rl_2_2_right); - id: light_us_rl_1_3_left name: Light - Relay 1 of 3 (left) <<: *light_partition_relays @@ -361,6 +564,11 @@ light: - id: light_full from: 22 to: 23 + on_state: + - script.execute: + id: light_attributes_save + light_ptr: !lambda return id(light_us_rl_1_3_left); + attr_global_ptr: !lambda return &id(attr_light_us_rl_1_3_left); - id: light_us_rl_1_3_right name: Light - Relay 1 of 3 (right) <<: *light_partition_relays @@ -368,6 +576,11 @@ light: - id: light_full from: 0 to: 1 + on_state: + - script.execute: + id: light_attributes_save + light_ptr: !lambda return id(light_us_rl_1_3_right); + attr_global_ptr: !lambda return &id(attr_light_us_rl_1_3_right); - id: light_us_rl_2_3_left name: Light - Relay 2 of 3 (left) <<: *light_partition_relays @@ -375,6 +588,11 @@ light: - id: light_full from: 19 to: 20 + on_state: + - script.execute: + id: light_attributes_save + light_ptr: !lambda return id(light_us_rl_2_3_left); + attr_global_ptr: !lambda return &id(attr_light_us_rl_2_3_left); - id: light_us_rl_2_3_right name: Light - Relay 2 of 3 (right) <<: *light_partition_relays @@ -382,6 +600,11 @@ light: - id: light_full from: 3 to: 4 + on_state: + - script.execute: + id: light_attributes_save + light_ptr: !lambda return id(light_us_rl_2_3_right); + attr_global_ptr: !lambda return &id(attr_light_us_rl_2_3_right); - id: light_us_rl_3_3_left name: Light - Relay 3 of 3 (left) <<: *light_partition_relays @@ -389,6 +612,11 @@ light: - id: light_full from: 16 to: 17 + on_state: + - script.execute: + id: light_attributes_save + light_ptr: !lambda return id(light_us_rl_3_3_left); + attr_global_ptr: !lambda return &id(attr_light_us_rl_3_3_left); - id: light_us_rl_3_3_right name: Light - Relay 3 of 3 (right) <<: *light_partition_relays @@ -396,6 +624,11 @@ light: - id: light_full from: 6 to: 7 + on_state: + - script.execute: + id: light_attributes_save + light_ptr: !lambda return id(light_us_rl_3_3_right); + attr_global_ptr: !lambda return &id(attr_light_us_rl_3_3_right); - id: light_us_rl_1_4_left name: Light - Relay 1 of 4 (left) <<: *light_partition_relays @@ -403,6 +636,11 @@ light: - id: light_full from: 23 to: 24 + on_state: + - script.execute: + id: light_attributes_save + light_ptr: !lambda return id(light_us_rl_1_4_left); + attr_global_ptr: !lambda return &id(attr_light_us_rl_1_4_left); - id: light_us_rl_1_4_right name: Light - Relay 1 of 4 (right) <<: *light_partition_relays @@ -413,6 +651,11 @@ light: - id: light_full from: 0 to: 0 + on_state: + - script.execute: + id: light_attributes_save + light_ptr: !lambda return id(light_us_rl_1_4_right); + attr_global_ptr: !lambda return &id(attr_light_us_rl_1_4_right); - id: light_us_rl_2_4_left name: Light - Relay 2 of 4 (left) <<: *light_partition_relays @@ -420,6 +663,11 @@ light: - id: light_full from: 21 to: 22 + on_state: + - script.execute: + id: light_attributes_save + light_ptr: !lambda return id(light_us_rl_2_4_left); + attr_global_ptr: !lambda return &id(attr_light_us_rl_2_4_left); - id: light_us_rl_2_4_right name: Light - Relay 2 of 4 (right) <<: *light_partition_relays @@ -427,6 +675,11 @@ light: - id: light_full from: 1 to: 2 + on_state: + - script.execute: + id: light_attributes_save + light_ptr: !lambda return id(light_us_rl_2_4_right); + attr_global_ptr: !lambda return &id(attr_light_us_rl_2_4_right); - id: light_us_rl_3_4_left name: Light - Relay 3 of 4 (left) <<: *light_partition_relays @@ -434,6 +687,11 @@ light: - id: light_full from: 17 to: 18 + on_state: + - script.execute: + id: light_attributes_save + light_ptr: !lambda return id(light_us_rl_3_4_left); + attr_global_ptr: !lambda return &id(attr_light_us_rl_3_4_left); - id: light_us_rl_3_4_right name: Light - Relay 3 of 4 (right) <<: *light_partition_relays @@ -441,6 +699,11 @@ light: - id: light_full from: 5 to: 6 + on_state: + - script.execute: + id: light_attributes_save + light_ptr: !lambda return id(light_us_rl_3_4_right); + attr_global_ptr: !lambda return &id(attr_light_us_rl_3_4_right); - id: light_us_rl_4_4_left name: Light - Relay 4 of 4 (left) <<: *light_partition_relays @@ -448,6 +711,11 @@ light: - id: light_full from: 15 to: 16 + on_state: + - script.execute: + id: light_attributes_save + light_ptr: !lambda return id(light_us_rl_4_4_left); + attr_global_ptr: !lambda return &id(attr_light_us_rl_4_4_left); - id: light_us_rl_4_4_right name: Light - Relay 4 of 4 (right) <<: *light_partition_relays @@ -455,6 +723,11 @@ light: - id: light_full from: 7 to: 8 + on_state: + - script.execute: + id: light_attributes_save + light_ptr: !lambda return id(light_us_rl_4_4_right); + attr_global_ptr: !lambda return &id(attr_light_us_rl_4_4_right); output: - id: output_relay_1 From 4634b650d9021d79a2dadd308adaa2aa24de498b Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Wed, 6 Aug 2025 16:30:40 +0200 Subject: [PATCH 19/50] Optimize nvs even more with dual light attributes saving --- ...TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml | 43 ++ ...imate-Easy-ESPHome_standard_hw_relays.yaml | 372 ++++++++++-------- .../tx_ultimate_easy_light.cpp | 51 +++ .../tx_ultimate_easy/tx_ultimate_easy_light.h | 113 ++++++ .../tx_ultimate_easy_touch.cpp | 227 +++++++++++ .../tx_ultimate_easy/tx_ultimate_easy_touch.h | 86 ++++ 6 files changed, 729 insertions(+), 163 deletions(-) create mode 100644 components/tx_ultimate_easy/tx_ultimate_easy_light.cpp create mode 100644 components/tx_ultimate_easy/tx_ultimate_easy_light.h create mode 100644 components/tx_ultimate_easy/tx_ultimate_easy_touch.cpp create mode 100644 components/tx_ultimate_easy/tx_ultimate_easy_touch.h diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml index 085f35b..1bbee37 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml @@ -30,6 +30,7 @@ substitutions: LIGHT_INDIVIDUAL_RESTORE_MODE: RESTORE_DEFAULT_OFF LIGHT_ATTRS_DEFAULT: '0x64FFFFFF' # brightness=100, RGB=255,255,255 + LIGHT_DUAL_ATTRS_DEFAULT: '0x64FFFFFF64FFFFFF' # Both lights: brightness=100, RGB=255,255,255 TAG_CORE_HW_LEDS: core.hw.leds @@ -559,6 +560,22 @@ script: ESP_LOGI("${TAG_CORE_HW_LEDS}", "Restored light attributes: brightness=%d%%, RGB=(%d,%d,%d)", attrs.brightness, attrs.red, attrs.green, attrs.blue); + - id: light_attributes_restore_dual + mode: parallel + parameters: + light_ptr: light::LightState* + attr_global_ptr: uint64_t* + group_index: uint8_t # 1 or 2 + then: + - lambda: |- + auto attrs_pair = tx_ultimate_easy::unpack_dual_light_attributes(*attr_global_ptr); + tx_ultimate_easy::LightAttributes attrs = (group_index == 1) ? attrs_pair.first : attrs_pair.second; + + auto call = light_ptr->make_call(); + call.set_brightness(attrs.brightness / 100.0f); + call.set_rgb(attrs.red / 255.0f, attrs.green / 255.0f, attrs.blue / 255.0f); + call.perform(); + - id: light_attributes_save mode: parallel parameters: @@ -578,6 +595,32 @@ script: ESP_LOGD("${TAG_CORE_HW_LEDS}", "Saved light attributes: brightness=%d%%, RGB=(%d,%d,%d)", attrs.brightness, attrs.red, attrs.green, attrs.blue); + - id: light_attributes_save_dual + mode: parallel + parameters: + light_ptr: light::LightState* + attr_global_ptr: uint64_t* + group_index: uint8_t # 1 or 2 (which group within the dual pack) + then: + - lambda: |- + auto current = light_ptr->current_values; + tx_ultimate_easy::LightAttributes new_attrs = { + .brightness = uint8_t(current.get_brightness() * 100.0f), + .red = uint8_t(current.get_red() * 255.0f), + .green = uint8_t(current.get_green() * 255.0f), + .blue = uint8_t(current.get_blue() * 255.0f) + }; + + // Unpack existing dual attributes + auto existing = tx_ultimate_easy::unpack_dual_light_attributes(*attr_global_ptr); + + // Update the appropriate group + if (group_index == 1) { + *attr_global_ptr = tx_ultimate_easy::pack_dual_light_attributes(new_attrs, existing.second); + } else { + *attr_global_ptr = tx_ultimate_easy::pack_dual_light_attributes(existing.first, new_attrs); + } + select: # EU relay light mode template - &relay_light_mode_eu diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml index 7772766..e67cd67 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml @@ -69,89 +69,29 @@ globals: initial_value: '0' # All switches default to OFF # Bit layout: [unused(3)][sw_relay4][sw_relay3][sw_relay2][sw_relay1][sw_expose_leds] - - &global_attr_light - id: attr_light_eu_rl_1_1_bottom - type: uint32_t + - &global_attr_dual_light + id: attr_light_rl_1_1_both # 1-gang: relay 1, both groups + type: uint64_t restore_value: true - initial_value: '${LIGHT_ATTRS_DEFAULT}' - - id: attr_light_eu_rl_1_1_top - <<: *global_attr_light - - id: attr_light_eu_rl_1_2_bottom - <<: *global_attr_light - - id: attr_light_eu_rl_1_2_top - <<: *global_attr_light - - id: attr_light_eu_rl_2_2_bottom - <<: *global_attr_light - - id: attr_light_eu_rl_2_2_top - <<: *global_attr_light - - id: attr_light_eu_rl_1_3_bottom - <<: *global_attr_light - - id: attr_light_eu_rl_1_3_top - <<: *global_attr_light - - id: attr_light_eu_rl_2_3_bottom - <<: *global_attr_light - - id: attr_light_eu_rl_2_3_top - <<: *global_attr_light - - id: attr_light_eu_rl_3_3_bottom - <<: *global_attr_light - - id: attr_light_eu_rl_3_3_top - <<: *global_attr_light - - id: attr_light_eu_rl_1_4_bottom - <<: *global_attr_light - - id: attr_light_eu_rl_1_4_top - <<: *global_attr_light - - id: attr_light_eu_rl_2_4_bottom - <<: *global_attr_light - - id: attr_light_eu_rl_2_4_top - <<: *global_attr_light - - id: attr_light_eu_rl_3_4_bottom - <<: *global_attr_light - - id: attr_light_eu_rl_3_4_top - <<: *global_attr_light - - id: attr_light_eu_rl_4_4_bottom - <<: *global_attr_light - - id: attr_light_eu_rl_4_4_top - <<: *global_attr_light - - id: attr_light_us_rl_1_1_left - <<: *global_attr_light - - id: attr_light_us_rl_1_1_right - <<: *global_attr_light - - id: attr_light_us_rl_1_2_left - <<: *global_attr_light - - id: attr_light_us_rl_1_2_right - <<: *global_attr_light - - id: attr_light_us_rl_2_2_left - <<: *global_attr_light - - id: attr_light_us_rl_2_2_right - <<: *global_attr_light - - id: attr_light_us_rl_1_3_left - <<: *global_attr_light - - id: attr_light_us_rl_1_3_right - <<: *global_attr_light - - id: attr_light_us_rl_2_3_left - <<: *global_attr_light - - id: attr_light_us_rl_2_3_right - <<: *global_attr_light - - id: attr_light_us_rl_3_3_left - <<: *global_attr_light - - id: attr_light_us_rl_3_3_right - <<: *global_attr_light - - id: attr_light_us_rl_1_4_left - <<: *global_attr_light - - id: attr_light_us_rl_1_4_right - <<: *global_attr_light - - id: attr_light_us_rl_2_4_left - <<: *global_attr_light - - id: attr_light_us_rl_2_4_right - <<: *global_attr_light - - id: attr_light_us_rl_3_4_left - <<: *global_attr_light - - id: attr_light_us_rl_3_4_right - <<: *global_attr_light - - id: attr_light_us_rl_4_4_left - <<: *global_attr_light - - id: attr_light_us_rl_4_4_right - <<: *global_attr_light + initial_value: '${LIGHT_DUAL_ATTRS_DEFAULT}' + - id: attr_light_rl_1_2_both # 2-gang: relay 1, both groups + <<: *global_attr_dual_light + - id: attr_light_rl_2_2_both # 2-gang: relay 2, both groups + <<: *global_attr_dual_light + - id: attr_light_rl_1_3_both # 3-gang: relay 1, both groups + <<: *global_attr_dual_light + - id: attr_light_rl_2_3_both # 3-gang: relay 2, both groups + <<: *global_attr_dual_light + - id: attr_light_rl_3_3_both # 3-gang: relay 3, both groups + <<: *global_attr_dual_light + - id: attr_light_rl_1_4_both # 4-gang: relay 1, both groups + <<: *global_attr_dual_light + - id: attr_light_rl_2_4_both # 4-gang: relay 2, both groups + <<: *global_attr_dual_light + - id: attr_light_rl_3_4_both # 4-gang: relay 3, both groups + <<: *global_attr_dual_light + - id: attr_light_rl_4_4_both # 4-gang: relay 4, both groups + <<: *global_attr_dual_light light: # These lights are used for the physical relays to be shown as lights @@ -248,9 +188,10 @@ light: to: 9 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_eu_rl_1_1_bottom); - attr_global_ptr: !lambda return &id(attr_light_eu_rl_1_1_bottom); + attr_global_ptr: !lambda return &id(attr_light_rl_1_1_both); + group_index: 1 - id: light_eu_rl_1_1_top name: Light - Relay 1 of 1 (top) <<: *light_partition_relays @@ -260,9 +201,10 @@ light: to: 23 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_eu_rl_1_1_top); - attr_global_ptr: !lambda return &id(attr_light_eu_rl_1_1_top); + attr_global_ptr: !lambda return &id(attr_light_rl_1_1_both); + group_index: 2 - id: light_eu_rl_1_2_bottom name: Light - Relay 1 of 2 (bottom) <<: *light_partition_relays @@ -272,9 +214,10 @@ light: to: 11 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_eu_rl_1_2_bottom); - attr_global_ptr: !lambda return &id(attr_light_eu_rl_1_2_bottom); + attr_global_ptr: !lambda return &id(attr_light_rl_1_2_both); + group_index: 1 - id: light_eu_rl_1_2_top name: Light - Relay 1 of 2 (top) <<: *light_partition_relays @@ -284,9 +227,10 @@ light: to: 21 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_eu_rl_1_2_top); - attr_global_ptr: !lambda return &id(attr_light_eu_rl_1_2_top); + attr_global_ptr: !lambda return &id(attr_light_rl_1_2_both); + group_index: 2 - id: light_eu_rl_2_2_bottom name: Light - Relay 2 of 2 (bottom) <<: *light_partition_relays @@ -296,9 +240,10 @@ light: to: 7 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_eu_rl_2_2_bottom); - attr_global_ptr: !lambda return &id(attr_light_eu_rl_2_2_bottom); + attr_global_ptr: !lambda return &id(attr_light_rl_2_2_both); + group_index: 1 - id: light_eu_rl_2_2_top name: Light - Relay 2 of 2 (top) <<: *light_partition_relays @@ -308,9 +253,10 @@ light: to: 25 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_eu_rl_2_2_top); - attr_global_ptr: !lambda return &id(attr_light_eu_rl_2_2_top); + attr_global_ptr: !lambda return &id(attr_light_rl_2_2_both); + group_index: 2 - id: light_eu_rl_1_3_bottom name: Light - Relay 1 of 3 (bottom) <<: *light_partition_relays @@ -320,9 +266,10 @@ light: to: 12 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_eu_rl_1_3_bottom); - attr_global_ptr: !lambda return &id(attr_light_eu_rl_1_3_bottom); + attr_global_ptr: !lambda return &id(attr_light_rl_1_3_both); + group_index: 1 - id: light_eu_rl_1_3_top name: Light - Relay 1 of 3 (top) <<: *light_partition_relays @@ -332,9 +279,10 @@ light: to: 20 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_eu_rl_1_3_top); - attr_global_ptr: !lambda return &id(attr_light_eu_rl_1_3_top); + attr_global_ptr: !lambda return &id(attr_light_rl_1_3_both); + group_index: 2 - id: light_eu_rl_2_3_bottom name: Light - Relay 2 of 3 (bottom) <<: *light_partition_relays @@ -344,9 +292,10 @@ light: to: 9 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_eu_rl_2_3_bottom); - attr_global_ptr: !lambda return &id(attr_light_eu_rl_2_3_bottom); + attr_global_ptr: !lambda return &id(attr_light_rl_2_3_both); + group_index: 1 - id: light_eu_rl_2_3_top name: Light - Relay 2 of 3 (top) <<: *light_partition_relays @@ -356,9 +305,10 @@ light: to: 23 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_eu_rl_2_3_top); - attr_global_ptr: !lambda return &id(attr_light_eu_rl_2_3_top); + attr_global_ptr: !lambda return &id(attr_light_rl_2_3_both); + group_index: 2 - id: light_eu_rl_3_3_bottom name: Light - Relay 3 of 3 (bottom) <<: *light_partition_relays @@ -368,9 +318,10 @@ light: to: 6 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_eu_rl_3_3_bottom); - attr_global_ptr: !lambda return &id(attr_light_eu_rl_3_3_bottom); + attr_global_ptr: !lambda return &id(attr_light_rl_3_3_both); + group_index: 1 - id: light_eu_rl_3_3_top name: Light - Relay 3 of 3 (top) <<: *light_partition_relays @@ -380,9 +331,10 @@ light: to: 26 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_eu_rl_3_3_top); - attr_global_ptr: !lambda return &id(attr_light_eu_rl_3_3_top); + attr_global_ptr: !lambda return &id(attr_light_rl_3_3_both); + group_index: 2 - id: light_eu_rl_1_4_bottom name: Light - Relay 1 of 4 (bottom) <<: *light_partition_relays @@ -392,9 +344,10 @@ light: to: 12 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_eu_rl_1_4_bottom); - attr_global_ptr: !lambda return &id(attr_light_eu_rl_1_4_bottom); + attr_global_ptr: !lambda return &id(attr_light_rl_1_4_both); + group_index: 1 - id: light_eu_rl_1_4_top name: Light - Relay 1 of 4 (top) <<: *light_partition_relays @@ -404,9 +357,10 @@ light: to: 20 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_eu_rl_1_4_top); - attr_global_ptr: !lambda return &id(attr_light_eu_rl_1_4_top); + attr_global_ptr: !lambda return &id(attr_light_rl_1_4_both); + group_index: 2 - id: light_eu_rl_2_4_bottom name: Light - Relay 2 of 4 (bottom) <<: *light_partition_relays @@ -416,9 +370,10 @@ light: to: 10 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_eu_rl_2_4_bottom); - attr_global_ptr: !lambda return &id(attr_light_eu_rl_2_4_bottom); + attr_global_ptr: !lambda return &id(attr_light_rl_2_4_both); + group_index: 1 - id: light_eu_rl_2_4_top name: Light - Relay 2 of 4 (top) <<: *light_partition_relays @@ -428,9 +383,10 @@ light: to: 22 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_eu_rl_2_4_top); - attr_global_ptr: !lambda return &id(attr_light_eu_rl_2_4_top); + attr_global_ptr: !lambda return &id(attr_light_rl_2_4_both); + group_index: 2 - id: light_eu_rl_3_4_bottom name: Light - Relay 3 of 4 (bottom) <<: *light_partition_relays @@ -440,9 +396,10 @@ light: to: 8 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_eu_rl_3_4_bottom); - attr_global_ptr: !lambda return &id(attr_light_eu_rl_3_4_bottom); + attr_global_ptr: !lambda return &id(attr_light_rl_3_4_both); + group_index: 1 - id: light_eu_rl_3_4_top name: Light - Relay 3 of 4 (top) <<: *light_partition_relays @@ -452,9 +409,10 @@ light: to: 24 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_eu_rl_3_4_top); - attr_global_ptr: !lambda return &id(attr_light_eu_rl_3_4_top); + attr_global_ptr: !lambda return &id(attr_light_rl_3_4_both); + group_index: 2 - id: light_eu_rl_4_4_bottom name: Light - Relay 4 of 4 (bottom) <<: *light_partition_relays @@ -464,9 +422,10 @@ light: to: 6 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_eu_rl_4_4_bottom); - attr_global_ptr: !lambda return &id(attr_light_eu_rl_4_4_bottom); + attr_global_ptr: !lambda return &id(attr_light_rl_4_4_both); + group_index: 1 - id: light_eu_rl_4_4_top name: Light - Relay 4 of 4 (top) <<: *light_partition_relays @@ -476,9 +435,10 @@ light: to: 26 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_eu_rl_4_4_top); - attr_global_ptr: !lambda return &id(attr_light_eu_rl_4_4_top); + attr_global_ptr: !lambda return &id(attr_light_rl_4_4_both); + group_index: 2 - id: light_us_rl_1_1_left name: Light - Relay 1 of 1 (left) <<: *light_partition_relays @@ -488,9 +448,10 @@ light: to: 24 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_us_rl_1_1_left); - attr_global_ptr: !lambda return &id(attr_light_us_rl_1_1_left); + attr_global_ptr: !lambda return &id(attr_light_rl_1_1_both); + group_index: 1 - id: light_us_rl_1_1_right name: Light - Relay 1 of 1 (right) <<: *light_partition_relays @@ -503,9 +464,10 @@ light: to: 8 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_us_rl_1_1_right); - attr_global_ptr: !lambda return &id(attr_light_us_rl_1_1_right); + attr_global_ptr: !lambda return &id(attr_light_rl_1_1_both); + group_index: 2 - id: light_us_rl_1_2_left name: Light - Relay 1 of 2 (left) <<: *light_partition_relays @@ -515,9 +477,10 @@ light: to: 24 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_us_rl_1_2_left); - attr_global_ptr: !lambda return &id(attr_light_us_rl_1_2_left); + attr_global_ptr: !lambda return &id(attr_light_rl_1_2_both); + group_index: 1 - id: light_us_rl_1_2_right name: Light - Relay 1 of 2 (right) <<: *light_partition_relays @@ -530,9 +493,10 @@ light: to: 3 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_us_rl_1_2_right); - attr_global_ptr: !lambda return &id(attr_light_us_rl_1_2_right); + attr_global_ptr: !lambda return &id(attr_light_rl_1_2_both); + group_index: 2 - id: light_us_rl_2_2_left name: Light - Relay 2 of 2 (left) <<: *light_partition_relays @@ -542,9 +506,10 @@ light: to: 19 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_us_rl_2_2_left); - attr_global_ptr: !lambda return &id(attr_light_us_rl_2_2_left); + attr_global_ptr: !lambda return &id(attr_light_rl_2_2_both); + group_index: 1 - id: light_us_rl_2_2_right name: Light - Relay 2 of 2 (right) <<: *light_partition_relays @@ -554,9 +519,10 @@ light: to: 8 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_us_rl_2_2_right); - attr_global_ptr: !lambda return &id(attr_light_us_rl_2_2_right); + attr_global_ptr: !lambda return &id(attr_light_rl_2_2_both); + group_index: 2 - id: light_us_rl_1_3_left name: Light - Relay 1 of 3 (left) <<: *light_partition_relays @@ -566,9 +532,10 @@ light: to: 23 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_us_rl_1_3_left); - attr_global_ptr: !lambda return &id(attr_light_us_rl_1_3_left); + attr_global_ptr: !lambda return &id(attr_light_rl_1_3_both); + group_index: 1 - id: light_us_rl_1_3_right name: Light - Relay 1 of 3 (right) <<: *light_partition_relays @@ -578,9 +545,10 @@ light: to: 1 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_us_rl_1_3_right); - attr_global_ptr: !lambda return &id(attr_light_us_rl_1_3_right); + attr_global_ptr: !lambda return &id(attr_light_rl_1_3_both); + group_index: 2 - id: light_us_rl_2_3_left name: Light - Relay 2 of 3 (left) <<: *light_partition_relays @@ -590,9 +558,10 @@ light: to: 20 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_us_rl_2_3_left); - attr_global_ptr: !lambda return &id(attr_light_us_rl_2_3_left); + attr_global_ptr: !lambda return &id(attr_light_rl_2_3_both); + group_index: 1 - id: light_us_rl_2_3_right name: Light - Relay 2 of 3 (right) <<: *light_partition_relays @@ -602,9 +571,10 @@ light: to: 4 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_us_rl_2_3_right); - attr_global_ptr: !lambda return &id(attr_light_us_rl_2_3_right); + attr_global_ptr: !lambda return &id(attr_light_rl_2_3_both); + group_index: 2 - id: light_us_rl_3_3_left name: Light - Relay 3 of 3 (left) <<: *light_partition_relays @@ -614,9 +584,10 @@ light: to: 17 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_us_rl_3_3_left); - attr_global_ptr: !lambda return &id(attr_light_us_rl_3_3_left); + attr_global_ptr: !lambda return &id(attr_light_rl_3_3_both); + group_index: 1 - id: light_us_rl_3_3_right name: Light - Relay 3 of 3 (right) <<: *light_partition_relays @@ -626,9 +597,10 @@ light: to: 7 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_us_rl_3_3_right); - attr_global_ptr: !lambda return &id(attr_light_us_rl_3_3_right); + attr_global_ptr: !lambda return &id(attr_light_rl_3_3_both); + group_index: 2 - id: light_us_rl_1_4_left name: Light - Relay 1 of 4 (left) <<: *light_partition_relays @@ -638,9 +610,10 @@ light: to: 24 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_us_rl_1_4_left); - attr_global_ptr: !lambda return &id(attr_light_us_rl_1_4_left); + attr_global_ptr: !lambda return &id(attr_light_rl_1_4_both); + group_index: 1 - id: light_us_rl_1_4_right name: Light - Relay 1 of 4 (right) <<: *light_partition_relays @@ -653,9 +626,10 @@ light: to: 0 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_us_rl_1_4_right); - attr_global_ptr: !lambda return &id(attr_light_us_rl_1_4_right); + attr_global_ptr: !lambda return &id(attr_light_rl_1_4_both); + group_index: 2 - id: light_us_rl_2_4_left name: Light - Relay 2 of 4 (left) <<: *light_partition_relays @@ -665,9 +639,10 @@ light: to: 22 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_us_rl_2_4_left); - attr_global_ptr: !lambda return &id(attr_light_us_rl_2_4_left); + attr_global_ptr: !lambda return &id(attr_light_rl_2_4_both); + group_index: 1 - id: light_us_rl_2_4_right name: Light - Relay 2 of 4 (right) <<: *light_partition_relays @@ -677,9 +652,10 @@ light: to: 2 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_us_rl_2_4_right); - attr_global_ptr: !lambda return &id(attr_light_us_rl_2_4_right); + attr_global_ptr: !lambda return &id(attr_light_rl_2_4_both); + group_index: 2 - id: light_us_rl_3_4_left name: Light - Relay 3 of 4 (left) <<: *light_partition_relays @@ -689,9 +665,10 @@ light: to: 18 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_us_rl_3_4_left); - attr_global_ptr: !lambda return &id(attr_light_us_rl_3_4_left); + attr_global_ptr: !lambda return &id(attr_light_rl_3_4_both); + group_index: 1 - id: light_us_rl_3_4_right name: Light - Relay 3 of 4 (right) <<: *light_partition_relays @@ -701,9 +678,10 @@ light: to: 6 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_us_rl_3_4_right); - attr_global_ptr: !lambda return &id(attr_light_us_rl_3_4_right); + attr_global_ptr: !lambda return &id(attr_light_rl_3_4_both); + group_index: 2 - id: light_us_rl_4_4_left name: Light - Relay 4 of 4 (left) <<: *light_partition_relays @@ -713,9 +691,10 @@ light: to: 16 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_us_rl_4_4_left); - attr_global_ptr: !lambda return &id(attr_light_us_rl_4_4_left); + attr_global_ptr: !lambda return &id(attr_light_rl_4_4_both); + group_index: 1 - id: light_us_rl_4_4_right name: Light - Relay 4 of 4 (right) <<: *light_partition_relays @@ -725,9 +704,10 @@ light: to: 8 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_us_rl_4_4_right); - attr_global_ptr: !lambda return &id(attr_light_us_rl_4_4_right); + attr_global_ptr: !lambda return &id(attr_light_rl_4_4_both); + group_index: 2 output: - id: output_relay_1 @@ -1144,7 +1124,7 @@ script: id: sl_relay_4_mode index: !lambda return std::min((uint8_t)((id(relay_modes_packed) >> 12) & 0x0F), (uint8_t)2); - # Switch states (new - your improved version) + # Switch states - if: condition: lambda: return (id(relay_switches_packed) & 0x01) == 0; @@ -1184,6 +1164,72 @@ script: # update lights based on new relay statuses (catch up, as this should be done when each relay is updated) - script.execute: show_relay_status + # Light attributes restoration + - lambda: |- + // Restore all light attributes based on current model and gang count + switch (id(gang_count)) { + case 1: + if (id(is_us_model)) { + light_attributes_restore_dual->execute(id(light_us_rl_1_1_left), &id(attr_light_rl_1_1_both), 1); + light_attributes_restore_dual->execute(id(light_us_rl_1_1_right), &id(attr_light_rl_1_1_both), 2); + } else { + light_attributes_restore_dual->execute(id(light_eu_rl_1_1_bottom), &id(attr_light_rl_1_1_both), 1); + light_attributes_restore_dual->execute(id(light_eu_rl_1_1_top), &id(attr_light_rl_1_1_both), 2); + } + break; + case 2: + if (id(is_us_model)) { + light_attributes_restore_dual->execute(id(light_us_rl_1_2_left), &id(attr_light_rl_1_2_both), 1); + light_attributes_restore_dual->execute(id(light_us_rl_1_2_right), &id(attr_light_rl_1_2_both), 2); + light_attributes_restore_dual->execute(id(light_us_rl_2_2_left), &id(attr_light_rl_2_2_both), 1); + light_attributes_restore_dual->execute(id(light_us_rl_2_2_right), &id(attr_light_rl_2_2_both), 2); + } else { + light_attributes_restore_dual->execute(id(light_eu_rl_1_2_bottom), &id(attr_light_rl_1_2_both), 1); + light_attributes_restore_dual->execute(id(light_eu_rl_1_2_top), &id(attr_light_rl_1_2_both), 2); + light_attributes_restore_dual->execute(id(light_eu_rl_2_2_bottom), &id(attr_light_rl_2_2_both), 1); + light_attributes_restore_dual->execute(id(light_eu_rl_2_2_top), &id(attr_light_rl_2_2_both), 2); + } + break; + case 3: + if (id(is_us_model)) { + light_attributes_restore_dual->execute(id(light_us_rl_1_3_left), &id(attr_light_rl_1_3_both), 1); + light_attributes_restore_dual->execute(id(light_us_rl_1_3_right), &id(attr_light_rl_1_3_both), 2); + light_attributes_restore_dual->execute(id(light_us_rl_2_3_left), &id(attr_light_rl_2_3_both), 1); + light_attributes_restore_dual->execute(id(light_us_rl_2_3_right), &id(attr_light_rl_2_3_both), 2); + light_attributes_restore_dual->execute(id(light_us_rl_3_3_left), &id(attr_light_rl_3_3_both), 1); + light_attributes_restore_dual->execute(id(light_us_rl_3_3_right), &id(attr_light_rl_3_3_both), 2); + } else { + light_attributes_restore_dual->execute(id(light_eu_rl_1_3_bottom), &id(attr_light_rl_1_3_both), 1); + light_attributes_restore_dual->execute(id(light_eu_rl_1_3_top), &id(attr_light_rl_1_3_both), 2); + light_attributes_restore_dual->execute(id(light_eu_rl_2_3_bottom), &id(attr_light_rl_2_3_both), 1); + light_attributes_restore_dual->execute(id(light_eu_rl_2_3_top), &id(attr_light_rl_2_3_both), 2); + light_attributes_restore_dual->execute(id(light_eu_rl_3_3_bottom), &id(attr_light_rl_3_3_both), 1); + light_attributes_restore_dual->execute(id(light_eu_rl_3_3_top), &id(attr_light_rl_3_3_both), 2); + } + break; + case 3: + if (id(is_us_model)) { + light_attributes_restore_dual->execute(id(light_us_rl_1_4_left), &id(attr_light_rl_1_4_both), 1); + light_attributes_restore_dual->execute(id(light_us_rl_1_4_right), &id(attr_light_rl_1_4_both), 2); + light_attributes_restore_dual->execute(id(light_us_rl_2_4_left), &id(attr_light_rl_2_4_both), 1); + light_attributes_restore_dual->execute(id(light_us_rl_2_4_right), &id(attr_light_rl_2_4_both), 2); + light_attributes_restore_dual->execute(id(light_us_rl_3_4_left), &id(attr_light_rl_3_4_both), 1); + light_attributes_restore_dual->execute(id(light_us_rl_3_4_right), &id(attr_light_rl_3_4_both), 2); + light_attributes_restore_dual->execute(id(light_us_rl_4_4_left), &id(attr_light_rl_4_4_both), 1); + light_attributes_restore_dual->execute(id(light_us_rl_4_4_right), &id(attr_light_rl_4_4_both), 2); + } else { + light_attributes_restore_dual->execute(id(light_eu_rl_1_4_bottom), &id(attr_light_rl_1_4_both), 1); + light_attributes_restore_dual->execute(id(light_eu_rl_1_4_top), &id(attr_light_rl_1_4_both), 2); + light_attributes_restore_dual->execute(id(light_eu_rl_2_4_bottom), &id(attr_light_rl_2_4_both), 1); + light_attributes_restore_dual->execute(id(light_eu_rl_2_4_top), &id(attr_light_rl_2_4_both), 2); + light_attributes_restore_dual->execute(id(light_eu_rl_3_4_bottom), &id(attr_light_rl_3_4_both), 1); + light_attributes_restore_dual->execute(id(light_eu_rl_3_4_top), &id(attr_light_rl_3_4_both), 2); + light_attributes_restore_dual->execute(id(light_eu_rl_4_4_bottom), &id(attr_light_rl_4_4_both), 1); + light_attributes_restore_dual->execute(id(light_eu_rl_4_4_top), &id(attr_light_rl_4_4_both), 2); + } + break; + } + - id: show_relay_status mode: restart then: diff --git a/components/tx_ultimate_easy/tx_ultimate_easy_light.cpp b/components/tx_ultimate_easy/tx_ultimate_easy_light.cpp new file mode 100644 index 0000000..cd7a290 --- /dev/null +++ b/components/tx_ultimate_easy/tx_ultimate_easy_light.cpp @@ -0,0 +1,51 @@ +// tx_ultimate_easy_light.cpp + +#include "esphome/core/log.h" +#include "tx_ultimate_easy_light.h" +#include +#include + +namespace esphome { + namespace tx_ultimate_easy { + + // Constant definitions + const LightAttributes LIGHT_ATTRS_DEFAULT = {100, 255, 255, 255}; + const uint32_t LIGHT_ATTRS_DEFAULT_PACKED = 0x64FFFFFF; + const uint64_t DEFAULT_DUAL_LIGHT_ATTRS_PACKED = 0x64FFFFFF64FFFFFF; + + // Function implementations + uint32_t pack_light_attributes(const LightAttributes& attr) { + return (uint32_t(attr.brightness) << 24) | + (uint32_t(attr.red) << 16) | + (uint32_t(attr.green) << 8) | + (uint32_t(attr.blue)); + } + + LightAttributes unpack_light_attributes(uint32_t packed) { + return { + .brightness = uint8_t((packed >> 24) & 0xFF), + .red = uint8_t((packed >> 16) & 0xFF), + .green = uint8_t((packed >> 8) & 0xFF), + .blue = uint8_t(packed & 0xFF) + }; + } + + uint64_t pack_dual_light_attributes(const LightAttributes& group1_attrs, const LightAttributes& group2_attrs) { + uint32_t group1_packed = pack_light_attributes(group1_attrs); + uint32_t group2_packed = pack_light_attributes(group2_attrs); + + return (uint64_t(group2_packed) << 32) | uint64_t(group1_packed); + } + + std::pair unpack_dual_light_attributes(uint64_t packed) { + uint32_t group1_packed = uint32_t(packed & 0xFFFFFFFF); // Lower 32 bits + uint32_t group2_packed = uint32_t((packed >> 32) & 0xFFFFFFFF); // Upper 32 bits + + return std::make_pair( + unpack_light_attributes(group1_packed), + unpack_light_attributes(group2_packed) + ); + } + + } // namespace tx_ultimate_easy +} // namespace esphome diff --git a/components/tx_ultimate_easy/tx_ultimate_easy_light.h b/components/tx_ultimate_easy/tx_ultimate_easy_light.h new file mode 100644 index 0000000..3a892c3 --- /dev/null +++ b/components/tx_ultimate_easy/tx_ultimate_easy_light.h @@ -0,0 +1,113 @@ +// tx_ultimate_easy_light.h + +#pragma once + +#include "esphome/core/automation.h" +#include "esphome/core/component.h" +#include "esphome/components/uart/uart.h" +#include "esphome/components/uart/uart_component.h" +#include "esphome/components/binary_sensor/binary_sensor.h" +#include "esphome/components/switch/switch.h" +#include "esphome/components/light/addressable_light.h" +#include + +namespace esphome { + namespace tx_ultimate_easy { + + /** + * @brief Light attributes structure for storing color and brightness data. + * + * This structure represents the visual attributes of a light without the state (on/off). + * It's designed to be packed efficiently into a 32-bit integer for NVS storage. + * RGB components use the full 8-bit range (0-255) while brightness uses percentage (0-100). + */ + struct LightAttributes { + uint8_t brightness; ///< Light brightness percentage (0-100, where 100 = full brightness) + uint8_t red; ///< Red color component (0-255) + uint8_t green; ///< Green color component (0-255) + uint8_t blue; ///< Blue color component (0-255) + }; + + /** + * @brief Packs LightAttributes structure into a 32-bit integer for storage. + * + * Bit layout: [brightness(8)][red(8)][green(8)][blue(8)] + * This allows efficient storage in ESPHome globals with restore_value support. + * Note: Brightness values > 100 will be clamped to 100 during packing. + * + * @param attr The LightAttributes structure to pack + * @return uint32_t Packed representation suitable for NVS storage + * + * @example + * LightAttributes attrs = {75, 255, 128, 64}; + * uint32_t packed = pack_light_attributes(attrs); // Returns 0x4BFF8040 + */ + uint32_t pack_light_attributes(const LightAttributes& attr); + + /** + * @brief Unpacks a 32-bit integer back into LightAttributes structure. + * + * Reverses the packing operation to extract individual color and brightness values. + * Brightness values > 100 in the packed data will be clamped to 100. + * + * @param packed The packed 32-bit representation + * @return LightAttributes The unpacked structure with individual components + * + * @example + * uint32_t packed = 0x4BFF8040; + * LightAttributes attrs = unpack_light_attributes(packed); + * // attrs.brightness = 75, attrs.red = 255, attrs.green = 128, attrs.blue = 64 + */ + LightAttributes unpack_light_attributes(uint32_t packed); + + /** + * @brief Default light attributes for initialization. + * + * Provides sensible defaults for new lights: + * - Brightness: 100 (full brightness) + * - Red: 255 (full intensity) + * - Green: 255 (full intensity) + * - Blue: 255 (full intensity) + * Result: Pure white color at full brightness + */ + extern const LightAttributes LIGHT_ATTRS_DEFAULT; + + /** + * @brief Pre-packed version of DEFAULT_LIGHT_ATTRS for convenience. + * + * This constant contains LIGHT_ATTRS_DEFAULT already packed into uint32_t format, + * suitable for direct use in ESPHome YAML initial_value fields. + * + * Value: 0x64FFFFFF (brightness=100, RGB=255,255,255) + */ + extern const uint32_t LIGHT_ATTRS_DEFAULT_PACKED; + + /** + * @brief Packs two LightAttributes into a single uint64_t for dual light storage. + * + * Bit layout: [group2_light(32)][group1_light(32)] + * Each light uses 32 bits: [brightness(8)][red(8)][green(8)][blue(8)] + * + * @param group1_attrs First light attributes (stored in lower 32 bits) + * @param group2_attrs Second light attributes (stored in upper 32 bits) + * @return uint64_t Packed representation suitable for NVS storage + */ + uint64_t pack_dual_light_attributes(const LightAttributes& group1_attrs, const LightAttributes& group2_attrs); + + /** + * @brief Unpacks uint64_t back into two LightAttributes structures. + * + * @param packed The packed 64-bit representation + * @return std::pair Pair of (group1, group2) attributes + */ + std::pair unpack_dual_light_attributes(uint64_t packed); + + /** + * @brief Pre-packed version of dual default attributes for convenience. + * + * Value: 0x64FFFFFF64FFFFFF (both lights: brightness=100, RGB=255,255,255) + */ + extern const uint64_t DEFAULT_DUAL_LIGHT_ATTRS_PACKED; + + } // namespace tx_ultimate_easy +} // namespace esphome diff --git a/components/tx_ultimate_easy/tx_ultimate_easy_touch.cpp b/components/tx_ultimate_easy/tx_ultimate_easy_touch.cpp new file mode 100644 index 0000000..92ad4cd --- /dev/null +++ b/components/tx_ultimate_easy/tx_ultimate_easy_touch.cpp @@ -0,0 +1,227 @@ +// tx_ultimate_easy_touch.cpp + +#include "esphome/core/log.h" +#include "tx_ultimate_easy_touch.h" +#include +#include + +namespace esphome { + namespace tx_ultimate_easy { + + void TxUltimateEasy::setup() { + ESP_LOGI(TAG, "TX Ultimate Easy is initialized"); + } + + void TxUltimateEasy::loop() { + bool found = false; + std::array uart_received_bytes{}; + int byte = -1; + int i = 0; + + while (this->available()) { + byte = this->read(); + if (byte == HEADER_BYTE_1) { + this->handle_touch(uart_received_bytes); + i = 0; + } + if (i < UART_RECEIVED_BYTES_SIZE) { + uart_received_bytes[i] = byte; + i++; + } + if (byte != 0x00) { + found = true; + } + } + if (found) this->handle_touch(uart_received_bytes); + } + + void TxUltimateEasy::handle_touch(const std::array &uart_received_bytes) { + ESP_LOGV(TAG, "------------"); + ESP_LOGV(TAG, "- UART-Log -"); + ESP_LOGV(TAG, "------------"); + for (int i = 0; i < UART_RECEIVED_BYTES_SIZE; i++) { + ESP_LOGV(TAG, "UART - Log - Byte[%i]: %i", i, uart_received_bytes[i]); + } + if (this->is_valid_data(uart_received_bytes)) { + this->send_touch_(this->get_touch_point(uart_received_bytes)); + } + } + + void TxUltimateEasy::dump_config() { + ESP_LOGCONFIG(TAG, "TX Ultimate Easy"); + ESP_LOGCONFIG(TAG, " Gang count: %" PRIu8, this->gang_count_); + } + + bool TxUltimateEasy::set_gang_count(const uint8_t gang_count) { + // Hardware supports maximum of 4 touch-sensitive buttons + if (gang_count < 1 or gang_count > 4) + return false; + this->gang_count_ = gang_count; + return true; + } + + uint8_t TxUltimateEasy::get_button_from_position(const uint8_t position) { + // Validate position bounds + if (position > TOUCH_MAX_POSITION) + return 0; + + // Special case for single gang (only one button exists) + if (this->gang_count_ == 1) + return 1; + + // Calculate button number Change to round up instead of truncate (integer division) + const uint8_t width = (TOUCH_MAX_POSITION + gang_count_) / this->gang_count_; // Width of each button region + if (width < 1) // Invalid width - and prevents division by zero + return 0; + const uint8_t button = std::min( + static_cast((position / width) + 1), // Convert position to button index + this->gang_count_ // Clamp to max gang count + ); + return button; + } + + void TxUltimateEasy::send_touch_(TouchPoint tp) { + this->trigger_touch_event_.trigger(tp); + switch (tp.state) { + case TOUCH_STATE_RELEASE: + if (tp.x >= 17) { + tp.x -= 16; + ESP_LOGV(TAG, "Long touch - Released (x=%d)", tp.x); + this->trigger_long_touch_release_.trigger(tp); + } else { + ESP_LOGV(TAG, "Touch - Released (x=%d)", tp.x); + this->trigger_release_.trigger(tp); + } + break; + + case TOUCH_STATE_PRESS: + ESP_LOGV(TAG, "Touch - Pressed (x=%d)", tp.x); + this->trigger_touch_.trigger(tp); + break; + + case TOUCH_STATE_SWIPE_LEFT: + ESP_LOGV(TAG, "Swipe - Left (x=%d)", tp.x); + this->trigger_swipe_left_.trigger(tp); + break; + + case TOUCH_STATE_SWIPE_RIGHT: + ESP_LOGV(TAG, "Swipe - Right (x=%d)", tp.x); + this->trigger_swipe_right_.trigger(tp); + break; + + case TOUCH_STATE_MULTI_TOUCH: + ESP_LOGV(TAG, "Multi touch - Released"); + this->trigger_multi_touch_release_.trigger(tp); + break; + + default: + break; + } + } + + bool TxUltimateEasy::is_valid_data(const std::array &uart_received_bytes) { + if (uart_received_bytes[0] != HEADER_BYTE_1 || + uart_received_bytes[1] != HEADER_BYTE_2 || + uart_received_bytes[2] != VALID_DATA_BYTE_2 || + uart_received_bytes[3] != VALID_DATA_BYTE_3) { + return false; + } + + int state = this->get_touch_state(uart_received_bytes); + return (state == TOUCH_STATE_PRESS || + state == TOUCH_STATE_RELEASE || + state == TOUCH_STATE_SWIPE_LEFT || + state == TOUCH_STATE_SWIPE_RIGHT || + state == TOUCH_STATE_MULTI_TOUCH) && + // Multi-touch events may have x < 0, all other events require valid x position + (uart_received_bytes[6] >= 0 || state == TOUCH_STATE_MULTI_TOUCH); + } + + int TxUltimateEasy::get_touch_position_x(const std::array &uart_received_bytes) { + switch (uart_received_bytes[4]) { + case TOUCH_STATE_RELEASE: + case TOUCH_STATE_MULTI_TOUCH: + return uart_received_bytes[5]; + + case TOUCH_STATE_SWIPE_LEFT: + case TOUCH_STATE_SWIPE_RIGHT: + // uart_received_bytes[5] indicates swipe direction: + // 12 = find last touch position (scan right to left) + // 13 = find first touch position (scan left to right) + // Bytes 6-7 contain a 10-bit bitmap of touch positions: + // Byte 7: bits 0-7 represent positions 1-8 + // Byte 6: bits 0-1 represent positions 9-10 + switch (uart_received_bytes[5]) { + case 12: + for (uint8_t i = 10; i > 0; i--) { + if (i > 8 + ? uart_received_bytes[6] & (1 << (i - 9)) + : uart_received_bytes[7] & (1 << (i - 1))) { + return i; + } + } + break; + case 13: + for (uint8_t i = 1; i <= 10; i++) { + if (i > 8 + ? uart_received_bytes[6] & (1 << (i - 9)) + : uart_received_bytes[7] & (1 << (i - 1))) { + return i; + } + } + break; + } + return uart_received_bytes[5]; + + default: + return uart_received_bytes[6]; + } + } + + int TxUltimateEasy::get_touch_state(const std::array &uart_received_bytes) { + int state = uart_received_bytes[4]; + if (state == TOUCH_STATE_PRESS && uart_received_bytes[5] != 0) + state = TOUCH_STATE_RELEASE; + if (state == TOUCH_STATE_RELEASE && uart_received_bytes[5] == TOUCH_STATE_MULTI_TOUCH) + state = TOUCH_STATE_MULTI_TOUCH; + if (state == TOUCH_STATE_SWIPE) { + state = (uart_received_bytes[5] == TOUCH_STATE_SWIPE_RIGHT) ? TOUCH_STATE_SWIPE_RIGHT : + (uart_received_bytes[5] == TOUCH_STATE_SWIPE_LEFT) ? TOUCH_STATE_SWIPE_LEFT : state; + } + return state; + } + + TouchPoint TxUltimateEasy::get_touch_point(const std::array &uart_received_bytes) { + TouchPoint tp; + tp.x = this->get_touch_position_x(uart_received_bytes); + if (tp.x >= 0) + tp.button = this->get_button_from_position(static_cast(tp.x)); + tp.state = this->get_touch_state(uart_received_bytes); + switch (tp.state) { + case TOUCH_STATE_RELEASE: + tp.state_str = "RELEASE"; + break; + case TOUCH_STATE_PRESS: + tp.state_str = "PRESS"; + break; + case TOUCH_STATE_SWIPE: + tp.state_str = "SWIPE"; + break; + case TOUCH_STATE_MULTI_TOUCH: + tp.state_str = "MULTI_TOUCH"; + break; + case TOUCH_STATE_SWIPE_RIGHT: + tp.state_str = "SWIPE_RIGHT"; + break; + case TOUCH_STATE_SWIPE_LEFT: + tp.state_str = "SWIPE_LEFT"; + break; + default: + tp.state_str = "Unknown"; + break; + } + return tp; + } + + } // namespace tx_ultimate_easy +} // namespace esphome diff --git a/components/tx_ultimate_easy/tx_ultimate_easy_touch.h b/components/tx_ultimate_easy/tx_ultimate_easy_touch.h new file mode 100644 index 0000000..08086a3 --- /dev/null +++ b/components/tx_ultimate_easy/tx_ultimate_easy_touch.h @@ -0,0 +1,86 @@ +// tx_ultimate_easy_touch.h + +#pragma once + +#include "esphome/core/automation.h" +#include "esphome/core/component.h" +#include "esphome/components/uart/uart.h" +#include "esphome/components/uart/uart_component.h" +#include "esphome/components/binary_sensor/binary_sensor.h" +#include "esphome/components/switch/switch.h" +#include "esphome/components/light/addressable_light.h" +#include + +namespace esphome { + namespace tx_ultimate_easy { + // Touch Max Position + constexpr uint8_t TOUCH_MAX_POSITION = 10; + + // Touch State Constants + constexpr uint8_t TOUCH_STATE_RELEASE = 0x01; + constexpr uint8_t TOUCH_STATE_PRESS = 0x02; + constexpr uint8_t TOUCH_STATE_SWIPE = 0x03; + constexpr uint8_t TOUCH_STATE_MULTI_TOUCH = 0x0B; + constexpr uint8_t TOUCH_STATE_SWIPE_RIGHT = 0x0C; + constexpr uint8_t TOUCH_STATE_SWIPE_LEFT = 0x0D; + + // UART Constants + constexpr int UART_RECEIVED_BYTES_SIZE = 15; + constexpr int HEADER_BYTE_1 = 0xAA; + constexpr int HEADER_BYTE_2 = 0x55; + constexpr int VALID_DATA_BYTE_2 = 0x01; + constexpr int VALID_DATA_BYTE_3 = 0x02; + + // Log tag + static const char *TAG = "tx_ultimate_easy"; + + struct TouchPoint { + uint8_t button = 0; + int8_t x = -1; + int8_t state = -1; + std::string state_str = "Unknown"; + }; + + class TxUltimateEasy : public uart::UARTDevice, public Component { + public: + Trigger *get_trigger_touch_event() { return &this->trigger_touch_event_; } + Trigger *get_trigger_touch() { return &this->trigger_touch_; } + Trigger *get_trigger_release() { return &this->trigger_release_; } + Trigger *get_trigger_swipe_left() { return &this->trigger_swipe_left_; } + Trigger *get_trigger_swipe_right() { return &this->trigger_swipe_right_; } + Trigger *get_trigger_multi_touch_release() { return &this->trigger_multi_touch_release_; } + Trigger *get_trigger_long_touch_release() { return &this->trigger_long_touch_release_; } + + void set_uart_component(esphome::uart::UARTComponent *uart_component) { this->set_uart_parent(uart_component); } + + void setup() override; + void loop() override; + void dump_config() override; + + uint8_t get_gang_count() { return this->gang_count_; } + bool set_gang_count(const uint8_t gang_count); + uint8_t get_button_from_position(const uint8_t position); + + protected: + void send_touch_(TouchPoint tp); + void handle_touch(const std::array &bytes); + + TouchPoint get_touch_point(const std::array &bytes); + bool is_valid_data(const std::array &bytes); + int get_touch_position_x(const std::array &bytes); + int get_touch_state(const std::array &bytes); + + Trigger trigger_touch_event_; + Trigger trigger_touch_; + Trigger trigger_release_; + Trigger trigger_swipe_left_; + Trigger trigger_swipe_right_; + Trigger trigger_multi_touch_release_; + Trigger trigger_long_touch_release_; + + uint8_t gang_count_ = 1; + + }; // class TxUltimateEasy + + } // namespace tx_ultimate_easy +} // namespace esphome From 0fb4b5fe090f51c8df5718608dff924ad73b627a Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Wed, 6 Aug 2025 16:32:13 +0200 Subject: [PATCH 20/50] Fix typo --- ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml index e67cd67..b9b155c 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml @@ -1168,7 +1168,7 @@ script: - lambda: |- // Restore all light attributes based on current model and gang count switch (id(gang_count)) { - case 1: + case 1: // 1-Gang if (id(is_us_model)) { light_attributes_restore_dual->execute(id(light_us_rl_1_1_left), &id(attr_light_rl_1_1_both), 1); light_attributes_restore_dual->execute(id(light_us_rl_1_1_right), &id(attr_light_rl_1_1_both), 2); @@ -1177,7 +1177,7 @@ script: light_attributes_restore_dual->execute(id(light_eu_rl_1_1_top), &id(attr_light_rl_1_1_both), 2); } break; - case 2: + case 2: // 2-Gang if (id(is_us_model)) { light_attributes_restore_dual->execute(id(light_us_rl_1_2_left), &id(attr_light_rl_1_2_both), 1); light_attributes_restore_dual->execute(id(light_us_rl_1_2_right), &id(attr_light_rl_1_2_both), 2); @@ -1190,7 +1190,7 @@ script: light_attributes_restore_dual->execute(id(light_eu_rl_2_2_top), &id(attr_light_rl_2_2_both), 2); } break; - case 3: + case 3: // 3-Gang if (id(is_us_model)) { light_attributes_restore_dual->execute(id(light_us_rl_1_3_left), &id(attr_light_rl_1_3_both), 1); light_attributes_restore_dual->execute(id(light_us_rl_1_3_right), &id(attr_light_rl_1_3_both), 2); @@ -1207,7 +1207,7 @@ script: light_attributes_restore_dual->execute(id(light_eu_rl_3_3_top), &id(attr_light_rl_3_3_both), 2); } break; - case 3: + case 4: // 4-Gang if (id(is_us_model)) { light_attributes_restore_dual->execute(id(light_us_rl_1_4_left), &id(attr_light_rl_1_4_both), 1); light_attributes_restore_dual->execute(id(light_us_rl_1_4_right), &id(attr_light_rl_1_4_both), 2); From 3c53b21fbe0daf154442b18ee69ca2f39a652487 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Wed, 6 Aug 2025 16:34:52 +0200 Subject: [PATCH 21/50] Lint --- ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml index 1bbee37..3bf536c 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml @@ -570,7 +570,7 @@ script: - lambda: |- auto attrs_pair = tx_ultimate_easy::unpack_dual_light_attributes(*attr_global_ptr); tx_ultimate_easy::LightAttributes attrs = (group_index == 1) ? attrs_pair.first : attrs_pair.second; - + auto call = light_ptr->make_call(); call.set_brightness(attrs.brightness / 100.0f); call.set_rgb(attrs.red / 255.0f, attrs.green / 255.0f, attrs.blue / 255.0f); @@ -610,10 +610,10 @@ script: .green = uint8_t(current.get_green() * 255.0f), .blue = uint8_t(current.get_blue() * 255.0f) }; - + // Unpack existing dual attributes auto existing = tx_ultimate_easy::unpack_dual_light_attributes(*attr_global_ptr); - + // Update the appropriate group if (group_index == 1) { *attr_global_ptr = tx_ultimate_easy::pack_dual_light_attributes(new_attrs, existing.second); From 00d70efc3bf92ae6a7c6db6394b276050a3f3852 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Wed, 6 Aug 2025 16:44:31 +0200 Subject: [PATCH 22/50] Remove duplicated files --- .../tx_ultimate_easy/tx_ultimate_easy.cpp | 250 ------------------ .../tx_ultimate_easy/tx_ultimate_easy.h | 154 ----------- 2 files changed, 404 deletions(-) delete mode 100644 components/tx_ultimate_easy/tx_ultimate_easy.cpp delete mode 100644 components/tx_ultimate_easy/tx_ultimate_easy.h diff --git a/components/tx_ultimate_easy/tx_ultimate_easy.cpp b/components/tx_ultimate_easy/tx_ultimate_easy.cpp deleted file mode 100644 index d820448..0000000 --- a/components/tx_ultimate_easy/tx_ultimate_easy.cpp +++ /dev/null @@ -1,250 +0,0 @@ -// tx_ultimate_easy.cpp - -#include "esphome/core/log.h" -#include "tx_ultimate_easy.h" -#include -#include - -namespace esphome { - namespace tx_ultimate_easy { - - void TxUltimateEasy::setup() { - ESP_LOGI(TAG, "TX Ultimate Easy is initialized"); - } - - void TxUltimateEasy::loop() { - bool found = false; - std::array uart_received_bytes{}; - int byte = -1; - int i = 0; - - while (this->available()) { - byte = this->read(); - if (byte == HEADER_BYTE_1) { - this->handle_touch(uart_received_bytes); - i = 0; - } - if (i < UART_RECEIVED_BYTES_SIZE) { - uart_received_bytes[i] = byte; - i++; - } - if (byte != 0x00) { - found = true; - } - } - if (found) this->handle_touch(uart_received_bytes); - } - - void TxUltimateEasy::handle_touch(const std::array &uart_received_bytes) { - ESP_LOGV(TAG, "------------"); - ESP_LOGV(TAG, "- UART-Log -"); - ESP_LOGV(TAG, "------------"); - for (int i = 0; i < UART_RECEIVED_BYTES_SIZE; i++) { - ESP_LOGV(TAG, "UART - Log - Byte[%i]: %i", i, uart_received_bytes[i]); - } - if (this->is_valid_data(uart_received_bytes)) { - this->send_touch_(this->get_touch_point(uart_received_bytes)); - } - } - - void TxUltimateEasy::dump_config() { - ESP_LOGCONFIG(TAG, "TX Ultimate Easy"); - ESP_LOGCONFIG(TAG, " Gang count: %" PRIu8, this->gang_count_); - } - - bool TxUltimateEasy::set_gang_count(const uint8_t gang_count) { - // Hardware supports maximum of 4 touch-sensitive buttons - if (gang_count < 1 or gang_count > 4) - return false; - this->gang_count_ = gang_count; - return true; - } - - uint8_t TxUltimateEasy::get_button_from_position(const uint8_t position) { - // Validate position bounds - if (position > TOUCH_MAX_POSITION) - return 0; - - // Special case for single gang (only one button exists) - if (this->gang_count_ == 1) - return 1; - - // Calculate button number Change to round up instead of truncate (integer division) - const uint8_t width = (TOUCH_MAX_POSITION + gang_count_) / this->gang_count_; // Width of each button region - if (width < 1) // Invalid width - and prevents division by zero - return 0; - const uint8_t button = std::min( - static_cast((position / width) + 1), // Convert position to button index - this->gang_count_ // Clamp to max gang count - ); - return button; - } - - void TxUltimateEasy::send_touch_(TouchPoint tp) { - this->trigger_touch_event_.trigger(tp); - switch (tp.state) { - case TOUCH_STATE_RELEASE: - if (tp.x >= 17) { - tp.x -= 16; - ESP_LOGV(TAG, "Long touch - Released (x=%d)", tp.x); - this->trigger_long_touch_release_.trigger(tp); - } else { - ESP_LOGV(TAG, "Touch - Released (x=%d)", tp.x); - this->trigger_release_.trigger(tp); - } - break; - - case TOUCH_STATE_PRESS: - ESP_LOGV(TAG, "Touch - Pressed (x=%d)", tp.x); - this->trigger_touch_.trigger(tp); - break; - - case TOUCH_STATE_SWIPE_LEFT: - ESP_LOGV(TAG, "Swipe - Left (x=%d)", tp.x); - this->trigger_swipe_left_.trigger(tp); - break; - - case TOUCH_STATE_SWIPE_RIGHT: - ESP_LOGV(TAG, "Swipe - Right (x=%d)", tp.x); - this->trigger_swipe_right_.trigger(tp); - break; - - case TOUCH_STATE_MULTI_TOUCH: - ESP_LOGV(TAG, "Multi touch - Released"); - this->trigger_multi_touch_release_.trigger(tp); - break; - - default: - break; - } - } - - bool TxUltimateEasy::is_valid_data(const std::array &uart_received_bytes) { - if (uart_received_bytes[0] != HEADER_BYTE_1 || - uart_received_bytes[1] != HEADER_BYTE_2 || - uart_received_bytes[2] != VALID_DATA_BYTE_2 || - uart_received_bytes[3] != VALID_DATA_BYTE_3) { - return false; - } - - int state = this->get_touch_state(uart_received_bytes); - return (state == TOUCH_STATE_PRESS || - state == TOUCH_STATE_RELEASE || - state == TOUCH_STATE_SWIPE_LEFT || - state == TOUCH_STATE_SWIPE_RIGHT || - state == TOUCH_STATE_MULTI_TOUCH) && - // Multi-touch events may have x < 0, all other events require valid x position - (uart_received_bytes[6] >= 0 || state == TOUCH_STATE_MULTI_TOUCH); - } - - int TxUltimateEasy::get_touch_position_x(const std::array &uart_received_bytes) { - switch (uart_received_bytes[4]) { - case TOUCH_STATE_RELEASE: - case TOUCH_STATE_MULTI_TOUCH: - return uart_received_bytes[5]; - - case TOUCH_STATE_SWIPE_LEFT: - case TOUCH_STATE_SWIPE_RIGHT: - // uart_received_bytes[5] indicates swipe direction: - // 12 = find last touch position (scan right to left) - // 13 = find first touch position (scan left to right) - // Bytes 6-7 contain a 10-bit bitmap of touch positions: - // Byte 7: bits 0-7 represent positions 1-8 - // Byte 6: bits 0-1 represent positions 9-10 - switch (uart_received_bytes[5]) { - case 12: - for (uint8_t i = 10; i > 0; i--) { - if (i > 8 - ? uart_received_bytes[6] & (1 << (i - 9)) - : uart_received_bytes[7] & (1 << (i - 1))) { - return i; - } - } - break; - case 13: - for (uint8_t i = 1; i <= 10; i++) { - if (i > 8 - ? uart_received_bytes[6] & (1 << (i - 9)) - : uart_received_bytes[7] & (1 << (i - 1))) { - return i; - } - } - break; - } - return uart_received_bytes[5]; - - default: - return uart_received_bytes[6]; - } - } - - int TxUltimateEasy::get_touch_state(const std::array &uart_received_bytes) { - int state = uart_received_bytes[4]; - if (state == TOUCH_STATE_PRESS && uart_received_bytes[5] != 0) - state = TOUCH_STATE_RELEASE; - if (state == TOUCH_STATE_RELEASE && uart_received_bytes[5] == TOUCH_STATE_MULTI_TOUCH) - state = TOUCH_STATE_MULTI_TOUCH; - if (state == TOUCH_STATE_SWIPE) { - state = (uart_received_bytes[5] == TOUCH_STATE_SWIPE_RIGHT) ? TOUCH_STATE_SWIPE_RIGHT : - (uart_received_bytes[5] == TOUCH_STATE_SWIPE_LEFT) ? TOUCH_STATE_SWIPE_LEFT : state; - } - return state; - } - - TouchPoint TxUltimateEasy::get_touch_point(const std::array &uart_received_bytes) { - TouchPoint tp; - tp.x = this->get_touch_position_x(uart_received_bytes); - if (tp.x >= 0) - tp.button = this->get_button_from_position(static_cast(tp.x)); - tp.state = this->get_touch_state(uart_received_bytes); - switch (tp.state) { - case TOUCH_STATE_RELEASE: - tp.state_str = "RELEASE"; - break; - case TOUCH_STATE_PRESS: - tp.state_str = "PRESS"; - break; - case TOUCH_STATE_SWIPE: - tp.state_str = "SWIPE"; - break; - case TOUCH_STATE_MULTI_TOUCH: - tp.state_str = "MULTI_TOUCH"; - break; - case TOUCH_STATE_SWIPE_RIGHT: - tp.state_str = "SWIPE_RIGHT"; - break; - case TOUCH_STATE_SWIPE_LEFT: - tp.state_str = "SWIPE_LEFT"; - break; - default: - tp.state_str = "Unknown"; - break; - } - return tp; - } - - // LightAttributes - - // Constant definitions - const LightAttributes LIGHT_ATTRS_DEFAULT = {100, 255, 255, 255}; - const uint32_t LIGHT_ATTRS_DEFAULT_PACKED = 0x64FFFFFF; - - // Function implementations - uint32_t pack_light_attributes(const LightAttributes& attr) { - return (uint32_t(attr.brightness) << 24) | - (uint32_t(attr.red) << 16) | - (uint32_t(attr.green) << 8) | - (uint32_t(attr.blue)); - } - - LightAttributes unpack_light_attributes(uint32_t packed) { - return { - .brightness = uint8_t((packed >> 24) & 0xFF), - .red = uint8_t((packed >> 16) & 0xFF), - .green = uint8_t((packed >> 8) & 0xFF), - .blue = uint8_t(packed & 0xFF) - }; - } - - } // namespace tx_ultimate_easy -} // namespace esphome diff --git a/components/tx_ultimate_easy/tx_ultimate_easy.h b/components/tx_ultimate_easy/tx_ultimate_easy.h deleted file mode 100644 index 5175398..0000000 --- a/components/tx_ultimate_easy/tx_ultimate_easy.h +++ /dev/null @@ -1,154 +0,0 @@ -// tx_ultimate_easy.h - -#pragma once - -#include "esphome/core/automation.h" -#include "esphome/core/component.h" -#include "esphome/components/uart/uart.h" -#include "esphome/components/uart/uart_component.h" -#include "esphome/components/binary_sensor/binary_sensor.h" -#include "esphome/components/switch/switch.h" -#include "esphome/components/light/addressable_light.h" -#include - -namespace esphome { - namespace tx_ultimate_easy { - // Touch Max Position - constexpr uint8_t TOUCH_MAX_POSITION = 10; - - // Touch State Constants - constexpr uint8_t TOUCH_STATE_RELEASE = 0x01; - constexpr uint8_t TOUCH_STATE_PRESS = 0x02; - constexpr uint8_t TOUCH_STATE_SWIPE = 0x03; - constexpr uint8_t TOUCH_STATE_MULTI_TOUCH = 0x0B; - constexpr uint8_t TOUCH_STATE_SWIPE_RIGHT = 0x0C; - constexpr uint8_t TOUCH_STATE_SWIPE_LEFT = 0x0D; - - // UART Constants - constexpr int UART_RECEIVED_BYTES_SIZE = 15; - constexpr int HEADER_BYTE_1 = 0xAA; - constexpr int HEADER_BYTE_2 = 0x55; - constexpr int VALID_DATA_BYTE_2 = 0x01; - constexpr int VALID_DATA_BYTE_3 = 0x02; - - // Log tag - static const char *TAG = "tx_ultimate_easy"; - - struct TouchPoint { - uint8_t button = 0; - int8_t x = -1; - int8_t state = -1; - std::string state_str = "Unknown"; - }; - - class TxUltimateEasy : public uart::UARTDevice, public Component { - public: - Trigger *get_trigger_touch_event() { return &this->trigger_touch_event_; } - Trigger *get_trigger_touch() { return &this->trigger_touch_; } - Trigger *get_trigger_release() { return &this->trigger_release_; } - Trigger *get_trigger_swipe_left() { return &this->trigger_swipe_left_; } - Trigger *get_trigger_swipe_right() { return &this->trigger_swipe_right_; } - Trigger *get_trigger_multi_touch_release() { return &this->trigger_multi_touch_release_; } - Trigger *get_trigger_long_touch_release() { return &this->trigger_long_touch_release_; } - - void set_uart_component(esphome::uart::UARTComponent *uart_component) { this->set_uart_parent(uart_component); } - - void setup() override; - void loop() override; - void dump_config() override; - - uint8_t get_gang_count() { return this->gang_count_; } - bool set_gang_count(const uint8_t gang_count); - uint8_t get_button_from_position(const uint8_t position); - - protected: - void send_touch_(TouchPoint tp); - void handle_touch(const std::array &bytes); - - TouchPoint get_touch_point(const std::array &bytes); - bool is_valid_data(const std::array &bytes); - int get_touch_position_x(const std::array &bytes); - int get_touch_state(const std::array &bytes); - - Trigger trigger_touch_event_; - Trigger trigger_touch_; - Trigger trigger_release_; - Trigger trigger_swipe_left_; - Trigger trigger_swipe_right_; - Trigger trigger_multi_touch_release_; - Trigger trigger_long_touch_release_; - - uint8_t gang_count_ = 1; - - }; // class TxUltimateEasy - - /** - * @brief Light attributes structure for storing color and brightness data. - * - * This structure represents the visual attributes of a light without the state (on/off). - * It's designed to be packed efficiently into a 32-bit integer for NVS storage. - * RGB components use the full 8-bit range (0-255) while brightness uses percentage (0-100). - */ - struct LightAttributes { - uint8_t brightness; ///< Light brightness percentage (0-100, where 100 = full brightness) - uint8_t red; ///< Red color component (0-255) - uint8_t green; ///< Green color component (0-255) - uint8_t blue; ///< Blue color component (0-255) - }; - - /** - * @brief Packs LightAttributes structure into a 32-bit integer for storage. - * - * Bit layout: [brightness(8)][red(8)][green(8)][blue(8)] - * This allows efficient storage in ESPHome globals with restore_value support. - * Note: Brightness values > 100 will be clamped to 100 during packing. - * - * @param attr The LightAttributes structure to pack - * @return uint32_t Packed representation suitable for NVS storage - * - * @example - * LightAttributes attrs = {75, 255, 128, 64}; - * uint32_t packed = pack_light_attributes(attrs); // Returns 0x4BFF8040 - */ - uint32_t pack_light_attributes(const LightAttributes& attr); - - /** - * @brief Unpacks a 32-bit integer back into LightAttributes structure. - * - * Reverses the packing operation to extract individual color and brightness values. - * Brightness values > 100 in the packed data will be clamped to 100. - * - * @param packed The packed 32-bit representation - * @return LightAttributes The unpacked structure with individual components - * - * @example - * uint32_t packed = 0x4BFF8040; - * LightAttributes attrs = unpack_light_attributes(packed); - * // attrs.brightness = 75, attrs.red = 255, attrs.green = 128, attrs.blue = 64 - */ - LightAttributes unpack_light_attributes(uint32_t packed); - - /** - * @brief Default light attributes for initialization. - * - * Provides sensible defaults for new lights: - * - Brightness: 100 (full brightness) - * - Red: 255 (full intensity) - * - Green: 255 (full intensity) - * - Blue: 255 (full intensity) - * Result: Pure white color at full brightness - */ - extern const LightAttributes LIGHT_ATTRS_DEFAULT; - - /** - * @brief Pre-packed version of DEFAULT_LIGHT_ATTRS for convenience. - * - * This constant contains LIGHT_ATTRS_DEFAULT already packed into uint32_t format, - * suitable for direct use in ESPHome YAML initial_value fields. - * - * Value: 0x64FFFFFF (brightness=100, RGB=255,255,255) - */ - extern const uint32_t LIGHT_ATTRS_DEFAULT_PACKED; - - } // namespace tx_ultimate_easy -} // namespace esphome From 1bb444e008476b134c549de726b2115c5044f4af Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Wed, 6 Aug 2025 16:45:57 +0200 Subject: [PATCH 23/50] Fix component tag --- components/tx_ultimate_easy/tx_ultimate_easy_light.h | 2 ++ components/tx_ultimate_easy/tx_ultimate_easy_touch.h | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/components/tx_ultimate_easy/tx_ultimate_easy_light.h b/components/tx_ultimate_easy/tx_ultimate_easy_light.h index 3a892c3..8fd5d1c 100644 --- a/components/tx_ultimate_easy/tx_ultimate_easy_light.h +++ b/components/tx_ultimate_easy/tx_ultimate_easy_light.h @@ -13,6 +13,8 @@ namespace esphome { namespace tx_ultimate_easy { + // Log tag + static const char *TAG = "tx_ultimate_easy.light"; /** * @brief Light attributes structure for storing color and brightness data. diff --git a/components/tx_ultimate_easy/tx_ultimate_easy_touch.h b/components/tx_ultimate_easy/tx_ultimate_easy_touch.h index 08086a3..5062f11 100644 --- a/components/tx_ultimate_easy/tx_ultimate_easy_touch.h +++ b/components/tx_ultimate_easy/tx_ultimate_easy_touch.h @@ -32,7 +32,7 @@ namespace esphome { constexpr int VALID_DATA_BYTE_3 = 0x02; // Log tag - static const char *TAG = "tx_ultimate_easy"; + static const char *TAG = "tx_ultimate_easy.touch"; struct TouchPoint { uint8_t button = 0; From dbbbc4040d22472fa9a14b70becb73cbc9aef52e Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Wed, 6 Aug 2025 19:06:39 +0200 Subject: [PATCH 24/50] Move tag to .cpp --- components/tx_ultimate_easy/tx_ultimate_easy_light.cpp | 7 +++++++ components/tx_ultimate_easy/tx_ultimate_easy_touch.cpp | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/components/tx_ultimate_easy/tx_ultimate_easy_light.cpp b/components/tx_ultimate_easy/tx_ultimate_easy_light.cpp index cd7a290..ea1e61a 100644 --- a/components/tx_ultimate_easy/tx_ultimate_easy_light.cpp +++ b/components/tx_ultimate_easy/tx_ultimate_easy_light.cpp @@ -1,5 +1,7 @@ // tx_ultimate_easy_light.cpp +#ifdef TX_ULTIMATE_EASY_CORE_HW_LEDS + #include "esphome/core/log.h" #include "tx_ultimate_easy_light.h" #include @@ -8,6 +10,9 @@ namespace esphome { namespace tx_ultimate_easy { + // Log tag + static const char *TAG = "tx_ultimate_easy.light"; + // Constant definitions const LightAttributes LIGHT_ATTRS_DEFAULT = {100, 255, 255, 255}; const uint32_t LIGHT_ATTRS_DEFAULT_PACKED = 0x64FFFFFF; @@ -49,3 +54,5 @@ namespace esphome { } // namespace tx_ultimate_easy } // namespace esphome + +#endif // TX_ULTIMATE_EASY_CORE_HW_LEDS diff --git a/components/tx_ultimate_easy/tx_ultimate_easy_touch.cpp b/components/tx_ultimate_easy/tx_ultimate_easy_touch.cpp index 92ad4cd..7a4ce70 100644 --- a/components/tx_ultimate_easy/tx_ultimate_easy_touch.cpp +++ b/components/tx_ultimate_easy/tx_ultimate_easy_touch.cpp @@ -1,5 +1,7 @@ // tx_ultimate_easy_touch.cpp +#ifdef TX_ULTIMATE_EASY_CORE_HW_TOUCH + #include "esphome/core/log.h" #include "tx_ultimate_easy_touch.h" #include @@ -8,6 +10,9 @@ namespace esphome { namespace tx_ultimate_easy { + // Log tag + static const char *TAG = "tx_ultimate_easy.touch"; + void TxUltimateEasy::setup() { ESP_LOGI(TAG, "TX Ultimate Easy is initialized"); } @@ -225,3 +230,5 @@ namespace esphome { } // namespace tx_ultimate_easy } // namespace esphome + +#endif // TX_ULTIMATE_EASY_CORE_HW_TOUCH From 3557c847b2a25a483e52aa269dcca3c1b5b4e9d1 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Wed, 6 Aug 2025 19:12:43 +0200 Subject: [PATCH 25/50] Remove TAG from .h --- components/tx_ultimate_easy/tx_ultimate_easy_light.h | 7 ++++--- components/tx_ultimate_easy/tx_ultimate_easy_touch.h | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/components/tx_ultimate_easy/tx_ultimate_easy_light.h b/components/tx_ultimate_easy/tx_ultimate_easy_light.h index 8fd5d1c..8e3da9c 100644 --- a/components/tx_ultimate_easy/tx_ultimate_easy_light.h +++ b/components/tx_ultimate_easy/tx_ultimate_easy_light.h @@ -2,6 +2,8 @@ #pragma once +#ifdef TX_ULTIMATE_EASY_CORE_HW_LEDS + #include "esphome/core/automation.h" #include "esphome/core/component.h" #include "esphome/components/uart/uart.h" @@ -13,9 +15,6 @@ namespace esphome { namespace tx_ultimate_easy { - // Log tag - static const char *TAG = "tx_ultimate_easy.light"; - /** * @brief Light attributes structure for storing color and brightness data. * @@ -113,3 +112,5 @@ namespace esphome { } // namespace tx_ultimate_easy } // namespace esphome + +#endif // TX_ULTIMATE_EASY_CORE_HW_LEDS diff --git a/components/tx_ultimate_easy/tx_ultimate_easy_touch.h b/components/tx_ultimate_easy/tx_ultimate_easy_touch.h index 5062f11..867d0ec 100644 --- a/components/tx_ultimate_easy/tx_ultimate_easy_touch.h +++ b/components/tx_ultimate_easy/tx_ultimate_easy_touch.h @@ -2,6 +2,8 @@ #pragma once +#ifdef TX_ULTIMATE_EASY_CORE_HW_TOUCH + #include "esphome/core/automation.h" #include "esphome/core/component.h" #include "esphome/components/uart/uart.h" @@ -31,9 +33,6 @@ namespace esphome { constexpr int VALID_DATA_BYTE_2 = 0x01; constexpr int VALID_DATA_BYTE_3 = 0x02; - // Log tag - static const char *TAG = "tx_ultimate_easy.touch"; - struct TouchPoint { uint8_t button = 0; int8_t x = -1; @@ -84,3 +83,5 @@ namespace esphome { } // namespace tx_ultimate_easy } // namespace esphome + +#endif // TX_ULTIMATE_EASY_CORE_HW_TOUCH From 0d0de08e197ea82c42a4cb151eb6bc4abe692383 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Wed, 6 Aug 2025 19:21:32 +0200 Subject: [PATCH 26/50] Ensure brightness is limited to 100 As suggested by coderabbitai. --- components/tx_ultimate_easy/tx_ultimate_easy_light.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/tx_ultimate_easy/tx_ultimate_easy_light.cpp b/components/tx_ultimate_easy/tx_ultimate_easy_light.cpp index ea1e61a..c6e0123 100644 --- a/components/tx_ultimate_easy/tx_ultimate_easy_light.cpp +++ b/components/tx_ultimate_easy/tx_ultimate_easy_light.cpp @@ -20,7 +20,7 @@ namespace esphome { // Function implementations uint32_t pack_light_attributes(const LightAttributes& attr) { - return (uint32_t(attr.brightness) << 24) | + return (uint32_t(std::min(attr.brightness, uint8_t(100))) << 24) | (uint32_t(attr.red) << 16) | (uint32_t(attr.green) << 8) | (uint32_t(attr.blue)); @@ -28,7 +28,7 @@ namespace esphome { LightAttributes unpack_light_attributes(uint32_t packed) { return { - .brightness = uint8_t((packed >> 24) & 0xFF), + .brightness = std::min(uint8_t((packed >> 24) & 0xFF), uint8_t(100)), .red = uint8_t((packed >> 16) & 0xFF), .green = uint8_t((packed >> 8) & 0xFF), .blue = uint8_t(packed & 0xFF) From eca54c01ed792fa484b4a81893c6837a66217d79 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Wed, 6 Aug 2025 19:47:27 +0200 Subject: [PATCH 27/50] Move "Relay n light mode" to `standard_hw_relays` --- ...TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml | 75 ----------------- ...imate-Easy-ESPHome_standard_hw_relays.yaml | 83 +++++++++++++++++++ 2 files changed, 83 insertions(+), 75 deletions(-) diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml index 3bf536c..95679e3 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml @@ -494,27 +494,6 @@ script: - id: !extend dump_config then: - lambda: |- - std::string gang_count_plural_suffix = id(gang_count) > 1 ? "s" : ""; - // Relay's LEDs modes - ESP_LOGCONFIG("${TAG_CORE_HW_LEDS}", "Relay%s LEDs mode%s:", - gang_count_plural_suffix.c_str(), - gang_count_plural_suffix.c_str()); - ESP_LOGCONFIG("${TAG_CORE_HW_LEDS}", " Relay 1: %s", id(is_us_model) ? - sl_relay_1_light_mode_us->state.c_str() : - sl_relay_1_light_mode_eu->state.c_str()); - if (id(gang_count) >= 2) - ESP_LOGCONFIG("${TAG_CORE_HW_LEDS}", " Relay 2: %s", id(is_us_model) ? - sl_relay_2_light_mode_us->state.c_str() : - sl_relay_2_light_mode_eu->state.c_str()); - if (id(gang_count) >= 3) - ESP_LOGCONFIG("${TAG_CORE_HW_LEDS}", " Relay 3: %s", id(is_us_model) ? - sl_relay_3_light_mode_us->state.c_str() : - sl_relay_3_light_mode_eu->state.c_str()); - if (id(gang_count) >= 4) - ESP_LOGCONFIG("${TAG_CORE_HW_LEDS}", " Relay 4: %s", id(is_us_model) ? - sl_relay_4_light_mode_us->state.c_str() : - sl_relay_4_light_mode_eu->state.c_str()); - // Transition times ESP_LOGCONFIG("${TAG_CORE_HW_LEDS}", "Transition times:"); ESP_LOGCONFIG("${TAG_CORE_HW_LEDS}", " Default: ${default_transition_length}"); @@ -621,58 +600,4 @@ script: *attr_global_ptr = tx_ultimate_easy::pack_dual_light_attributes(existing.first, new_attrs); } -select: - # EU relay light mode template - - &relay_light_mode_eu - id: sl_relay_1_light_mode_eu - name: Relay 1 light mode EU - platform: template - options: - - "${LIGHT_MODE_DISABLED_TEXT}" - - "${LIGHT_MODE_BOTTOM_TEXT}" - - "${LIGHT_MODE_TOP_TEXT}" - - "${LIGHT_MODE_BOTH_TEXT}" - initial_option: "${LIGHT_MODE_BOTTOM_TEXT}" - optimistic: true - restore_value: true - internal: true - entity_category: config - disabled_by_default: false - icon: mdi:dots-square - - id: sl_relay_2_light_mode_eu - name: Relay 2 light mode EU - <<: *relay_light_mode_eu - - id: sl_relay_3_light_mode_eu - name: Relay 3 light mode EU - <<: *relay_light_mode_eu - - id: sl_relay_4_light_mode_eu - name: Relay 4 light mode EU - <<: *relay_light_mode_eu - - # US relay light mode template - - &relay_light_mode_us - id: sl_relay_1_light_mode_us - name: Relay 1 light mode US - platform: template - options: - - "${LIGHT_MODE_DISABLED_TEXT}" - - "${LIGHT_MODE_LEFT_TEXT}" - - "${LIGHT_MODE_RIGHT_TEXT}" - - "${LIGHT_MODE_BOTH_TEXT}" - initial_option: "${LIGHT_MODE_RIGHT_TEXT}" - optimistic: true - restore_value: true - internal: true - entity_category: config - disabled_by_default: false - icon: mdi:dots-square - - id: sl_relay_2_light_mode_us - name: Relay 2 light mode US - <<: *relay_light_mode_us - - id: sl_relay_3_light_mode_us - name: Relay 3 light mode US - <<: *relay_light_mode_us - - id: sl_relay_4_light_mode_us - name: Relay 4 light mode US - <<: *relay_light_mode_us ... diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml index b9b155c..a7b6b2a 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml @@ -1028,6 +1028,25 @@ script: ESP_LOGCONFIG("${TAG_STD_HW_RELAYS}", " Relay modes: %s", YESNO(id(boot_initialization_relays_relay_modes))); ESP_LOGCONFIG("${TAG_STD_HW_RELAYS}", " Status LEDs: %s", YESNO(id(boot_initialization_relays_relay_leds))); + // Relay's LEDs modes + ESP_LOGCONFIG("${TAG_STD_HW_RELAYS}", "Relay%s LEDs mode%s:", gang_count_plural_suffix.c_str(), + gang_count_plural_suffix.c_str()); + ESP_LOGCONFIG("${TAG_STD_HW_RELAYS}", " Relay 1: %s", id(is_us_model) ? + sl_relay_1_light_mode_us->state.c_str() : + sl_relay_1_light_mode_eu->state.c_str()); + if (id(gang_count) >= 2) + ESP_LOGCONFIG("${TAG_STD_HW_RELAYS}", " Relay 2: %s", id(is_us_model) ? + sl_relay_2_light_mode_us->state.c_str() : + sl_relay_2_light_mode_eu->state.c_str()); + if (id(gang_count) >= 3) + ESP_LOGCONFIG("${TAG_STD_HW_RELAYS}", " Relay 3: %s", id(is_us_model) ? + sl_relay_3_light_mode_us->state.c_str() : + sl_relay_3_light_mode_eu->state.c_str()); + if (id(gang_count) >= 4) + ESP_LOGCONFIG("${TAG_STD_HW_RELAYS}", " Relay 4: %s", id(is_us_model) ? + sl_relay_4_light_mode_us->state.c_str() : + sl_relay_4_light_mode_eu->state.c_str()); + - id: !extend dump_config_list_packages then: - script.wait: dump_config @@ -1294,6 +1313,42 @@ script: } select: + # EU relay light mode template + - &relay_light_mode_eu + id: sl_relay_1_light_mode_eu + name: Relay 1 light mode EU + platform: template + options: + - "${LIGHT_MODE_DISABLED_TEXT}" + - "${LIGHT_MODE_BOTTOM_TEXT}" + - "${LIGHT_MODE_TOP_TEXT}" + - "${LIGHT_MODE_BOTH_TEXT}" + initial_option: "${LIGHT_MODE_BOTTOM_TEXT}" + optimistic: true + restore_value: true + internal: true + entity_category: config + disabled_by_default: false + icon: mdi:dots-square + + # US relay light mode template + - &relay_light_mode_us + id: sl_relay_1_light_mode_us + name: Relay 1 light mode US + platform: template + options: + - "${LIGHT_MODE_DISABLED_TEXT}" + - "${LIGHT_MODE_LEFT_TEXT}" + - "${LIGHT_MODE_RIGHT_TEXT}" + - "${LIGHT_MODE_BOTH_TEXT}" + initial_option: "${LIGHT_MODE_RIGHT_TEXT}" + optimistic: true + restore_value: true + internal: true + entity_category: config + disabled_by_default: false + icon: mdi:dots-square + - id: sl_relay_1_mode name: Relay 1 display mode platform: template @@ -1316,6 +1371,15 @@ select: - lambda: |- id(relay_modes_packed) = (id(relay_modes_packed) & ~(0x0F << 0)) | ((id(sl_relay_1_mode).active_index().value_or(1) & 0x0F) << 0); + + - id: sl_relay_2_light_mode_eu + name: Relay 2 light mode EU + <<: *relay_light_mode_eu + + - id: sl_relay_2_light_mode_us + name: Relay 2 light mode US + <<: *relay_light_mode_us + - id: sl_relay_2_mode name: Relay 2 display mode platform: template @@ -1338,6 +1402,15 @@ select: - lambda: |- id(relay_modes_packed) = (id(relay_modes_packed) & ~(0x0F << 4)) | ((id(sl_relay_2_mode).active_index().value_or(1) & 0x0F) << 4); + + - id: sl_relay_3_light_mode_eu + name: Relay 3 light mode EU + <<: *relay_light_mode_eu + + - id: sl_relay_3_light_mode_us + name: Relay 3 light mode US + <<: *relay_light_mode_us + - id: sl_relay_3_mode name: Relay 3 display mode platform: template @@ -1360,6 +1433,15 @@ select: - lambda: |- id(relay_modes_packed) = (id(relay_modes_packed) & ~(0x0F << 8)) | ((id(sl_relay_3_mode).active_index().value_or(1) & 0x0F) << 8); + + - id: sl_relay_4_light_mode_eu + name: Relay 4 light mode EU + <<: *relay_light_mode_eu + + - id: sl_relay_4_light_mode_us + name: Relay 4 light mode US + <<: *relay_light_mode_us + - id: sl_relay_4_mode name: Relay 4 display mode platform: template @@ -1510,4 +1592,5 @@ switch: - light.turn_off: light_output_4 - lambda: |- id(relay_switches_packed) &= ~0x10; // Clear bit 4 + ... From cf8590b46044c2429a01150064cb2de3d15882f3 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Wed, 6 Aug 2025 23:00:41 +0200 Subject: [PATCH 28/50] Use `switch.control` from pr#10105 (esphome) --- ...imate-Easy-ESPHome_standard_hw_relays.yaml | 193 +++++++++++------- 1 file changed, 123 insertions(+), 70 deletions(-) diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml index a7b6b2a..15e17fc 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml @@ -25,7 +25,36 @@ esphome: build_flags: - -D TX_ULTIMATE_EASY_STANDARD_HW_RELAYS +external_components: + - source: github://pr#10105 # To-do: remove after this PR is released + components: [switch] + refresh: 1h + globals: + - &global_attr_dual_light + id: attr_light_rl_1_1_both # 1-gang: relay 1, both groups + type: uint64_t + restore_value: true + initial_value: '${LIGHT_DUAL_ATTRS_DEFAULT}' + - id: attr_light_rl_1_2_both # 2-gang: relay 1, both groups + <<: *global_attr_dual_light + - id: attr_light_rl_2_2_both # 2-gang: relay 2, both groups + <<: *global_attr_dual_light + - id: attr_light_rl_1_3_both # 3-gang: relay 1, both groups + <<: *global_attr_dual_light + - id: attr_light_rl_2_3_both # 3-gang: relay 2, both groups + <<: *global_attr_dual_light + - id: attr_light_rl_3_3_both # 3-gang: relay 3, both groups + <<: *global_attr_dual_light + - id: attr_light_rl_1_4_both # 4-gang: relay 1, both groups + <<: *global_attr_dual_light + - id: attr_light_rl_2_4_both # 4-gang: relay 2, both groups + <<: *global_attr_dual_light + - id: attr_light_rl_3_4_both # 4-gang: relay 3, both groups + <<: *global_attr_dual_light + - id: attr_light_rl_4_4_both # 4-gang: relay 4, both groups + <<: *global_attr_dual_light + - id: boot_initialization_relays type: bool restore_value: false @@ -54,6 +83,14 @@ globals: type: std::vector restore_value: false + # Packed relay light modes - stores current model's 4 light modes + - id: relay_light_modes_packed + type: uint16_t + restore_value: true + initial_value: '0x1111' # All 4 relays set to option 1 (bottom/left) + # Bit layout: [relay4(4)][relay3(4)][relay2(4)][relay1(4)] + # 16-bit value with 4 bits per relay: 0=Disabled, 1=Bottom/Left, 2=Top/Right, 3=Both, 4-15=Reserved + # Packed relay modes - replaces 4 separate selects - id: relay_modes_packed type: uint16_t @@ -69,30 +106,6 @@ globals: initial_value: '0' # All switches default to OFF # Bit layout: [unused(3)][sw_relay4][sw_relay3][sw_relay2][sw_relay1][sw_expose_leds] - - &global_attr_dual_light - id: attr_light_rl_1_1_both # 1-gang: relay 1, both groups - type: uint64_t - restore_value: true - initial_value: '${LIGHT_DUAL_ATTRS_DEFAULT}' - - id: attr_light_rl_1_2_both # 2-gang: relay 1, both groups - <<: *global_attr_dual_light - - id: attr_light_rl_2_2_both # 2-gang: relay 2, both groups - <<: *global_attr_dual_light - - id: attr_light_rl_1_3_both # 3-gang: relay 1, both groups - <<: *global_attr_dual_light - - id: attr_light_rl_2_3_both # 3-gang: relay 2, both groups - <<: *global_attr_dual_light - - id: attr_light_rl_3_3_both # 3-gang: relay 3, both groups - <<: *global_attr_dual_light - - id: attr_light_rl_1_4_both # 4-gang: relay 1, both groups - <<: *global_attr_dual_light - - id: attr_light_rl_2_4_both # 4-gang: relay 2, both groups - <<: *global_attr_dual_light - - id: attr_light_rl_3_4_both # 4-gang: relay 3, both groups - <<: *global_attr_dual_light - - id: attr_light_rl_4_4_both # 4-gang: relay 4, both groups - <<: *global_attr_dual_light - light: # These lights are used for the physical relays to be shown as lights - id: light_output_1 @@ -181,7 +194,7 @@ light: internal: true disabled_by_default: false default_transition_length: ${default_transition_length} - restore_mode: ${LIGHT_RELAYS_RESTORE_MODE} + restore_mode: ALWAYS_OFF segments: - id: light_full from: 9 @@ -1143,42 +1156,50 @@ script: id: sl_relay_4_mode index: !lambda return std::min((uint8_t)((id(relay_modes_packed) >> 12) & 0x0F), (uint8_t)2); + # Light mode restoration - restore to active model's selects only + # Restore US light modes + - select.set_index: + id: sl_relay_1_light_mode_us + index: !lambda return std::min((uint8_t)(id(relay_light_modes_packed) & 0x0F), (uint8_t)3); + - select.set_index: + id: sl_relay_2_light_mode_us + index: !lambda return std::min((uint8_t)((id(relay_light_modes_packed) >> 4) & 0x0F), (uint8_t)3); + - select.set_index: + id: sl_relay_3_light_mode_us + index: !lambda return std::min((uint8_t)((id(relay_light_modes_packed) >> 8) & 0x0F), (uint8_t)3); + - select.set_index: + id: sl_relay_4_light_mode_us + index: !lambda return std::min((uint8_t)((id(relay_light_modes_packed) >> 12) & 0x0F), (uint8_t)3); + # Restore EU light modes + - select.set_index: + id: sl_relay_1_light_mode_eu + index: !lambda return std::min((uint8_t)(id(relay_light_modes_packed) & 0x0F), (uint8_t)3); + - select.set_index: + id: sl_relay_2_light_mode_eu + index: !lambda return std::min((uint8_t)((id(relay_light_modes_packed) >> 4) & 0x0F), (uint8_t)3); + - select.set_index: + id: sl_relay_3_light_mode_eu + index: !lambda return std::min((uint8_t)((id(relay_light_modes_packed) >> 8) & 0x0F), (uint8_t)3); + - select.set_index: + id: sl_relay_4_light_mode_eu + index: !lambda return std::min((uint8_t)((id(relay_light_modes_packed) >> 12) & 0x0F), (uint8_t)3); + # Switch states - - if: - condition: - lambda: return (id(relay_switches_packed) & 0x01) == 0; - then: - - switch.turn_off: sw_expose_relays_leds_to_ha - else: - - switch.turn_on: sw_expose_relays_leds_to_ha - - if: - condition: - lambda: return (id(relay_switches_packed) & 0x02) == 0; - then: - - switch.turn_off: sw_relay_1 - else: - - switch.turn_on: sw_relay_1 - - if: - condition: - lambda: return (id(relay_switches_packed) & 0x04) == 0; - then: - - switch.turn_off: sw_relay_2 - else: - - switch.turn_on: sw_relay_2 - - if: - condition: - lambda: return (id(relay_switches_packed) & 0x08) == 0; - then: - - switch.turn_off: sw_relay_3 - else: - - switch.turn_on: sw_relay_3 - - if: - condition: - lambda: return (id(relay_switches_packed) & 0x10) == 0; - then: - - switch.turn_off: sw_relay_4 - else: - - switch.turn_on: sw_relay_4 + - switch.control: + id: sw_expose_relays_leds_to_ha + state: !lambda return (id(relay_switches_packed) & 0x01); + - switch.control: + id: sw_relay_1 + state: !lambda return (id(relay_switches_packed) & 0x02); + - switch.control: + id: sw_relay_2 + state: !lambda return (id(relay_switches_packed) & 0x04); + - switch.control: + id: sw_relay_3 + state: !lambda return (id(relay_switches_packed) & 0x08); + - switch.control: + id: sw_relay_4 + state: !lambda return (id(relay_switches_packed) & 0x10); # update lights based on new relay statuses (catch up, as this should be done when each relay is updated) - script.execute: show_relay_status @@ -1325,11 +1346,15 @@ select: - "${LIGHT_MODE_BOTH_TEXT}" initial_option: "${LIGHT_MODE_BOTTOM_TEXT}" optimistic: true - restore_value: true + restore_value: false internal: true entity_category: config disabled_by_default: false icon: mdi:dots-square + on_value: + - lambda: |- + id(relay_light_modes_packed) = (id(relay_light_modes_packed) & ~(0x0F << 0)) | + ((id(sl_relay_1_light_mode_eu).active_index().value_or(1) & 0x0F) << 0); # US relay light mode template - &relay_light_mode_us @@ -1343,11 +1368,15 @@ select: - "${LIGHT_MODE_BOTH_TEXT}" initial_option: "${LIGHT_MODE_RIGHT_TEXT}" optimistic: true - restore_value: true + restore_value: false internal: true entity_category: config disabled_by_default: false icon: mdi:dots-square + on_value: + - lambda: |- + id(relay_light_modes_packed) = (id(relay_light_modes_packed) & ~(0x0F << 0)) | + ((id(sl_relay_1_light_mode_us).active_index().value_or(1) & 0x0F) << 0); - id: sl_relay_1_mode name: Relay 1 display mode @@ -1358,7 +1387,7 @@ select: - "${RELAY_MODE_TEXT_NOT_USED}" initial_option: "${RELAY_MODE_TEXT_SWITCH}" optimistic: true - restore_value: true + restore_value: false internal: true entity_category: config disabled_by_default: false @@ -1370,15 +1399,23 @@ select: state: true - lambda: |- id(relay_modes_packed) = (id(relay_modes_packed) & ~(0x0F << 0)) | - ((id(sl_relay_1_mode).active_index().value_or(1) & 0x0F) << 0); + ((id(sl_relay_1_mode).active_index().value_or(0) & 0x0F) << 0); - id: sl_relay_2_light_mode_eu name: Relay 2 light mode EU <<: *relay_light_mode_eu + on_value: + - lambda: |- + id(relay_light_modes_packed) = (id(relay_light_modes_packed) & ~(0x0F << 4)) | + ((id(sl_relay_2_light_mode_eu).active_index().value_or(1) & 0x0F) << 4); - id: sl_relay_2_light_mode_us name: Relay 2 light mode US <<: *relay_light_mode_us + on_value: + - lambda: |- + id(relay_light_modes_packed) = (id(relay_light_modes_packed) & ~(0x0F << 4)) | + ((id(sl_relay_2_light_mode_us).active_index().value_or(1) & 0x0F) << 4); - id: sl_relay_2_mode name: Relay 2 display mode @@ -1389,7 +1426,7 @@ select: - "${RELAY_MODE_TEXT_NOT_USED}" initial_option: "${RELAY_MODE_TEXT_SWITCH}" optimistic: true - restore_value: true + restore_value: false internal: true entity_category: config disabled_by_default: false @@ -1401,15 +1438,23 @@ select: state: true - lambda: |- id(relay_modes_packed) = (id(relay_modes_packed) & ~(0x0F << 4)) | - ((id(sl_relay_2_mode).active_index().value_or(1) & 0x0F) << 4); + ((id(sl_relay_2_mode).active_index().value_or(0) & 0x0F) << 4); - id: sl_relay_3_light_mode_eu name: Relay 3 light mode EU <<: *relay_light_mode_eu + on_value: + - lambda: |- + id(relay_light_modes_packed) = (id(relay_light_modes_packed) & ~(0x0F << 8)) | + ((id(sl_relay_3_light_mode_eu).active_index().value_or(1) & 0x0F) << 8); - id: sl_relay_3_light_mode_us name: Relay 3 light mode US <<: *relay_light_mode_us + on_value: + - lambda: |- + id(relay_light_modes_packed) = (id(relay_light_modes_packed) & ~(0x0F << 8)) | + ((id(sl_relay_3_light_mode_us).active_index().value_or(1) & 0x0F) << 8); - id: sl_relay_3_mode name: Relay 3 display mode @@ -1420,7 +1465,7 @@ select: - "${RELAY_MODE_TEXT_NOT_USED}" initial_option: "${RELAY_MODE_TEXT_SWITCH}" optimistic: true - restore_value: true + restore_value: false internal: true entity_category: config disabled_by_default: false @@ -1432,15 +1477,23 @@ select: state: true - lambda: |- id(relay_modes_packed) = (id(relay_modes_packed) & ~(0x0F << 8)) | - ((id(sl_relay_3_mode).active_index().value_or(1) & 0x0F) << 8); + ((id(sl_relay_3_mode).active_index().value_or(0) & 0x0F) << 8); - id: sl_relay_4_light_mode_eu name: Relay 4 light mode EU <<: *relay_light_mode_eu + on_value: + - lambda: |- + id(relay_light_modes_packed) = (id(relay_light_modes_packed) & ~(0x0F << 12)) | + ((id(sl_relay_4_light_mode_eu).active_index().value_or(1) & 0x0F) << 12); - id: sl_relay_4_light_mode_us name: Relay 4 light mode US <<: *relay_light_mode_us + on_value: + - lambda: |- + id(relay_light_modes_packed) = (id(relay_light_modes_packed) & ~(0x0F << 12)) | + ((id(sl_relay_4_light_mode_eu).active_index().value_or(1) & 0x0F) << 12); - id: sl_relay_4_mode name: Relay 4 display mode @@ -1451,7 +1504,7 @@ select: - "${RELAY_MODE_TEXT_NOT_USED}" initial_option: "${RELAY_MODE_TEXT_SWITCH}" optimistic: true - restore_value: true + restore_value: false internal: true entity_category: config disabled_by_default: false @@ -1463,7 +1516,7 @@ select: state: true - lambda: |- id(relay_modes_packed) = (id(relay_modes_packed) & ~(0x0F << 12)) | - ((id(sl_relay_4_mode).active_index().value_or(1) & 0x0F) << 12); + ((id(sl_relay_4_mode).active_index().value_or(0) & 0x0F) << 12); switch: - id: sw_expose_relays_leds_to_ha From 462b131cb6589ef9670ab69dbe55e712e10dd4c5 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Wed, 6 Aug 2025 23:08:00 +0200 Subject: [PATCH 29/50] =?UTF-8?q?Fixes=20"Wrong=20selector=20referenced=20?= =?UTF-8?q?=E2=80=93=20US=20light-mode=20writes=20EU=20value"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml index 15e17fc..1d24dc5 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml @@ -1493,7 +1493,7 @@ select: on_value: - lambda: |- id(relay_light_modes_packed) = (id(relay_light_modes_packed) & ~(0x0F << 12)) | - ((id(sl_relay_4_light_mode_eu).active_index().value_or(1) & 0x0F) << 12); + ((id(sl_relay_4_light_mode_us).active_index().value_or(1) & 0x0F) << 12); - id: sl_relay_4_mode name: Relay 4 display mode From d905b69ad29d0e4197b16e1eb88cf18306ae3862 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Wed, 6 Aug 2025 23:49:27 +0200 Subject: [PATCH 30/50] Requires ESPHome v2025.8.0 --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index b17d138..a667872 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,9 @@ [buymeacoffee-shield]: https://img.shields.io/static/v1?label=Buy%20me%20an%20ice%20cream&message=❄&color=blue [buymeacoffee]: https://www.buymeacoffee.com/edwardfirmo +> [!WARNING] +> ESPHome builder v2025.8.0+ is required + ## Framework Migration to ESP-IDF > [!IMPORTANT] From 1df58e162f258a3e733703043f758cb3a980d34f Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Fri, 8 Aug 2025 07:37:52 +0200 Subject: [PATCH 31/50] Set `min_version` to ESPHome 2025.8.0 --- .../TX-Ultimate-Easy-ESPHome_core_common.yaml | 1 + ...TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml | 20 ++++++++++++-- ...imate-Easy-ESPHome_standard_hw_relays.yaml | 26 +++++++++---------- .../tx_ultimate_easy_touch.cpp | 5 ++-- 4 files changed, 35 insertions(+), 17 deletions(-) diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core_common.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core_common.yaml index ace3829..ca66660 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core_common.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core_common.yaml @@ -71,6 +71,7 @@ esphome: name: ${name} friendly_name: ${friendly_name} comment: TX Ultimate Easy + min_version: 2025.8.0-dev project: name: "edwardtfn.tx_ultimate_easy" version: ${version} diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml index 95679e3..c5deead 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml @@ -29,8 +29,8 @@ substitutions: LIGHT_SIDES_RESTORE_MODE: RESTORE_DEFAULT_OFF LIGHT_INDIVIDUAL_RESTORE_MODE: RESTORE_DEFAULT_OFF - LIGHT_ATTRS_DEFAULT: '0x64FFFFFF' # brightness=100, RGB=255,255,255 - LIGHT_DUAL_ATTRS_DEFAULT: '0x64FFFFFF64FFFFFF' # Both lights: brightness=100, RGB=255,255,255 + LIGHT_ATTRS_DEFAULT: '0x64FFFFFF' # brightness=100, RGB=255,255,255 + LIGHT_DUAL_ATTRS_DEFAULT: '0x0064FFFFFF64FFFFFF' # Both lights: brightness=100, RGB=255,255,255 TAG_CORE_HW_LEDS: core.hw.leds @@ -562,6 +562,14 @@ script: attr_global_ptr: uint32_t* then: - lambda: |- + if (!light_ptr) { + ESP_LOGE("${TAG_CORE_HW_LEDS}", "Invalid light pointer"); + return; + } + if (!attr_global_ptr) { + ESP_LOGE("${TAG_CORE_HW_LEDS}", "Invalid attributes pointer"); + return; + } auto current = light_ptr->current_values; tx_ultimate_easy::LightAttributes attrs = { .brightness = uint8_t(current.get_brightness() * 100.0f), @@ -582,6 +590,14 @@ script: group_index: uint8_t # 1 or 2 (which group within the dual pack) then: - lambda: |- + if (!light_ptr) { + ESP_LOGE("${TAG_CORE_HW_LEDS}", "Invalid light pointer"); + return; + } + if (!attr_global_ptr) { + ESP_LOGE("${TAG_CORE_HW_LEDS}", "Invalid attributes pointer"); + return; + } auto current = light_ptr->current_values; tx_ultimate_easy::LightAttributes new_attrs = { .brightness = uint8_t(current.get_brightness() * 100.0f), diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml index 1d24dc5..41b738d 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml @@ -16,7 +16,12 @@ substitutions: RELAY_MODE_TEXT_LIGHT: "Light" RELAY_MODE_TEXT_NOT_USED: "Not in use" - LIGHT_RELAYS_RESTORE_MODE: ALWAYS_OFF + # You shouldn't change the restore mode as there is a custom restoring engine based on globals. + # The reason of a custom engine is to address an issue where NVS space was exceeded causing saving/restoring issues + RELAYS_LIGHT_RESTORE_MODE: ALWAYS_OFF + RELAYS_LIGHT_PARTITION_RESTORE_MODE: ALWAYS_OFF + RELAYS_EXPOSE_LEDS_TO_HA_RESTORE_MODE: ALWAYS_OFF + RELAYS_SWITCH_RESTORE_MODE: ALWAYS_OFF TAG_STD_HW_RELAYS: std.hw.relays @@ -25,11 +30,6 @@ esphome: build_flags: - -D TX_ULTIMATE_EASY_STANDARD_HW_RELAYS -external_components: - - source: github://pr#10105 # To-do: remove after this PR is released - components: [switch] - refresh: 1h - globals: - &global_attr_dual_light id: attr_light_rl_1_1_both # 1-gang: relay 1, both groups @@ -113,7 +113,7 @@ light: output: output_relay_1 platform: binary internal: true - restore_mode: ${LIGHT_RELAYS_RESTORE_MODE} + restore_mode: ${RELAYS_LIGHT_RESTORE_MODE} on_turn_on: then: - if: @@ -194,7 +194,7 @@ light: internal: true disabled_by_default: false default_transition_length: ${default_transition_length} - restore_mode: ALWAYS_OFF + restore_mode: ${RELAYS_LIGHT_PARTITION_RESTORE_MODE} segments: - id: light_full from: 9 @@ -1522,7 +1522,7 @@ switch: - id: sw_expose_relays_leds_to_ha name: Expose Relay's LEDs to Home Assistant platform: template - restore_mode: ALWAYS_OFF + restore_mode: ${RELAYS_EXPOSE_LEDS_TO_HA_RESTORE_MODE} optimistic: true internal: false entity_category: config @@ -1545,7 +1545,7 @@ switch: name: Relay 1 output: output_relay_1 platform: output - restore_mode: ALWAYS_OFF + restore_mode: ${RELAYS_SWITCH_RESTORE_MODE} internal: true on_turn_on: then: @@ -1571,7 +1571,7 @@ switch: name: Relay 2 output: output_relay_2 platform: output - restore_mode: ALWAYS_OFF + restore_mode: ${RELAYS_SWITCH_RESTORE_MODE} internal: true on_turn_on: then: @@ -1597,7 +1597,7 @@ switch: name: Relay 3 output: output_relay_3 platform: output - restore_mode: ALWAYS_OFF + restore_mode: ${RELAYS_SWITCH_RESTORE_MODE} internal: true on_turn_on: then: @@ -1623,7 +1623,7 @@ switch: name: Relay 4 output: output_relay_4 platform: output - restore_mode: ALWAYS_OFF + restore_mode: ${RELAYS_SWITCH_RESTORE_MODE} internal: true on_turn_on: then: diff --git a/components/tx_ultimate_easy/tx_ultimate_easy_touch.cpp b/components/tx_ultimate_easy/tx_ultimate_easy_touch.cpp index 7a4ce70..7c1b4d3 100644 --- a/components/tx_ultimate_easy/tx_ultimate_easy_touch.cpp +++ b/components/tx_ultimate_easy/tx_ultimate_easy_touch.cpp @@ -74,8 +74,9 @@ namespace esphome { if (this->gang_count_ == 1) return 1; - // Calculate button number Change to round up instead of truncate (integer division) - const uint8_t width = (TOUCH_MAX_POSITION + gang_count_) / this->gang_count_; // Width of each button region + // Calculate button width (rounds up to ensure full coverage) + const uint8_t width = + (TOUCH_MAX_POSITION + this->gang_count_) / this->gang_count_; // Width of each button region if (width < 1) // Invalid width - and prevents division by zero return 0; const uint8_t button = std::min( From a10edb3d0654da0339d83a6775a7b2deffa5007d Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Fri, 8 Aug 2025 14:00:43 +0200 Subject: [PATCH 32/50] Fix "Hex literal exceeds 64-bit capacity" --- ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml index c5deead..fcad74a 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml @@ -29,8 +29,8 @@ substitutions: LIGHT_SIDES_RESTORE_MODE: RESTORE_DEFAULT_OFF LIGHT_INDIVIDUAL_RESTORE_MODE: RESTORE_DEFAULT_OFF - LIGHT_ATTRS_DEFAULT: '0x64FFFFFF' # brightness=100, RGB=255,255,255 - LIGHT_DUAL_ATTRS_DEFAULT: '0x0064FFFFFF64FFFFFF' # Both lights: brightness=100, RGB=255,255,255 + LIGHT_ATTRS_DEFAULT: '0x64FFFFFF' # brightness=100, RGB=255,255,255 + LIGHT_DUAL_ATTRS_DEFAULT: '0x64FFFFFF64FFFFFF' # Both lights: brightness=100, RGB=255,255,255 TAG_CORE_HW_LEDS: core.hw.leds From 6520b8050e9bc68e0b21c80a6db27e82a2362e3d Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Fri, 8 Aug 2025 14:02:40 +0200 Subject: [PATCH 33/50] Fixes "Dereference without null-check may crash on bad caller" --- .../TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml index fcad74a..566e30d 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml @@ -531,6 +531,14 @@ script: attr_global_ptr: uint32_t* then: - lambda: |- + if (!light_ptr) { + ESP_LOGE("${TAG_CORE_HW_LEDS}", "Invalid light pointer"); + return; + } + if (!attr_global_ptr) { + ESP_LOGE("${TAG_CORE_HW_LEDS}", "Invalid attributes pointer"); + return; + } tx_ultimate_easy::LightAttributes attrs = tx_ultimate_easy::unpack_light_attributes(*attr_global_ptr); auto call = light_ptr->make_call(); call.set_brightness(attrs.brightness / 100.0f); @@ -547,6 +555,14 @@ script: group_index: uint8_t # 1 or 2 then: - lambda: |- + if (!light_ptr) { + ESP_LOGE("${TAG_CORE_HW_LEDS}", "Invalid light pointer"); + return; + } + if (!attr_global_ptr) { + ESP_LOGE("${TAG_CORE_HW_LEDS}", "Invalid attributes pointer"); + return; + } auto attrs_pair = tx_ultimate_easy::unpack_dual_light_attributes(*attr_global_ptr); tx_ultimate_easy::LightAttributes attrs = (group_index == 1) ? attrs_pair.first : attrs_pair.second; From 5757714b31be711fcbd6b602ff4e375efb95c47e Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Fri, 8 Aug 2025 14:05:12 +0200 Subject: [PATCH 34/50] Guard against invalid `group_index` --- ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml index 566e30d..23a834b 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml @@ -614,6 +614,10 @@ script: ESP_LOGE("${TAG_CORE_HW_LEDS}", "Invalid attributes pointer"); return; } + if (group_index != 1 && group_index != 2) { + ESP_LOGE("${TAG_CORE_HW_LEDS}", "Invalid group_index=%d (expected 1 or 2)", group_index); + return; + } auto current = light_ptr->current_values; tx_ultimate_easy::LightAttributes new_attrs = { .brightness = uint8_t(current.get_brightness() * 100.0f), From fe745c0132c011a6759d932b96514774a150ac7f Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Fri, 8 Aug 2025 14:52:40 +0200 Subject: [PATCH 35/50] Update TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml --- ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml index 23a834b..64fa2be 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml @@ -599,7 +599,7 @@ script: attrs.brightness, attrs.red, attrs.green, attrs.blue); - id: light_attributes_save_dual - mode: parallel + mode: queued # To prevent two instances trying to change the same global parameters: light_ptr: light::LightState* attr_global_ptr: uint64_t* From aae3eaa20489daab7640ce9369fe0a472e4e2675 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Fri, 8 Aug 2025 14:54:56 +0200 Subject: [PATCH 36/50] Validate `group_index` and add symmetric logging in `restore_dual` --- ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml index 64fa2be..9878e4f 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml @@ -563,6 +563,10 @@ script: ESP_LOGE("${TAG_CORE_HW_LEDS}", "Invalid attributes pointer"); return; } + if (group_index != 1 && group_index != 2) { + ESP_LOGE("${TAG_CORE_HW_LEDS}", "Invalid group_index=%d (expected 1 or 2)", group_index); + return; + } auto attrs_pair = tx_ultimate_easy::unpack_dual_light_attributes(*attr_global_ptr); tx_ultimate_easy::LightAttributes attrs = (group_index == 1) ? attrs_pair.first : attrs_pair.second; @@ -570,6 +574,8 @@ script: call.set_brightness(attrs.brightness / 100.0f); call.set_rgb(attrs.red / 255.0f, attrs.green / 255.0f, attrs.blue / 255.0f); call.perform(); + ESP_LOGI("${TAG_CORE_HW_LEDS}", "Restored (group=%d) attributes: brightness=%d%%, RGB=(%d,%d,%d)", + group_index, attrs.brightness, attrs.red, attrs.green, attrs.blue); - id: light_attributes_save mode: parallel From fc135875c5ebf01366e280554bf9425fb8b2782a Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Sat, 9 Aug 2025 14:46:14 +0200 Subject: [PATCH 37/50] Use new `switch.on_state` --- ...imate-Easy-ESPHome_standard_hw_relays.yaml | 62 +++++++++---------- ...te-Easy-ESPHome_standard_hw_vibration.yaml | 11 ++-- 2 files changed, 32 insertions(+), 41 deletions(-) diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml index 41b738d..d7f67a0 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml @@ -1526,20 +1526,18 @@ switch: optimistic: true internal: false entity_category: config - on_turn_on: + on_state: then: - binary_sensor.template.publish: id: bs_pending_restart state: true - - lambda: |- - id(relay_switches_packed) |= 0x01; // Set bit 0 - on_turn_off: - then: - - binary_sensor.template.publish: - id: bs_pending_restart - state: true - - lambda: |- - id(relay_switches_packed) &= ~0x01; // Clear bit 0 + - if: + condition: + - lambda: return x; // Turned on? + then: + - lambda: id(relay_switches_packed) |= 0x01; // Set bit 0 + else: + - lambda: id(relay_switches_packed) &= ~0x01; // Clear bit 0 - id: sw_relay_1 name: Relay 1 @@ -1547,103 +1545,99 @@ switch: platform: output restore_mode: ${RELAYS_SWITCH_RESTORE_MODE} internal: true - on_turn_on: + on_state: then: - script.execute: show_relay_status + on_turn_on: + then: - if: condition: - light.is_off: light_output_1 then: light.turn_on: light_output_1 - - lambda: |- - id(relay_switches_packed) |= 0x02; // Set bit 1 + - lambda: id(relay_switches_packed) |= 0x02; // Set bit 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 - - lambda: |- - id(relay_switches_packed) &= ~0x02; // Clear bit 1 + - lambda: id(relay_switches_packed) &= ~0x02; // Clear bit 1 - id: sw_relay_2 name: Relay 2 output: output_relay_2 platform: output restore_mode: ${RELAYS_SWITCH_RESTORE_MODE} internal: true - on_turn_on: + on_state: then: - script.execute: show_relay_status + on_turn_on: + then: - if: condition: - light.is_off: light_output_2 then: light.turn_on: light_output_2 - - lambda: |- - id(relay_switches_packed) |= 0x04; // Set bit 2 + - lambda: id(relay_switches_packed) |= 0x04; // Set bit 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 - - lambda: |- - id(relay_switches_packed) &= ~0x04; // Clear bit 2 + - lambda: id(relay_switches_packed) &= ~0x04; // Clear bit 2 - id: sw_relay_3 name: Relay 3 output: output_relay_3 platform: output restore_mode: ${RELAYS_SWITCH_RESTORE_MODE} internal: true - on_turn_on: + on_state: then: - script.execute: show_relay_status + on_turn_on: + then: - if: condition: - light.is_off: light_output_3 then: - light.turn_on: light_output_3 - - lambda: |- - id(relay_switches_packed) |= 0x08; // Set bit 3 + - lambda: id(relay_switches_packed) |= 0x08; // Set bit 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 - - lambda: |- - id(relay_switches_packed) &= ~0x08; // Clear bit 3 + - lambda: id(relay_switches_packed) &= ~0x08; // Clear bit 3 - id: sw_relay_4 name: Relay 4 output: output_relay_4 platform: output restore_mode: ${RELAYS_SWITCH_RESTORE_MODE} internal: true - on_turn_on: + on_state: then: - script.execute: show_relay_status + on_turn_on: + then: - if: condition: - light.is_off: light_output_4 then: - light.turn_on: light_output_4 - - lambda: |- - id(relay_switches_packed) |= 0x10; // Set bit 4 + - lambda: id(relay_switches_packed) |= 0x10; // Set bit 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 - - lambda: |- - id(relay_switches_packed) &= ~0x10; // Clear bit 4 + - lambda: id(relay_switches_packed) &= ~0x10; // Clear bit 4 ... diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_vibration.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_vibration.yaml index db1895e..2dba617 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_vibration.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_vibration.yaml @@ -151,20 +151,17 @@ switch: pin: GPIO21 restore_mode: ALWAYS_OFF internal: true - on_turn_on: + on_state: then: - binary_sensor.template.publish: id: bs_vibrating - state: ON # yamllint disable-line rule:truthy + state: !lambda return x; + on_turn_on: + then: - 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 ... From 8af692e82d3ab3f1a590e313de1a3158fdeef835 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Sun, 10 Aug 2025 22:59:29 +0200 Subject: [PATCH 38/50] Introduces `bs_boot_completed` to know the boot status To know when the boot was completed --- .../TX-Ultimate-Easy-ESPHome_core_common.yaml | 48 ++++++++++++++----- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core_common.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core_common.yaml index ca66660..7eace18 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core_common.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core_common.yaml @@ -27,7 +27,7 @@ substitutions: TX_MODEL_3_GANG_TEXT: "3 Gang" TX_MODEL_4_GANG_TEXT: "4 Gang" - DUMP_CONFIG_CALLER_DELAY: 10s # Delay to dump config after requested + DUMP_CONFIG_CALLER_DELAY: 5s # Delay to dump config after requested TAG_CORE_COMMON: core.common @@ -48,11 +48,18 @@ binary_sensor: device_class: problem on_state: then: - - lambda: |- - if (x) - ESP_LOGW("${TAG_CORE_COMMON}", "Pending restart: YES"); - else - ESP_LOGD("${TAG_CORE_COMMON}", "Pending restart: No"); + - script.execute: dump_config_caller + + - id: bs_boot_completed + name: Boot Completed + internal: false + disabled_by_default: false + platform: template + entity_category: diagnostic + device_class: connectivity + icon: mdi:rocket-launch + on_state: + then: - script.execute: dump_config_caller button: @@ -80,6 +87,12 @@ esphome: - -D TX_ULTIMATE_EASY_CORE_COMMON on_boot: + - priority: 1000 # Very early in boot process + then: + - binary_sensor.template.publish: + id: bs_boot_completed + state: OFF # yamllint disable-line rule:truthy + - priority: 750 then: - script.execute: restore_from_nvs @@ -106,16 +119,16 @@ external_components: - tx_ultimate_easy globals: - - id: is_us_model - type: bool - restore_value: true - initial_value: 'false' - - id: gang_count type: uint8_t restore_value: true initial_value: '0' + - id: is_us_model + type: bool + restore_value: true + initial_value: 'false' + logger: level: DEBUG @@ -143,9 +156,16 @@ script: - id: boot_done mode: restart then: + - script.wait: restore_from_nvs + - script.wait: boot_initialize + - script.wait: boot_sequence - script.execute: id: api_send_ha_event_boot type: done + - script.wait: api_send_ha_event_boot + - binary_sensor.template.publish: + id: bs_boot_completed + state: ON # yamllint disable-line rule:truthy - id: boot_initialize mode: restart @@ -208,6 +228,12 @@ script: ESP_LOGCONFIG("${TAG_CORE_COMMON}", "Gangs (detected): %" PRIu8 "-Gang%s", id(gang_count), id(gang_count) > 1 ? "s" : ""); + // Boot completion status + if (bs_boot_completed->state) + ESP_LOGCONFIG("${TAG_CORE_COMMON}", "Boot completed: Yes"); + else + ESP_LOGW("${TAG_CORE_COMMON}", "Boot completed: NO"); + // System state if (bs_pending_restart->state) ESP_LOGW("${TAG_CORE_COMMON}", "Pending restart: YES"); From 6d2de0d32bc4b751a7f52a3c1db3fcf1a080c098 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Sun, 10 Aug 2025 23:12:15 +0200 Subject: [PATCH 39/50] Guard against invalid gang_count when restoring index --- ESPHome/TX-Ultimate-Easy-ESPHome_core_common.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core_common.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core_common.yaml index 7eace18..2524545 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core_common.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core_common.yaml @@ -283,7 +283,9 @@ script: index: !lambda if (id(is_us_model)) return 1; else return 0; - select.set_index: id: sl_tx_model_gang - index: !lambda return (id(gang_count)-1); + index: !lambda |- + if (id(gang_count) < 1 || id(gang_count) > 4) return 0; // default to 1-Gang + return static_cast(id(gang_count)) - 1; select: - id: sl_tx_model_format From 03e0004e8ca370dd15c1af2e219aabe573e1067d Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Sun, 10 Aug 2025 23:29:25 +0200 Subject: [PATCH 40/50] Update lights when mode changes --- ...imate-Easy-ESPHome_standard_hw_relays.yaml | 274 ++++++++++++++++++ 1 file changed, 274 insertions(+) diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml index d7f67a0..1693501 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml @@ -1353,9 +1353,44 @@ select: icon: mdi:dots-square on_value: - lambda: |- + // Update packed value (existing code) id(relay_light_modes_packed) = (id(relay_light_modes_packed) & ~(0x0F << 0)) | ((id(sl_relay_1_light_mode_eu).active_index().value_or(1) & 0x0F) << 0); + // Handle mode transitions (only after boot is complete) + if (!id(bs_boot_completed)->state) return; // Skip during boot + + static std::string previous_mode = ""; // Static variable local to THIS lambda only + std::string current_mode = x; + + if (previous_mode.empty()) { + previous_mode = current_mode; // Initialize on first run after boot + return; + } + + if (previous_mode != "${LIGHT_MODE_DISABLED_TEXT}" && + current_mode == "${LIGHT_MODE_DISABLED_TEXT}") { + // Transitioning TO Disabled - turn off lights + ESP_LOGI("${TAG_STD_HW_RELAYS}", + "Relay 1 light mode changed to ${LIGHT_MODE_DISABLED_TEXT} - turning off lights"); + id(light_set_state)->execute(1, 0, false); // Group 1, relay index 0 + id(light_set_state)->execute(2, 0, false); // Group 2, relay index 0 + } else if (previous_mode == "${LIGHT_MODE_DISABLED_TEXT}" && + current_mode != "${LIGHT_MODE_DISABLED_TEXT}") { + // Transitioning FROM Disabled - update lights based on relay state + ESP_LOGI("${TAG_STD_HW_RELAYS}", + "Relay 1 light mode changed from ${LIGHT_MODE_DISABLED_TEXT} - updating lights"); + bool relay_state = id(sw_relay_1).state; + if (current_mode == "${LIGHT_MODE_BOTTOM_TEXT}" || current_mode == "${LIGHT_MODE_BOTH_TEXT}") { + id(light_set_state)->execute(1, 0, relay_state); + } + if (current_mode == "${LIGHT_MODE_TOP_TEXT}" || current_mode == "${LIGHT_MODE_BOTH_TEXT}") { + id(light_set_state)->execute(2, 0, relay_state); + } + } + + previous_mode = current_mode; + # US relay light mode template - &relay_light_mode_us id: sl_relay_1_light_mode_us @@ -1375,9 +1410,44 @@ select: icon: mdi:dots-square on_value: - lambda: |- + // Update packed value id(relay_light_modes_packed) = (id(relay_light_modes_packed) & ~(0x0F << 0)) | ((id(sl_relay_1_light_mode_us).active_index().value_or(1) & 0x0F) << 0); + // Handle mode transitions (only after boot is complete) + if (!id(bs_boot_completed)->state) return; // Skip during boot + + static std::string previous_mode = ""; + std::string current_mode = x; + + if (previous_mode.empty()) { + previous_mode = current_mode; // Initialize on first run after boot + return; + } + + if (previous_mode != "${LIGHT_MODE_DISABLED_TEXT}" && + current_mode == "${LIGHT_MODE_DISABLED_TEXT}") { + // Transitioning TO Disabled - turn off lights + ESP_LOGI("${TAG_STD_HW_RELAYS}", + "Relay 1 light mode changed to ${LIGHT_MODE_DISABLED_TEXT} - turning off lights"); + id(light_set_state)->execute(1, 0, false); // Group 1, relay index 0 + id(light_set_state)->execute(2, 0, false); // Group 2, relay index 0 + } else if (previous_mode == "${LIGHT_MODE_DISABLED_TEXT}" && + current_mode != "${LIGHT_MODE_DISABLED_TEXT}") { + // Transitioning FROM Disabled - update lights based on relay state + ESP_LOGI("${TAG_STD_HW_RELAYS}", + "Relay 1 light mode changed from ${LIGHT_MODE_DISABLED_TEXT} - updating lights"); + bool relay_state = id(sw_relay_1).state; + if (current_mode == "${LIGHT_MODE_LEFT_TEXT}" || current_mode == "${LIGHT_MODE_BOTH_TEXT}") { + id(light_set_state)->execute(1, 0, relay_state); + } + if (current_mode == "${LIGHT_MODE_RIGHT_TEXT}" || current_mode == "${LIGHT_MODE_BOTH_TEXT}") { + id(light_set_state)->execute(2, 0, relay_state); + } + } + + previous_mode = current_mode; + - id: sl_relay_1_mode name: Relay 1 display mode platform: template @@ -1406,17 +1476,87 @@ select: <<: *relay_light_mode_eu on_value: - lambda: |- + // Update packed value id(relay_light_modes_packed) = (id(relay_light_modes_packed) & ~(0x0F << 4)) | ((id(sl_relay_2_light_mode_eu).active_index().value_or(1) & 0x0F) << 4); + // Handle mode transitions (only after boot is complete) + if (!id(bs_boot_completed)->state) return; // Skip during boot + + static std::string previous_mode = ""; + std::string current_mode = x; + + if (previous_mode.empty()) { + previous_mode = current_mode; // Initialize on first run after boot + return; + } + + if (previous_mode != "${LIGHT_MODE_DISABLED_TEXT}" && + current_mode == "${LIGHT_MODE_DISABLED_TEXT}") { + // Transitioning TO Disabled - turn off lights + ESP_LOGI("${TAG_STD_HW_RELAYS}", + "Relay 2 light mode changed to ${LIGHT_MODE_DISABLED_TEXT} - turning off lights"); + id(light_set_state)->execute(1, 1, false); // Group 1, relay index 1 + id(light_set_state)->execute(2, 1, false); // Group 2, relay index 1 + } else if (previous_mode == "${LIGHT_MODE_DISABLED_TEXT}" && + current_mode != "${LIGHT_MODE_DISABLED_TEXT}") { + // Transitioning FROM Disabled - update lights based on relay state + ESP_LOGI("${TAG_STD_HW_RELAYS}", + "Relay 2 light mode changed from ${LIGHT_MODE_DISABLED_TEXT} - updating lights"); + bool relay_state = id(sw_relay_2).state; + if (current_mode == "${LIGHT_MODE_BOTTOM_TEXT}" || current_mode == "${LIGHT_MODE_BOTH_TEXT}") { + id(light_set_state)->execute(1, 1, relay_state); + } + if (current_mode == "${LIGHT_MODE_TOP_TEXT}" || current_mode == "${LIGHT_MODE_BOTH_TEXT}") { + id(light_set_state)->execute(2, 1, relay_state); + } + } + + previous_mode = current_mode; + - id: sl_relay_2_light_mode_us name: Relay 2 light mode US <<: *relay_light_mode_us on_value: - lambda: |- + // Update packed value id(relay_light_modes_packed) = (id(relay_light_modes_packed) & ~(0x0F << 4)) | ((id(sl_relay_2_light_mode_us).active_index().value_or(1) & 0x0F) << 4); + // Handle mode transitions (only after boot is complete) + if (!id(bs_boot_completed)->state) return; // Skip during boot + + static std::string previous_mode = ""; + std::string current_mode = x; + + if (previous_mode.empty()) { + previous_mode = current_mode; // Initialize on first run after boot + return; + } + + if (previous_mode != "${LIGHT_MODE_DISABLED_TEXT}" && + current_mode == "${LIGHT_MODE_DISABLED_TEXT}") { + // Transitioning TO Disabled - turn off lights + ESP_LOGI("${TAG_STD_HW_RELAYS}", + "Relay 2 light mode changed to ${LIGHT_MODE_DISABLED_TEXT} - turning off lights"); + id(light_set_state)->execute(1, 1, false); // Group 1, relay index 1 + id(light_set_state)->execute(2, 1, false); // Group 2, relay index 1 + } else if (previous_mode == "${LIGHT_MODE_DISABLED_TEXT}" && + current_mode != "${LIGHT_MODE_DISABLED_TEXT}") { + // Transitioning FROM Disabled - update lights based on relay state + ESP_LOGI("${TAG_STD_HW_RELAYS}", + "Relay 2 light mode changed from ${LIGHT_MODE_DISABLED_TEXT} - updating lights"); + bool relay_state = id(sw_relay_2).state; + if (current_mode == "${LIGHT_MODE_LEFT_TEXT}" || current_mode == "${LIGHT_MODE_BOTH_TEXT}") { + id(light_set_state)->execute(1, 1, relay_state); + } + if (current_mode == "${LIGHT_MODE_RIGHT_TEXT}" || current_mode == "${LIGHT_MODE_BOTH_TEXT}") { + id(light_set_state)->execute(2, 1, relay_state); + } + } + + previous_mode = current_mode; + - id: sl_relay_2_mode name: Relay 2 display mode platform: template @@ -1445,17 +1585,85 @@ select: <<: *relay_light_mode_eu on_value: - lambda: |- + // Update packed value id(relay_light_modes_packed) = (id(relay_light_modes_packed) & ~(0x0F << 8)) | ((id(sl_relay_3_light_mode_eu).active_index().value_or(1) & 0x0F) << 8); + // Handle mode transitions (only after boot is complete) + if (!id(bs_boot_completed)->state) return; // Skip during boot + + static std::string previous_mode = ""; + std::string current_mode = x; + + if (previous_mode.empty()) { + previous_mode = current_mode; // Initialize on first run after boot + return; + } + + if (previous_mode != "${LIGHT_MODE_DISABLED_TEXT}" && + current_mode == "${LIGHT_MODE_DISABLED_TEXT}") { + // Transitioning TO Disabled - turn off lights + ESP_LOGI("${TAG_STD_HW_RELAYS}", + "Relay 3 light mode changed to ${LIGHT_MODE_DISABLED_TEXT} - turning off lights"); + id(light_set_state)->execute(1, 2, false); // Group 1, relay index 2 + id(light_set_state)->execute(2, 2, false); // Group 2, relay index 2 + } else if (previous_mode == "${LIGHT_MODE_DISABLED_TEXT}" && + current_mode != "${LIGHT_MODE_DISABLED_TEXT}") { + // Transitioning FROM Disabled - update lights based on relay state + ESP_LOGI("${TAG_STD_HW_RELAYS}", + "Relay 3 light mode changed from ${LIGHT_MODE_DISABLED_TEXT} - updating lights"); + bool relay_state = id(sw_relay_3).state; + if (current_mode == "${LIGHT_MODE_BOTTOM_TEXT}" || current_mode == "${LIGHT_MODE_BOTH_TEXT}") { + id(light_set_state)->execute(1, 2, relay_state); + } + if (current_mode == "${LIGHT_MODE_TOP_TEXT}" || current_mode == "${LIGHT_MODE_BOTH_TEXT}") { + id(light_set_state)->execute(2, 2, relay_state); + } + } + + previous_mode = current_mode; + - id: sl_relay_3_light_mode_us name: Relay 3 light mode US <<: *relay_light_mode_us on_value: - lambda: |- + // Update packed value id(relay_light_modes_packed) = (id(relay_light_modes_packed) & ~(0x0F << 8)) | ((id(sl_relay_3_light_mode_us).active_index().value_or(1) & 0x0F) << 8); + // Handle mode transitions (only after boot is complete) + if (!id(bs_boot_completed)->state) return; // Skip during boot + + static std::string previous_mode = ""; + std::string current_mode = x; + + if (previous_mode.empty()) { + previous_mode = current_mode; // Initialize on first run after boot + return; + } + + if (previous_mode != "${LIGHT_MODE_DISABLED_TEXT}" && + current_mode == "${LIGHT_MODE_DISABLED_TEXT}") { + // Transitioning TO Disabled - turn off lights + ESP_LOGI("${TAG_STD_HW_RELAYS}", "Relay 3 light mode changed to ${LIGHT_MODE_DISABLED_TEXT} - turning off lights"); + id(light_set_state)->execute(1, 2, false); // Group 1, relay index 2 + id(light_set_state)->execute(2, 2, false); // Group 2, relay index 2 + } else if (previous_mode == "${LIGHT_MODE_DISABLED_TEXT}" && + current_mode != "${LIGHT_MODE_DISABLED_TEXT}") { + // Transitioning FROM Disabled - update lights based on relay state + ESP_LOGI("${TAG_STD_HW_RELAYS}", "Relay 3 light mode changed from ${LIGHT_MODE_DISABLED_TEXT} - updating lights"); + bool relay_state = id(sw_relay_3).state; + if (current_mode == "${LIGHT_MODE_LEFT_TEXT}" || current_mode == "${LIGHT_MODE_BOTH_TEXT}") { + id(light_set_state)->execute(1, 2, relay_state); + } + if (current_mode == "${LIGHT_MODE_RIGHT_TEXT}" || current_mode == "${LIGHT_MODE_BOTH_TEXT}") { + id(light_set_state)->execute(2, 2, relay_state); + } + } + + previous_mode = current_mode; + - id: sl_relay_3_mode name: Relay 3 display mode platform: template @@ -1484,17 +1692,83 @@ select: <<: *relay_light_mode_eu on_value: - lambda: |- + // Update packed value id(relay_light_modes_packed) = (id(relay_light_modes_packed) & ~(0x0F << 12)) | ((id(sl_relay_4_light_mode_eu).active_index().value_or(1) & 0x0F) << 12); + // Handle mode transitions (only after boot is complete) + if (!id(bs_boot_completed)->state) return; // Skip during boot + + static std::string previous_mode = ""; + std::string current_mode = x; + + if (previous_mode.empty()) { + previous_mode = current_mode; // Initialize on first run after boot + return; + } + + if (previous_mode != "${LIGHT_MODE_DISABLED_TEXT}" && + current_mode == "${LIGHT_MODE_DISABLED_TEXT}") { + // Transitioning TO Disabled - turn off lights + ESP_LOGI("${TAG_STD_HW_RELAYS}", "Relay 4 light mode changed to ${LIGHT_MODE_DISABLED_TEXT} - turning off lights"); + id(light_set_state)->execute(1, 3, false); // Group 1, relay index 3 + id(light_set_state)->execute(2, 3, false); // Group 2, relay index 3 + } else if (previous_mode == "${LIGHT_MODE_DISABLED_TEXT}" && + current_mode != "${LIGHT_MODE_DISABLED_TEXT}") { + // Transitioning FROM Disabled - update lights based on relay state + ESP_LOGI("${TAG_STD_HW_RELAYS}", "Relay 4 light mode changed from ${LIGHT_MODE_DISABLED_TEXT} - updating lights"); + bool relay_state = id(sw_relay_4).state; + if (current_mode == "${LIGHT_MODE_BOTTOM_TEXT}" || current_mode == "${LIGHT_MODE_BOTH_TEXT}") { + id(light_set_state)->execute(1, 3, relay_state); + } + if (current_mode == "${LIGHT_MODE_TOP_TEXT}" || current_mode == "${LIGHT_MODE_BOTH_TEXT}") { + id(light_set_state)->execute(2, 3, relay_state); + } + } + + previous_mode = current_mode; + - id: sl_relay_4_light_mode_us name: Relay 4 light mode US <<: *relay_light_mode_us on_value: - lambda: |- + // Update packed value id(relay_light_modes_packed) = (id(relay_light_modes_packed) & ~(0x0F << 12)) | ((id(sl_relay_4_light_mode_us).active_index().value_or(1) & 0x0F) << 12); + // Handle mode transitions (only after boot is complete) + if (!id(bs_boot_completed)->state) return; // Skip during boot + + static std::string previous_mode = ""; + std::string current_mode = x; + + if (previous_mode.empty()) { + previous_mode = current_mode; // Initialize on first run after boot + return; + } + + if (previous_mode != "${LIGHT_MODE_DISABLED_TEXT}" && + current_mode == "${LIGHT_MODE_DISABLED_TEXT}") { + // Transitioning TO Disabled - turn off lights + ESP_LOGI("${TAG_STD_HW_RELAYS}", "Relay 4 light mode changed to ${LIGHT_MODE_DISABLED_TEXT} - turning off lights"); + id(light_set_state)->execute(1, 3, false); // Group 1, relay index 3 + id(light_set_state)->execute(2, 3, false); // Group 2, relay index 3 + } else if (previous_mode == "${LIGHT_MODE_DISABLED_TEXT}" && + current_mode != "${LIGHT_MODE_DISABLED_TEXT}") { + // Transitioning FROM Disabled - update lights based on relay state + ESP_LOGI("${TAG_STD_HW_RELAYS}", "Relay 4 light mode changed from ${LIGHT_MODE_DISABLED_TEXT} - updating lights"); + bool relay_state = id(sw_relay_4).state; + if (current_mode == "${LIGHT_MODE_LEFT_TEXT}" || current_mode == "${LIGHT_MODE_BOTH_TEXT}") { + id(light_set_state)->execute(1, 3, relay_state); + } + if (current_mode == "${LIGHT_MODE_RIGHT_TEXT}" || current_mode == "${LIGHT_MODE_BOTH_TEXT}") { + id(light_set_state)->execute(2, 3, relay_state); + } + } + + previous_mode = current_mode; + - id: sl_relay_4_mode name: Relay 4 display mode platform: template From 0b430a7ce0122204ff9971f9ffa2b2ec24d4c6b1 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Sun, 10 Aug 2025 23:36:08 +0200 Subject: [PATCH 41/50] Lint --- ...imate-Easy-ESPHome_standard_hw_relays.yaml | 54 +++++++------------ 1 file changed, 20 insertions(+), 34 deletions(-) diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml index 1693501..23bb010 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml @@ -1368,15 +1368,13 @@ select: return; } - if (previous_mode != "${LIGHT_MODE_DISABLED_TEXT}" && - current_mode == "${LIGHT_MODE_DISABLED_TEXT}") { + if (previous_mode != "${LIGHT_MODE_DISABLED_TEXT}" && current_mode == "${LIGHT_MODE_DISABLED_TEXT}") { // Transitioning TO Disabled - turn off lights ESP_LOGI("${TAG_STD_HW_RELAYS}", "Relay 1 light mode changed to ${LIGHT_MODE_DISABLED_TEXT} - turning off lights"); id(light_set_state)->execute(1, 0, false); // Group 1, relay index 0 id(light_set_state)->execute(2, 0, false); // Group 2, relay index 0 - } else if (previous_mode == "${LIGHT_MODE_DISABLED_TEXT}" && - current_mode != "${LIGHT_MODE_DISABLED_TEXT}") { + } else if (previous_mode == "${LIGHT_MODE_DISABLED_TEXT}" && current_mode != "${LIGHT_MODE_DISABLED_TEXT}") { // Transitioning FROM Disabled - update lights based on relay state ESP_LOGI("${TAG_STD_HW_RELAYS}", "Relay 1 light mode changed from ${LIGHT_MODE_DISABLED_TEXT} - updating lights"); @@ -1425,15 +1423,13 @@ select: return; } - if (previous_mode != "${LIGHT_MODE_DISABLED_TEXT}" && - current_mode == "${LIGHT_MODE_DISABLED_TEXT}") { + if (previous_mode != "${LIGHT_MODE_DISABLED_TEXT}" && current_mode == "${LIGHT_MODE_DISABLED_TEXT}") { // Transitioning TO Disabled - turn off lights ESP_LOGI("${TAG_STD_HW_RELAYS}", "Relay 1 light mode changed to ${LIGHT_MODE_DISABLED_TEXT} - turning off lights"); id(light_set_state)->execute(1, 0, false); // Group 1, relay index 0 id(light_set_state)->execute(2, 0, false); // Group 2, relay index 0 - } else if (previous_mode == "${LIGHT_MODE_DISABLED_TEXT}" && - current_mode != "${LIGHT_MODE_DISABLED_TEXT}") { + } else if (previous_mode == "${LIGHT_MODE_DISABLED_TEXT}" && current_mode != "${LIGHT_MODE_DISABLED_TEXT}") { // Transitioning FROM Disabled - update lights based on relay state ESP_LOGI("${TAG_STD_HW_RELAYS}", "Relay 1 light mode changed from ${LIGHT_MODE_DISABLED_TEXT} - updating lights"); @@ -1491,15 +1487,13 @@ select: return; } - if (previous_mode != "${LIGHT_MODE_DISABLED_TEXT}" && - current_mode == "${LIGHT_MODE_DISABLED_TEXT}") { + if (previous_mode != "${LIGHT_MODE_DISABLED_TEXT}" && current_mode == "${LIGHT_MODE_DISABLED_TEXT}") { // Transitioning TO Disabled - turn off lights ESP_LOGI("${TAG_STD_HW_RELAYS}", "Relay 2 light mode changed to ${LIGHT_MODE_DISABLED_TEXT} - turning off lights"); id(light_set_state)->execute(1, 1, false); // Group 1, relay index 1 id(light_set_state)->execute(2, 1, false); // Group 2, relay index 1 - } else if (previous_mode == "${LIGHT_MODE_DISABLED_TEXT}" && - current_mode != "${LIGHT_MODE_DISABLED_TEXT}") { + } else if (previous_mode == "${LIGHT_MODE_DISABLED_TEXT}" && current_mode != "${LIGHT_MODE_DISABLED_TEXT}") { // Transitioning FROM Disabled - update lights based on relay state ESP_LOGI("${TAG_STD_HW_RELAYS}", "Relay 2 light mode changed from ${LIGHT_MODE_DISABLED_TEXT} - updating lights"); @@ -1534,15 +1528,13 @@ select: return; } - if (previous_mode != "${LIGHT_MODE_DISABLED_TEXT}" && - current_mode == "${LIGHT_MODE_DISABLED_TEXT}") { + if (previous_mode != "${LIGHT_MODE_DISABLED_TEXT}" && current_mode == "${LIGHT_MODE_DISABLED_TEXT}") { // Transitioning TO Disabled - turn off lights ESP_LOGI("${TAG_STD_HW_RELAYS}", "Relay 2 light mode changed to ${LIGHT_MODE_DISABLED_TEXT} - turning off lights"); id(light_set_state)->execute(1, 1, false); // Group 1, relay index 1 id(light_set_state)->execute(2, 1, false); // Group 2, relay index 1 - } else if (previous_mode == "${LIGHT_MODE_DISABLED_TEXT}" && - current_mode != "${LIGHT_MODE_DISABLED_TEXT}") { + } else if (previous_mode == "${LIGHT_MODE_DISABLED_TEXT}" && current_mode != "${LIGHT_MODE_DISABLED_TEXT}") { // Transitioning FROM Disabled - update lights based on relay state ESP_LOGI("${TAG_STD_HW_RELAYS}", "Relay 2 light mode changed from ${LIGHT_MODE_DISABLED_TEXT} - updating lights"); @@ -1600,15 +1592,13 @@ select: return; } - if (previous_mode != "${LIGHT_MODE_DISABLED_TEXT}" && - current_mode == "${LIGHT_MODE_DISABLED_TEXT}") { + if (previous_mode != "${LIGHT_MODE_DISABLED_TEXT}" && current_mode == "${LIGHT_MODE_DISABLED_TEXT}") { // Transitioning TO Disabled - turn off lights ESP_LOGI("${TAG_STD_HW_RELAYS}", "Relay 3 light mode changed to ${LIGHT_MODE_DISABLED_TEXT} - turning off lights"); id(light_set_state)->execute(1, 2, false); // Group 1, relay index 2 id(light_set_state)->execute(2, 2, false); // Group 2, relay index 2 - } else if (previous_mode == "${LIGHT_MODE_DISABLED_TEXT}" && - current_mode != "${LIGHT_MODE_DISABLED_TEXT}") { + } else if (previous_mode == "${LIGHT_MODE_DISABLED_TEXT}" && current_mode != "${LIGHT_MODE_DISABLED_TEXT}") { // Transitioning FROM Disabled - update lights based on relay state ESP_LOGI("${TAG_STD_HW_RELAYS}", "Relay 3 light mode changed from ${LIGHT_MODE_DISABLED_TEXT} - updating lights"); @@ -1643,16 +1633,16 @@ select: return; } - if (previous_mode != "${LIGHT_MODE_DISABLED_TEXT}" && - current_mode == "${LIGHT_MODE_DISABLED_TEXT}") { + if (previous_mode != "${LIGHT_MODE_DISABLED_TEXT}" && current_mode == "${LIGHT_MODE_DISABLED_TEXT}") { // Transitioning TO Disabled - turn off lights - ESP_LOGI("${TAG_STD_HW_RELAYS}", "Relay 3 light mode changed to ${LIGHT_MODE_DISABLED_TEXT} - turning off lights"); + ESP_LOGI("${TAG_STD_HW_RELAYS}", + "Relay 3 light mode changed to ${LIGHT_MODE_DISABLED_TEXT} - turning off lights"); id(light_set_state)->execute(1, 2, false); // Group 1, relay index 2 id(light_set_state)->execute(2, 2, false); // Group 2, relay index 2 - } else if (previous_mode == "${LIGHT_MODE_DISABLED_TEXT}" && - current_mode != "${LIGHT_MODE_DISABLED_TEXT}") { + } else if (previous_mode == "${LIGHT_MODE_DISABLED_TEXT}" && current_mode != "${LIGHT_MODE_DISABLED_TEXT}") { // Transitioning FROM Disabled - update lights based on relay state - ESP_LOGI("${TAG_STD_HW_RELAYS}", "Relay 3 light mode changed from ${LIGHT_MODE_DISABLED_TEXT} - updating lights"); + ESP_LOGI("${TAG_STD_HW_RELAYS}", + "Relay 3 light mode changed from ${LIGHT_MODE_DISABLED_TEXT} - updating lights"); bool relay_state = id(sw_relay_3).state; if (current_mode == "${LIGHT_MODE_LEFT_TEXT}" || current_mode == "${LIGHT_MODE_BOTH_TEXT}") { id(light_set_state)->execute(1, 2, relay_state); @@ -1707,14 +1697,12 @@ select: return; } - if (previous_mode != "${LIGHT_MODE_DISABLED_TEXT}" && - current_mode == "${LIGHT_MODE_DISABLED_TEXT}") { + if (previous_mode != "${LIGHT_MODE_DISABLED_TEXT}" && current_mode == "${LIGHT_MODE_DISABLED_TEXT}") { // Transitioning TO Disabled - turn off lights ESP_LOGI("${TAG_STD_HW_RELAYS}", "Relay 4 light mode changed to ${LIGHT_MODE_DISABLED_TEXT} - turning off lights"); id(light_set_state)->execute(1, 3, false); // Group 1, relay index 3 id(light_set_state)->execute(2, 3, false); // Group 2, relay index 3 - } else if (previous_mode == "${LIGHT_MODE_DISABLED_TEXT}" && - current_mode != "${LIGHT_MODE_DISABLED_TEXT}") { + } else if (previous_mode == "${LIGHT_MODE_DISABLED_TEXT}" && current_mode != "${LIGHT_MODE_DISABLED_TEXT}") { // Transitioning FROM Disabled - update lights based on relay state ESP_LOGI("${TAG_STD_HW_RELAYS}", "Relay 4 light mode changed from ${LIGHT_MODE_DISABLED_TEXT} - updating lights"); bool relay_state = id(sw_relay_4).state; @@ -1748,14 +1736,12 @@ select: return; } - if (previous_mode != "${LIGHT_MODE_DISABLED_TEXT}" && - current_mode == "${LIGHT_MODE_DISABLED_TEXT}") { + if (previous_mode != "${LIGHT_MODE_DISABLED_TEXT}" && current_mode == "${LIGHT_MODE_DISABLED_TEXT}") { // Transitioning TO Disabled - turn off lights ESP_LOGI("${TAG_STD_HW_RELAYS}", "Relay 4 light mode changed to ${LIGHT_MODE_DISABLED_TEXT} - turning off lights"); id(light_set_state)->execute(1, 3, false); // Group 1, relay index 3 id(light_set_state)->execute(2, 3, false); // Group 2, relay index 3 - } else if (previous_mode == "${LIGHT_MODE_DISABLED_TEXT}" && - current_mode != "${LIGHT_MODE_DISABLED_TEXT}") { + } else if (previous_mode == "${LIGHT_MODE_DISABLED_TEXT}" && current_mode != "${LIGHT_MODE_DISABLED_TEXT}") { // Transitioning FROM Disabled - update lights based on relay state ESP_LOGI("${TAG_STD_HW_RELAYS}", "Relay 4 light mode changed from ${LIGHT_MODE_DISABLED_TEXT} - updating lights"); bool relay_state = id(sw_relay_4).state; From 0690ae14d88965b08f4f52b7cd964baebd62a06d Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Sun, 10 Aug 2025 23:42:22 +0200 Subject: [PATCH 42/50] Test core under `dev` --- .github/workflows/validate_esphome.yml | 44 +++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/.github/workflows/validate_esphome.yml b/.github/workflows/validate_esphome.yml index a4f2363..ca29b9a 100644 --- a/.github/workflows/validate_esphome.yml +++ b/.github/workflows/validate_esphome.yml @@ -65,6 +65,19 @@ jobs: yaml-file: ".test/esphome_ard_core.yaml" version: latest + build_ard_core_dev: + name: "🏗️ Arduino Core (dev)" + needs: code_scan + if: success() && !github.event.inputs.skip_dev + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@main + - name: Build Arduino Core - dev + uses: esphome/build-action@main + with: + yaml-file: ".test/esphome_ard_core.yaml" + version: dev + build_ard_basic: name: "📦 Arduino Basic (latest)" needs: build_ard_core @@ -79,7 +92,7 @@ jobs: build_ard_basic_dev: name: "📦 Arduino Basic (dev)" - needs: build_ard_core + needs: build_ard_core_dev if: success() && !github.event.inputs.skip_dev runs-on: ubuntu-latest steps: @@ -164,6 +177,19 @@ jobs: yaml-file: ".test/esphome_idf_core.yaml" version: latest + build_idf_core_dev: + name: "🏗️ IDF Core (dev)" + needs: code_scan + if: success() && !github.event.inputs.skip_dev + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@main + - name: Build IDF Core - dev + uses: esphome/build-action@main + with: + yaml-file: ".test/esphome_idf_core.yaml" + version: dev + build_idf_basic: name: "📦 IDF Basic (latest)" needs: build_idf_core @@ -178,7 +204,7 @@ jobs: build_idf_basic_dev: name: "📦 IDF Basic (dev)" - needs: build_idf_core + needs: build_idf_core_dev if: success() && !github.event.inputs.skip_dev runs-on: ubuntu-latest steps: @@ -255,6 +281,7 @@ jobs: name: "📊 Arduino Framework" needs: - build_ard_core + - build_ard_core_dev - build_ard_basic - build_ard_basic_dev - build_ard_hw_relays @@ -281,9 +308,10 @@ jobs: # Count dev builds (if not skipped) if [ "${{ github.event.inputs.skip_dev }}" != "true" ]; then + [ "${{ needs.build_ard_core_dev.result }}" == "success" ] && SUCCESS=$((SUCCESS + 1)) [ "${{ needs.build_ard_basic_dev.result }}" == "success" ] && SUCCESS=$((SUCCESS + 1)) [ "${{ needs.build_ard_ble_proxy_dev.result }}" == "success" ] && SUCCESS=$((SUCCESS + 1)) - TOTAL=$((TOTAL + 2)) + TOTAL=$((TOTAL + 3)) fi echo "🤖 Arduino Framework: $SUCCESS/$TOTAL builds passed" @@ -299,6 +327,7 @@ jobs: name: "📊 ESP-IDF Framework" needs: - build_idf_core + - build_idf_core_dev - build_idf_basic - build_idf_basic_dev - build_idf_hw_relays @@ -325,9 +354,10 @@ jobs: # Count dev builds (if not skipped) if [ "${{ github.event.inputs.skip_dev }}" != "true" ]; then + [ "${{ needs.build_idf_core_dev.result }}" == "success" ] && SUCCESS=$((SUCCESS + 1)) [ "${{ needs.build_idf_basic_dev.result }}" == "success" ] && SUCCESS=$((SUCCESS + 1)) [ "${{ needs.build_idf_ble_proxy_dev.result }}" == "success" ] && SUCCESS=$((SUCCESS + 1)) - TOTAL=$((TOTAL + 2)) + TOTAL=$((TOTAL + 3)) fi echo "🔧 ESP-IDF Framework: $SUCCESS/$TOTAL builds passed" @@ -390,8 +420,10 @@ jobs: summary_dev: name: "📊 ESPHome (Dev)" needs: + - build_ard_core_dev - build_ard_basic_dev - build_ard_ble_proxy_dev + - build_idf_core_dev - build_idf_basic_dev - build_idf_ble_proxy_dev if: always() && !github.event.inputs.skip_dev @@ -400,13 +432,15 @@ jobs: - name: Dev ESPHome Results run: | SUCCESS=0 - TOTAL=4 + TOTAL=6 # Arduino Dev builds + [ "${{ needs.build_ard_core_dev.result }}" == "success" ] && SUCCESS=$((SUCCESS + 1)) [ "${{ needs.build_ard_basic_dev.result }}" == "success" ] && SUCCESS=$((SUCCESS + 1)) [ "${{ needs.build_ard_ble_proxy_dev.result }}" == "success" ] && SUCCESS=$((SUCCESS + 1)) # ESP-IDF Dev builds + [ "${{ needs.build_idf_core_dev.result }}" == "success" ] && SUCCESS=$((SUCCESS + 1)) [ "${{ needs.build_idf_basic_dev.result }}" == "success" ] && SUCCESS=$((SUCCESS + 1)) [ "${{ needs.build_idf_ble_proxy_dev.result }}" == "success" ] && SUCCESS=$((SUCCESS + 1)) From 65d08194aab0392e6f386f457d466320b1fa80c4 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Mon, 11 Aug 2025 23:55:32 +0200 Subject: [PATCH 43/50] nvs optimization for hw leds --- ...TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml | 156 +++++++++++++++++- ...imate-Easy-ESPHome_standard_hw_relays.yaml | 23 ++- 2 files changed, 169 insertions(+), 10 deletions(-) diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml index 9878e4f..5ecc15c 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml @@ -25,9 +25,9 @@ substitutions: LIGHT_TRANSITION_TURN_OFF: '0' # Transition time in msec LIGHT_BRIGHTNESS_TURN_ON: '0' # Brightness (1 to 100%, 0 to not set it) - LIGHT_FULL_RESTORE_MODE: RESTORE_DEFAULT_OFF - LIGHT_SIDES_RESTORE_MODE: RESTORE_DEFAULT_OFF - LIGHT_INDIVIDUAL_RESTORE_MODE: RESTORE_DEFAULT_OFF + LIGHT_FULL_RESTORE_MODE: ALWAYS_OFF + LIGHT_SIDES_RESTORE_MODE: ALWAYS_OFF + LIGHT_INDIVIDUAL_RESTORE_MODE: ALWAYS_OFF LIGHT_ATTRS_DEFAULT: '0x64FFFFFF' # brightness=100, RGB=255,255,255 LIGHT_DUAL_ATTRS_DEFAULT: '0x64FFFFFF64FFFFFF' # Both lights: brightness=100, RGB=255,255,255 @@ -39,6 +39,46 @@ esphome: build_flags: - -D TX_ULTIMATE_EASY_CORE_HW_LEDS +external_components: + - source: github://pr#10195 # To-do: Remove this when this PR is merged + components: [light] + refresh: 1h + +globals: + # light_full with effect support + - id: led_full_attrs + type: uint32_t + restore_value: true + initial_value: '${LIGHT_ATTRS_DEFAULT}' + # Basic attributes: brightness + RGB + + # For light_full effects + - id: led_full_effect_packed + type: uint32_t + restore_value: true + initial_value: '0x00000000' + # Stores: effect_index(16 bits) + effect_speed(8 bits) + reserved(8 bits) + # 0 = no effect/solid color, 1-10 = effect indices + + - id: led_main_packed_attrs + type: uint64_t + restore_value: true + initial_value: '${LIGHT_DUAL_ATTRS_DEFAULT}' + # light_eu(group1) + light_us(group2) - the main effect lights + + - id: led_main_packed_effect + type: uint32_t + restore_value: true + initial_value: '0x00000000' + # Shared between light_eu and light_us + + # Main user-facing LED lights only + - id: led_sides_packed_attrs + type: uint64_t + restore_value: true + initial_value: '${LIGHT_DUAL_ATTRS_DEFAULT}' + # EU: bottom(group1) + top(group2) OR US: left(group1) + right(group2) + light: # These lights are controlling the LEDs on the device - id: light_full @@ -108,6 +148,17 @@ light: - id: light_full from: 0 to: 27 + on_state: + then: + - script.execute: + id: light_attributes_save_dual + light_ptr: !lambda return id(light_eu); + attr_global_ptr: !lambda return &id(led_main_packed_attrs); + group_index: 1 + - script.execute: + id: light_effect_save + light_ptr: !lambda return id(light_eu); + effect_global_ptr: !lambda return &id(led_main_packed_effect); - &light_partition_with_effects_us id: light_us @@ -160,6 +211,17 @@ light: - id: light_full from: 0 to: 31 + on_state: + then: + - script.execute: + id: light_attributes_save_dual + light_ptr: !lambda return id(light_us); + attr_global_ptr: !lambda return &id(led_main_packed_attrs); + group_index: 2 + - script.execute: + id: light_effect_save + light_ptr: !lambda return id(light_us); + effect_global_ptr: !lambda return &id(led_main_packed_effect); - &light_partition_no_effects_disabled id: light_eu_bottom @@ -174,6 +236,13 @@ light: from: 6 to: 12 reversed: true + on_state: + - script.execute: + id: light_attributes_save_dual + light_ptr: !lambda return id(light_eu_bottom); + attr_global_ptr: !lambda return &id(led_sides_packed_attrs); + group_index: 1 + - id: light_eu_left name: Light - Left EU <<: *light_partition_no_effects_disabled @@ -182,6 +251,12 @@ light: from: 13 to: 19 reversed: true + on_state: + - script.execute: + id: light_attributes_save_dual + light_ptr: !lambda return id(light_eu_left); + attr_global_ptr: !lambda return &id(led_sides_packed_attrs); + group_index: 1 - id: light_eu_right name: Light - Right EU <<: *light_partition_no_effects_disabled @@ -194,6 +269,12 @@ light: from: 0 to: 5 reversed: false + on_state: + - script.execute: + id: light_attributes_save_dual + light_ptr: !lambda return id(light_eu_right); + attr_global_ptr: !lambda return &id(led_sides_packed_attrs); + group_index: 2 - id: light_eu_top name: Light - Top EU <<: *light_partition_no_effects_disabled @@ -202,6 +283,12 @@ light: from: 20 to: 26 reversed: false + on_state: + - script.execute: + id: light_attributes_save_dual + light_ptr: !lambda return id(light_eu_top); + attr_global_ptr: !lambda return &id(led_sides_packed_attrs); + group_index: 2 - id: light_us_bottom name: Light - Bottom US <<: *light_partition_no_effects_disabled @@ -210,6 +297,12 @@ light: from: 9 to: 14 reversed: true + on_state: + - script.execute: + id: light_attributes_save_dual + light_ptr: !lambda return id(light_us_bottom); + attr_global_ptr: !lambda return &id(led_sides_packed_attrs); + group_index: 1 - id: light_us_left name: Light - Left US <<: *light_partition_no_effects_disabled @@ -218,6 +311,12 @@ light: from: 15 to: 24 reversed: true + on_state: + - script.execute: + id: light_attributes_save_dual + light_ptr: !lambda return id(light_us_left); + attr_global_ptr: !lambda return &id(led_sides_packed_attrs); + group_index: 1 - id: light_us_right name: Light - Right US <<: *light_partition_no_effects_disabled @@ -230,6 +329,12 @@ light: from: 0 to: 8 reversed: false + on_state: + - script.execute: + id: light_attributes_save_dual + light_ptr: !lambda return id(light_us_right); + attr_global_ptr: !lambda return &id(led_sides_packed_attrs); + group_index: 2 - id: light_us_top name: Light - Top US <<: *light_partition_no_effects_disabled @@ -238,6 +343,12 @@ light: from: 25 to: 30 reversed: false + on_state: + - script.execute: + id: light_attributes_save_dual + light_ptr: !lambda return id(light_us_top); + attr_global_ptr: !lambda return &id(led_sides_packed_attrs); + group_index: 2 - &light_partition_individual_led id: light_00 @@ -642,4 +753,43 @@ script: *attr_global_ptr = tx_ultimate_easy::pack_dual_light_attributes(existing.first, new_attrs); } + - id: light_effect_save + mode: queued + parameters: + light_ptr: light::LightState* + effect_global_ptr: uint32_t* + then: + - lambda: |- + if (!light_ptr || !effect_global_ptr) return; + + uint32_t effect_index = light_ptr->get_current_effect_index(); + // Store effect index (16 bits) + reserved space for future use + *effect_global_ptr = effect_index & 0xFFFF; + + ESP_LOGD("${TAG_CORE_HW_LEDS}", "Saved effect index: %d", effect_index); + + - id: light_effect_restore + mode: parallel + parameters: + light_ptr: light::LightState* + effect_global_ptr: uint32_t* + then: + - lambda: |- + if (!light_ptr || !effect_global_ptr) return; + + uint32_t effect_index = *effect_global_ptr & 0xFFFF; + + if (effect_index == 0) return; // No effect + if (effect_index > light_ptr->get_effect_count()) { + ESP_LOGW("${TAG_CORE_HW_LEDS}", "Invalid effect index: %d (max: %d)", + effect_index, light_ptr->get_effect_count()); + return; + } + + auto call = light_ptr->turn_on(); + call.set_effect(effect_index); + call.perform(); + + ESP_LOGI("${TAG_CORE_HW_LEDS}", "Restored effect: %s (index=%d)", + light_ptr->get_effect_name_by_index(effect_index).c_str(), effect_index); ... diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml index 23bb010..8fe7287 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml @@ -96,7 +96,7 @@ globals: type: uint16_t restore_value: true initial_value: '0' # Binary: 0000000000000000 = all relay modes set to option 0 (default action - switch) - # Bit layout: [unused][relay4(4)][relay3(4)][relay2(4)][relay1(4)] + # Bit layout: [relay4(4)][relay3(4)][relay2(4)][relay1(4)] # 16-bit value with 4 bits per relay: 0=Switch, 1=Light, 2=Not in use, 3-15=Reserved # Packed switch states - replaces 5 separate switches @@ -104,7 +104,7 @@ globals: type: uint8_t restore_value: true initial_value: '0' # All switches default to OFF - # Bit layout: [unused(3)][sw_relay4][sw_relay3][sw_relay2][sw_relay1][sw_expose_leds] + # Bit layout: [sw_relay4][sw_relay3][sw_relay2][sw_relay1][sw_expose_leds] light: # These lights are used for the physical relays to be shown as lights @@ -133,6 +133,7 @@ light: output: output_relay_2 platform: binary internal: true + restore_mode: ${RELAYS_LIGHT_RESTORE_MODE} on_turn_on: then: - if: @@ -152,6 +153,7 @@ light: output: output_relay_3 platform: binary internal: true + restore_mode: ${RELAYS_LIGHT_RESTORE_MODE} on_turn_on: then: - if: @@ -171,6 +173,7 @@ light: output: output_relay_4 platform: binary internal: true + restore_mode: ${RELAYS_LIGHT_RESTORE_MODE} on_turn_on: then: - if: @@ -910,8 +913,8 @@ script: // Set indicator (either switch or light) visible on Home Assistant 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); + light_output_1->set_internal(relay_mode_index.value() != 1); + sw_relay_1->set_internal(relay_mode_index.value() != 0); } relay_mode_index = sl_relay_2_mode->active_index(); if (relay_mode_index.has_value()) { @@ -1119,11 +1122,13 @@ script: break; case 2: if (light_index >= id(gb_lights_2).size()) { - ESP_LOGE("${TAG_STD_HW_RELAYS}", "Invalid set with %s light for Relay %" PRIu8, light_mode_group_2.c_str(), light_index+1); + ESP_LOGE("${TAG_STD_HW_RELAYS}", + "Invalid set with %s light for Relay %" PRIu8, light_mode_group_2.c_str(), light_index+1); return; } if (state and !id(gb_lights_2)[light_index]->current_values.is_on()) { - ESP_LOGI("${TAG_STD_HW_RELAYS}", "Turn-on %s light for Relay %" PRIu8, light_mode_group_2.c_str(), light_index+1); + ESP_LOGI("${TAG_STD_HW_RELAYS}", + "Turn-on %s light for Relay %" PRIu8, light_mode_group_2.c_str(), light_index+1); auto call = id(gb_lights_2)[light_index]->turn_on(); if (LIGHT_TRANSITION_TURN_ON > 0) call.set_transition_length(LIGHT_TRANSITION_TURN_ON); // in ms @@ -1131,13 +1136,17 @@ script: call.set_brightness(LIGHT_BRIGHTNESS_TURN_ON / 100.0f); call.perform(); } else if (!state and id(gb_lights_2)[light_index]->current_values.is_on()) { - ESP_LOGI("${TAG_STD_HW_RELAYS}", "Turn-off %s light for Relay %" PRIu8, light_mode_group_2.c_str(), light_index+1); + ESP_LOGI("${TAG_STD_HW_RELAYS}", + "Turn-off %s light for Relay %" PRIu8, light_mode_group_2.c_str(), light_index+1); auto call = id(gb_lights_2)[light_index]->turn_off(); if (LIGHT_TRANSITION_TURN_OFF > 0) call.set_transition_length(LIGHT_TRANSITION_TURN_OFF); // in ms call.perform(); } break; + default: + ESP_LOGE("${TAG_STD_HW_RELAYS}", "Invalid light_group=%" PRIu8 " (expected 1 or 2)", light_group); + break; } - id: !extend restore_from_nvs From 67d1c2cdceee898ae9b68bc281ac32b0e3fec601 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Tue, 12 Aug 2025 00:00:33 +0200 Subject: [PATCH 44/50] Call to `restore_from_nvs` --- ...TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml index 5ecc15c..6529ead 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml @@ -792,4 +792,59 @@ script: ESP_LOGI("${TAG_CORE_HW_LEDS}", "Restored effect: %s (index=%d)", light_ptr->get_effect_name_by_index(effect_index).c_str(), effect_index); + + - id: !extend restore_from_nvs + then: + # LED restoration + - if: + condition: + lambda: return id(is_us_model); + then: + # US model restoration + - script.execute: + id: light_attributes_restore_dual + light_ptr: !lambda return id(light_us); + attr_global_ptr: !lambda return &id(led_main_packed_attrs); + group_index: 2 + - script.execute: + id: light_effect_restore + light_ptr: !lambda return id(light_us); + effect_global_ptr: !lambda return &id(led_main_packed_effect); + - script.execute: + id: light_attributes_restore_dual + light_ptr: !lambda return id(light_us_left); + attr_global_ptr: !lambda return &id(led_sides_packed_attrs); + group_index: 1 + - script.execute: + id: light_attributes_restore_dual + light_ptr: !lambda return id(light_us_right); + attr_global_ptr: !lambda return &id(led_sides_packed_attrs); + group_index: 2 + else: + # EU model restoration + - script.execute: + id: light_attributes_restore_dual + light_ptr: !lambda return id(light_eu); + attr_global_ptr: !lambda return &id(led_main_packed_attrs); + group_index: 1 + - script.execute: + id: light_effect_restore + light_ptr: !lambda return id(light_eu); + effect_global_ptr: !lambda return &id(led_main_packed_effect); + - script.execute: + id: light_attributes_restore_dual + light_ptr: !lambda return id(light_eu_bottom); + attr_global_ptr: !lambda return &id(led_sides_packed_attrs); + group_index: 1 + - script.execute: + id: light_attributes_restore_dual + light_ptr: !lambda return id(light_eu_top); + attr_global_ptr: !lambda return &id(led_sides_packed_attrs); + group_index: 2 + + # Light full restoration + - script.execute: + id: light_attributes_restore + light_ptr: !lambda return id(light_full); + attr_global_ptr: !lambda return &id(led_full_attrs); ... From 22a99017fa1719b3b0c47f3a497a2b9f696b5adb Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Tue, 12 Aug 2025 00:04:21 +0200 Subject: [PATCH 45/50] Update .github/workflows/validate_esphome.yml Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .github/workflows/validate_esphome.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/validate_esphome.yml b/.github/workflows/validate_esphome.yml index ca29b9a..059fde7 100644 --- a/.github/workflows/validate_esphome.yml +++ b/.github/workflows/validate_esphome.yml @@ -68,7 +68,7 @@ jobs: build_ard_core_dev: name: "🏗️ Arduino Core (dev)" needs: code_scan - if: success() && !github.event.inputs.skip_dev + if: ${{ success() && (github.event_name != 'workflow_dispatch' || github.event.inputs.skip_dev != 'true') }} runs-on: ubuntu-latest steps: - uses: actions/checkout@main @@ -77,7 +77,6 @@ jobs: with: yaml-file: ".test/esphome_ard_core.yaml" version: dev - build_ard_basic: name: "📦 Arduino Basic (latest)" needs: build_ard_core From 15c7ddc4b415998b6880819b14cc715dd5a6bf9a Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Tue, 12 Aug 2025 00:08:53 +0200 Subject: [PATCH 46/50] Fix dev job gating for IDF core (dev) --- .github/workflows/validate_esphome.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/validate_esphome.yml b/.github/workflows/validate_esphome.yml index 059fde7..d3063bc 100644 --- a/.github/workflows/validate_esphome.yml +++ b/.github/workflows/validate_esphome.yml @@ -92,7 +92,7 @@ jobs: build_ard_basic_dev: name: "📦 Arduino Basic (dev)" needs: build_ard_core_dev - if: success() && !github.event.inputs.skip_dev + if: ${{ success() && (github.event_name != 'workflow_dispatch' || github.event.inputs.skip_dev != 'true') }} runs-on: ubuntu-latest steps: - uses: actions/checkout@main @@ -153,7 +153,7 @@ jobs: build_ard_ble_proxy_dev: name: "⚡ Arduino BLE Proxy (dev)" needs: build_ard_basic_dev - if: success() && !github.event.inputs.skip_dev + if: ${{ success() && (github.event_name != 'workflow_dispatch' || github.event.inputs.skip_dev != 'true') }} runs-on: ubuntu-latest steps: - uses: actions/checkout@main @@ -179,7 +179,7 @@ jobs: build_idf_core_dev: name: "🏗️ IDF Core (dev)" needs: code_scan - if: success() && !github.event.inputs.skip_dev + if: ${{ success() && (github.event_name != 'workflow_dispatch' || github.event.inputs.skip_dev != 'true') }} runs-on: ubuntu-latest steps: - uses: actions/checkout@main @@ -204,7 +204,7 @@ jobs: build_idf_basic_dev: name: "📦 IDF Basic (dev)" needs: build_idf_core_dev - if: success() && !github.event.inputs.skip_dev + if: ${{ success() && (github.event_name != 'workflow_dispatch' || github.event.inputs.skip_dev != 'true') }} runs-on: ubuntu-latest steps: - uses: actions/checkout@main @@ -265,7 +265,7 @@ jobs: build_idf_ble_proxy_dev: name: "⚡ IDF BLE Proxy (dev)" needs: build_idf_basic_dev - if: success() && !github.event.inputs.skip_dev + if: ${{ success() && (github.event_name != 'workflow_dispatch' || github.event.inputs.skip_dev != 'true') }} runs-on: ubuntu-latest steps: - uses: actions/checkout@main From 8943dc7ea9f52b165466457df2f1259eb3b61b99 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Thu, 18 Sep 2025 06:20:14 +0200 Subject: [PATCH 47/50] Req v2025.8.0 --- ESPHome/TX-Ultimate-Easy-ESPHome_core_common.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core_common.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core_common.yaml index 2524545..1e7531f 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core_common.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core_common.yaml @@ -78,7 +78,7 @@ esphome: name: ${name} friendly_name: ${friendly_name} comment: TX Ultimate Easy - min_version: 2025.8.0-dev + min_version: 2025.8.0 project: name: "edwardtfn.tx_ultimate_easy" version: ${version} From 63c98fcc68e1da2c864fda242f984a23db52ae81 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Thu, 18 Sep 2025 19:48:32 +0200 Subject: [PATCH 48/50] Remove reference to PR #10195 --- ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml index 6529ead..21ed5af 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml @@ -39,11 +39,6 @@ esphome: build_flags: - -D TX_ULTIMATE_EASY_CORE_HW_LEDS -external_components: - - source: github://pr#10195 # To-do: Remove this when this PR is merged - components: [light] - refresh: 1h - globals: # light_full with effect support - id: led_full_attrs From e2b4fa41868d8bc3d3de75cca50603ad3c6cf56c Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Tue, 30 Sep 2025 09:44:06 +0200 Subject: [PATCH 49/50] Do not turn light on when restoring state --- ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml index 21ed5af..a345ea6 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml @@ -781,7 +781,7 @@ script: return; } - auto call = light_ptr->turn_on(); + auto call = light_ptr->make_call(); call.set_effect(effect_index); call.perform(); From 6ffa73b321aa8cb1c2cc2ec04fd8c89f44ec8812 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Fri, 3 Oct 2025 07:40:20 +0200 Subject: [PATCH 50/50] Update .github/workflows/validate_esphome.yml Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .github/workflows/validate_esphome.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/validate_esphome.yml b/.github/workflows/validate_esphome.yml index d3063bc..0288479 100644 --- a/.github/workflows/validate_esphome.yml +++ b/.github/workflows/validate_esphome.yml @@ -425,7 +425,7 @@ jobs: - build_idf_core_dev - build_idf_basic_dev - build_idf_ble_proxy_dev - if: always() && !github.event.inputs.skip_dev + if: ${{ always() && (github.event_name != 'workflow_dispatch' || github.event.inputs.skip_dev != 'true') }} runs-on: ubuntu-latest steps: - name: Dev ESPHome Results