Compare commits
29 Commits
v2025.8.2d
...
v2025.10.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd39fee788 | ||
|
|
d9f361b404 | ||
|
|
6ffa73b321 | ||
|
|
e2b4fa4186 | ||
|
|
63c98fcc68 | ||
|
|
8943dc7ea9 | ||
|
|
15c7ddc4b4 | ||
|
|
22a99017fa | ||
|
|
67d1c2cdce | ||
|
|
65d08194aa | ||
|
|
0690ae14d8 | ||
|
|
0b430a7ce0 | ||
|
|
03e0004e8c | ||
|
|
6d2de0d32b | ||
|
|
8af692e82d | ||
|
|
fc135875c5 | ||
|
|
aae3eaa204 | ||
|
|
fe745c0132 | ||
|
|
5757714b31 | ||
|
|
6520b8050e | ||
|
|
a10edb3d06 | ||
|
|
1df58e162f | ||
|
|
d905b69ad2 | ||
|
|
462b131cb6 | ||
|
|
cf8590b460 | ||
|
|
eca54c01ed | ||
|
|
0d0de08e19 | ||
|
|
3557c847b2 | ||
|
|
dbbbc4040d |
53
.github/workflows/validate_esphome.yml
vendored
53
.github/workflows/validate_esphome.yml
vendored
@@ -65,6 +65,18 @@ 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_name != 'workflow_dispatch' || github.event.inputs.skip_dev != 'true') }}
|
||||
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,8 +91,8 @@ jobs:
|
||||
|
||||
build_ard_basic_dev:
|
||||
name: "📦 Arduino Basic (dev)"
|
||||
needs: build_ard_core
|
||||
if: success() && !github.event.inputs.skip_dev
|
||||
needs: build_ard_core_dev
|
||||
if: ${{ success() && (github.event_name != 'workflow_dispatch' || github.event.inputs.skip_dev != 'true') }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@main
|
||||
@@ -141,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
|
||||
@@ -164,6 +176,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_name != 'workflow_dispatch' || github.event.inputs.skip_dev != 'true') }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@main
|
||||
- name: Build IDF Core - dev
|
||||
uses: esphome/build-action@main
|
||||
with:
|
||||
yaml-file: ".test/esphome_idf_core.yaml"
|
||||
version: dev
|
||||
|
||||
build_idf_basic:
|
||||
name: "📦 IDF Basic (latest)"
|
||||
needs: build_idf_core
|
||||
@@ -178,8 +203,8 @@ jobs:
|
||||
|
||||
build_idf_basic_dev:
|
||||
name: "📦 IDF Basic (dev)"
|
||||
needs: build_idf_core
|
||||
if: success() && !github.event.inputs.skip_dev
|
||||
needs: build_idf_core_dev
|
||||
if: ${{ success() && (github.event_name != 'workflow_dispatch' || github.event.inputs.skip_dev != 'true') }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@main
|
||||
@@ -240,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
|
||||
@@ -255,6 +280,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 +307,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 +326,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 +353,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,23 +419,27 @@ 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
|
||||
if: ${{ always() && (github.event_name != 'workflow_dispatch' || github.event.inputs.skip_dev != 'true') }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Dev ESPHome Results
|
||||
run: |
|
||||
SUCCESS=0
|
||||
TOTAL=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))
|
||||
|
||||
|
||||
@@ -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:
|
||||
@@ -71,6 +78,7 @@ esphome:
|
||||
name: ${name}
|
||||
friendly_name: ${friendly_name}
|
||||
comment: TX Ultimate Easy
|
||||
min_version: 2025.8.0
|
||||
project:
|
||||
name: "edwardtfn.tx_ultimate_easy"
|
||||
version: ${version}
|
||||
@@ -79,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
|
||||
@@ -105,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
|
||||
|
||||
@@ -142,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
|
||||
@@ -207,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");
|
||||
@@ -256,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<uint8_t>(id(gang_count)) - 1;
|
||||
|
||||
select:
|
||||
- id: sl_tx_model_format
|
||||
|
||||
@@ -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,41 @@ esphome:
|
||||
build_flags:
|
||||
- -D TX_ULTIMATE_EASY_CORE_HW_LEDS
|
||||
|
||||
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 +143,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 +206,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 +231,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 +246,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 +264,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 +278,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 +292,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 +306,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 +324,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 +338,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
|
||||
@@ -494,27 +600,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}");
|
||||
@@ -552,6 +637,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);
|
||||
@@ -568,6 +661,18 @@ 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;
|
||||
}
|
||||
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;
|
||||
|
||||
@@ -575,6 +680,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
|
||||
@@ -583,6 +690,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),
|
||||
@@ -596,13 +711,25 @@ 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*
|
||||
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;
|
||||
}
|
||||
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),
|
||||
@@ -621,58 +748,98 @@ 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
|
||||
- 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;
|
||||
|
||||
# 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
|
||||
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->make_call();
|
||||
call.set_effect(effect_index);
|
||||
call.perform();
|
||||
|
||||
ESP_LOGI("${TAG_CORE_HW_LEDS}", "Restored effect: %s (index=%d)",
|
||||
light_ptr->get_effect_name_by_index(effect_index).c_str(), effect_index);
|
||||
|
||||
- id: !extend restore_from_nvs
|
||||
then:
|
||||
# LED restoration
|
||||
- if:
|
||||
condition:
|
||||
lambda: return id(is_us_model);
|
||||
then:
|
||||
# US model restoration
|
||||
- script.execute:
|
||||
id: light_attributes_restore_dual
|
||||
light_ptr: !lambda return id(light_us);
|
||||
attr_global_ptr: !lambda return &id(led_main_packed_attrs);
|
||||
group_index: 2
|
||||
- script.execute:
|
||||
id: light_effect_restore
|
||||
light_ptr: !lambda return id(light_us);
|
||||
effect_global_ptr: !lambda return &id(led_main_packed_effect);
|
||||
- script.execute:
|
||||
id: light_attributes_restore_dual
|
||||
light_ptr: !lambda return id(light_us_left);
|
||||
attr_global_ptr: !lambda return &id(led_sides_packed_attrs);
|
||||
group_index: 1
|
||||
- script.execute:
|
||||
id: light_attributes_restore_dual
|
||||
light_ptr: !lambda return id(light_us_right);
|
||||
attr_global_ptr: !lambda return &id(led_sides_packed_attrs);
|
||||
group_index: 2
|
||||
else:
|
||||
# EU model restoration
|
||||
- script.execute:
|
||||
id: light_attributes_restore_dual
|
||||
light_ptr: !lambda return id(light_eu);
|
||||
attr_global_ptr: !lambda return &id(led_main_packed_attrs);
|
||||
group_index: 1
|
||||
- script.execute:
|
||||
id: light_effect_restore
|
||||
light_ptr: !lambda return id(light_eu);
|
||||
effect_global_ptr: !lambda return &id(led_main_packed_effect);
|
||||
- script.execute:
|
||||
id: light_attributes_restore_dual
|
||||
light_ptr: !lambda return id(light_eu_bottom);
|
||||
attr_global_ptr: !lambda return &id(led_sides_packed_attrs);
|
||||
group_index: 1
|
||||
- script.execute:
|
||||
id: light_attributes_restore_dual
|
||||
light_ptr: !lambda return id(light_eu_top);
|
||||
attr_global_ptr: !lambda return &id(led_sides_packed_attrs);
|
||||
group_index: 2
|
||||
|
||||
# Light full restoration
|
||||
- script.execute:
|
||||
id: light_attributes_restore
|
||||
light_ptr: !lambda return id(light_full);
|
||||
attr_global_ptr: !lambda return &id(led_full_attrs);
|
||||
...
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -26,6 +31,30 @@ esphome:
|
||||
- -D TX_ULTIMATE_EASY_STANDARD_HW_RELAYS
|
||||
|
||||
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,12 +83,20 @@ globals:
|
||||
type: std::vector<light::LightState*>
|
||||
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
|
||||
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
|
||||
@@ -67,31 +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]
|
||||
|
||||
- &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
|
||||
# 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
|
||||
@@ -100,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:
|
||||
@@ -120,6 +133,7 @@ light:
|
||||
output: output_relay_2
|
||||
platform: binary
|
||||
internal: true
|
||||
restore_mode: ${RELAYS_LIGHT_RESTORE_MODE}
|
||||
on_turn_on:
|
||||
then:
|
||||
- if:
|
||||
@@ -139,6 +153,7 @@ light:
|
||||
output: output_relay_3
|
||||
platform: binary
|
||||
internal: true
|
||||
restore_mode: ${RELAYS_LIGHT_RESTORE_MODE}
|
||||
on_turn_on:
|
||||
then:
|
||||
- if:
|
||||
@@ -158,6 +173,7 @@ light:
|
||||
output: output_relay_4
|
||||
platform: binary
|
||||
internal: true
|
||||
restore_mode: ${RELAYS_LIGHT_RESTORE_MODE}
|
||||
on_turn_on:
|
||||
then:
|
||||
- if:
|
||||
@@ -181,7 +197,7 @@ light:
|
||||
internal: true
|
||||
disabled_by_default: false
|
||||
default_transition_length: ${default_transition_length}
|
||||
restore_mode: ${LIGHT_RELAYS_RESTORE_MODE}
|
||||
restore_mode: ${RELAYS_LIGHT_PARTITION_RESTORE_MODE}
|
||||
segments:
|
||||
- id: light_full
|
||||
from: 9
|
||||
@@ -897,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()) {
|
||||
@@ -1028,6 +1044,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
|
||||
@@ -1087,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
|
||||
@@ -1099,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
|
||||
@@ -1124,42 +1165,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
|
||||
@@ -1294,6 +1343,116 @@ 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: false
|
||||
internal: true
|
||||
entity_category: config
|
||||
disabled_by_default: false
|
||||
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
|
||||
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: false
|
||||
internal: true
|
||||
entity_category: config
|
||||
disabled_by_default: false
|
||||
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
|
||||
@@ -1303,7 +1462,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
|
||||
@@ -1315,7 +1474,90 @@ 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: |-
|
||||
// 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
|
||||
@@ -1325,7 +1567,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
|
||||
@@ -1337,7 +1579,90 @@ 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: |-
|
||||
// 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
|
||||
@@ -1347,7 +1672,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
|
||||
@@ -1359,7 +1684,86 @@ 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: |-
|
||||
// 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
|
||||
@@ -1369,7 +1773,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
|
||||
@@ -1381,133 +1785,128 @@ 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
|
||||
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
|
||||
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:
|
||||
- if:
|
||||
condition:
|
||||
- lambda: return x; // Turned on?
|
||||
then:
|
||||
- binary_sensor.template.publish:
|
||||
id: bs_pending_restart
|
||||
state: true
|
||||
- lambda: |-
|
||||
id(relay_switches_packed) &= ~0x01; // Clear bit 0
|
||||
- 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
|
||||
output: output_relay_1
|
||||
platform: output
|
||||
restore_mode: ALWAYS_OFF
|
||||
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: ALWAYS_OFF
|
||||
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: ALWAYS_OFF
|
||||
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: ALWAYS_OFF
|
||||
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
|
||||
|
||||
...
|
||||
|
||||
@@ -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
|
||||
...
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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 <cinttypes>
|
||||
@@ -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;
|
||||
@@ -15,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));
|
||||
@@ -23,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)
|
||||
@@ -49,3 +54,5 @@ namespace esphome {
|
||||
|
||||
} // namespace tx_ultimate_easy
|
||||
} // namespace esphome
|
||||
|
||||
#endif // TX_ULTIMATE_EASY_CORE_HW_LEDS
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 <cinttypes>
|
||||
@@ -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");
|
||||
}
|
||||
@@ -69,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(
|
||||
@@ -225,3 +231,5 @@ namespace esphome {
|
||||
|
||||
} // namespace tx_ultimate_easy
|
||||
} // namespace esphome
|
||||
|
||||
#endif // TX_ULTIMATE_EASY_CORE_HW_TOUCH
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1 +1 @@
|
||||
2025.8.2
|
||||
2025.10.1
|
||||
|
||||
@@ -1 +1 @@
|
||||
version: 2025.8.2dev
|
||||
version: 2025.10.1
|
||||
|
||||
Reference in New Issue
Block a user