diff --git a/.github/workflows/release_tag.yml b/.github/workflows/release_tag.yml new file mode 100644 index 0000000..94df2cf --- /dev/null +++ b/.github/workflows/release_tag.yml @@ -0,0 +1,32 @@ +--- +name: Update Tags + +# yamllint disable-line rule:truthy +on: + release: + types: [published] + +jobs: + update-tags: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@main + with: + fetch-depth: '0' + + - name: Set up Git + run: | + git config --local user.email "action@github.com" + git config --local user.name "GitHub Action" + + - name: Move and push stable tag + run: | + git tag -f stable ${{ github.event.release.tag_name }} + git push -f origin stable + + - name: Move and push latest tag + run: | + git tag -f latest ${{ github.event.release.tag_name }} + git push -f origin latest +... diff --git a/.github/workflows/validate_clang_format.yml b/.github/workflows/validate_clang_format.yml new file mode 100644 index 0000000..4379668 --- /dev/null +++ b/.github/workflows/validate_clang_format.yml @@ -0,0 +1,26 @@ +--- +name: Validate C++ (Clang Format) + +# yamllint disable-line rule:truthy +on: + push: + paths: + - '**/*.h' + - '**/*.c' + - '**/*.cpp' + pull_request: + paths: + - '**/*.h' + - '**/*.c' + - '**/*.cpp' + workflow_dispatch: + +jobs: + clang-format-checking: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@main + - uses: RafikFarhad/clang-format-github-action@v3 + with: + sources: "components/tx_ultimate_easy/*.h,components/tx_ultimate_easy/*.cpp" +... diff --git a/.github/workflows/validate_esphome.yml b/.github/workflows/validate_esphome.yml new file mode 100644 index 0000000..f3b8fef --- /dev/null +++ b/.github/workflows/validate_esphome.yml @@ -0,0 +1,116 @@ +--- +name: Validate and Build ESPHome + +# yamllint disable-line rule:truthy +on: + push: + paths: + - "*.yaml" + - "prebuilt/*.yaml" + - ".test/*.yaml" + - "*.h" + - "*.c" + - "*.cpp" + - "*.py" + pull_request: + paths: + - "*.yaml" + - "prebuilt/*.yaml" + - ".test/*.yaml" + - "*.h" + - "*.c" + - "*.cpp" + - "*.py" + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + setup_dependencies: + name: Setup & Cache Dependencies + runs-on: ubuntu-latest + outputs: + cache-hit-arduino: ${{ steps.cache-arduino.outputs.cache-hit }} + + steps: + - name: Checkout repository + uses: actions/checkout@main + + - name: Cache Arduino Dependencies + id: cache-arduino + uses: actions/cache@main + with: + path: | + ~/.esphome/cache + ~/.platformio/packages + ~/.platformio/platforms + key: ${{ runner.os }}-arduino-${{ hashFiles('**/esphome_ard_basic.yaml') }} + restore-keys: | + ${{ runner.os }}-arduino- + + code_scan: + name: Code scan (YAML) + runs-on: "ubuntu-latest" + needs: setup_dependencies + steps: + - name: Checkout Code + uses: actions/checkout@main + + - name: Setup Python + uses: actions/setup-python@main + with: + python-version: '3.8' + + - name: Install Yamllint + run: pip install yamllint + + - name: Validate YAML files + run: find . -name "*.yaml" -exec yamllint -c ./.rules/yamllint.yml {} + + + build_basic: + name: Basic + needs: [code_scan, setup_dependencies] + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - id: ard + yaml_file: ".test/esphome_ard_basic.yaml" + cache-hit: ${{ needs.setup_dependencies.outputs.cache-hit-arduino }} + + steps: + - name: Checkout repository + uses: actions/checkout@main + + - name: Firmware + if: steps.matrix.outputs.cache-hit != 'true' + uses: esphome/build-action@v2.0.0 + with: + yaml_file: ${{ matrix.yaml_file }} + + build_bluetooth_proxy: + name: Bluetooth Proxy + needs: build_basic + runs-on: ubuntu-latest + strategy: + matrix: + include: + - id: idf_v4 + base: idf_v4 + yaml_file: ".test/esphome_idf_bluetooth_proxy.yaml" + - id: idf_v5 + base: idf_v5 + yaml_file: ".test/esphome_idf5_bluetooth_proxy.yaml" + + steps: + - name: Checkout repository + uses: actions/checkout@main + + - name: Build Bluetooth Proxy Firmware + uses: esphome/build-action@v2.0.0 + with: + yaml_file: ${{ matrix.yaml_file }} +... diff --git a/.github/workflows/validate_esphome_beta.yml b/.github/workflows/validate_esphome_beta.yml new file mode 100644 index 0000000..ff9b88a --- /dev/null +++ b/.github/workflows/validate_esphome_beta.yml @@ -0,0 +1,100 @@ +--- +name: Validate ESPHome (Beta) + +# yamllint disable-line rule:truthy +on: + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + setup_dependencies: + name: Setup & Cache Dependencies + runs-on: ubuntu-latest + outputs: + cache-hit-arduino: ${{ steps.cache-arduino.outputs.cache-hit }} + + steps: + - name: Checkout repository + uses: actions/checkout@main + + - name: Cache Arduino Dependencies + id: cache-arduino + uses: actions/cache@main + with: + path: | + ~/.esphome/cache + ~/.platformio/packages + ~/.platformio/platforms + key: ${{ runner.os }}-arduino-${{ hashFiles('**/esphome_ard_basic.yaml') }} + restore-keys: | + ${{ runner.os }}-arduino- + + code_scan: + name: Code scan (YAML) + runs-on: "ubuntu-latest" + needs: setup_dependencies + steps: + - name: Checkout Code + uses: actions/checkout@main + + - name: Setup Python + uses: actions/setup-python@main + with: + python-version: '3.8' + + - name: Install Yamllint + run: pip install yamllint + + - name: Validate YAML files + run: find . -name "*.yaml" -exec yamllint -c ./.rules/yamllint.yml {} + + + build_basic: + name: Basic + needs: [code_scan, setup_dependencies] + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - id: ard + yaml_file: ".test/esphome_ard_basic.yaml" + cache-hit: ${{ needs.setup_dependencies.outputs.cache-hit-arduino }} + + steps: + - name: Checkout repository + uses: actions/checkout@main + + - name: Firmware + if: steps.matrix.outputs.cache-hit != 'true' + uses: barndawgie/build-action@v1.9.0 + with: + yaml_file: ${{ matrix.yaml_file }} + version: beta + + build_bluetooth_proxy: + name: Bluetooth Proxy + needs: build_basic + runs-on: ubuntu-latest + strategy: + matrix: + include: + - id: idf_v4 + base: idf_v4 + yaml_file: ".test/esphome_idf_bluetooth_proxy.yaml" + - id: idf_v5 + base: idf_v5 + yaml_file: ".test/esphome_idf5_bluetooth_proxy.yaml" + + steps: + - name: Checkout repository + uses: actions/checkout@main + + - name: Build Bluetooth Proxy Firmware + uses: barndawgie/build-action@v1.9.0 + with: + yaml_file: ${{ matrix.yaml_file }} + version: beta +... diff --git a/.github/workflows/validate_markdown.yml b/.github/workflows/validate_markdown.yml new file mode 100644 index 0000000..1991d59 --- /dev/null +++ b/.github/workflows/validate_markdown.yml @@ -0,0 +1,58 @@ +--- +name: Validate Markdown + +# yamllint disable-line rule:truthy +on: + pull_request: + paths: + - '**/*.md' + push: + paths: + - '**/*.md' + workflow_dispatch: + +jobs: + markdown-lint: + name: Markdown Lint + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@main + with: + fetch-depth: '0' + + # https://github.com/marketplace/actions/markdownlint-cli2-action + - name: Identify changed files + uses: tj-actions/changed-files@v41 + id: changed-files + with: + files: '**/*.md' + separator: "," + # https://github.com/marketplace/actions/markdownlint-cli2-action + - name: Markdown Lint + uses: DavidAnson/markdownlint-cli2-action@v14 + if: steps.changed-files.outputs.any_changed == 'true' + with: + globs: ${{ steps.changed-files.outputs.all_changed_files }} + separator: "," + config: '.rules/.markdownlint.jsonc' + fix: true + + markdown-links: + name: Check links + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@main + with: + fetch-depth: '0' + + # https://github.com/gaurav-nelson/github-action-markdown-link-check + - name: Markdown links + uses: gaurav-nelson/github-action-markdown-link-check@v1 + with: + # yamllint disable-line rule:truthy + check-modified-files-only: yes + config-file: '.rules/mlc_config.json' + base-branch: 'main' +... diff --git a/.github/workflows/validate_python.yml b/.github/workflows/validate_python.yml new file mode 100644 index 0000000..4804b57 --- /dev/null +++ b/.github/workflows/validate_python.yml @@ -0,0 +1,30 @@ +--- +name: Validate Python (flake8) + +# yamllint disable-line rule:truthy +on: + push: + paths: + - '*.py' + pull_request: + paths: + - '*.py' + workflow_dispatch: + +jobs: + flake8-lint: + runs-on: ubuntu-latest + name: Lint + steps: + - name: Check out source repository + uses: actions/checkout@main + - name: Set up Python environment + uses: actions/setup-python@main + with: + python-version: "3.11" + - name: flake8 Lint + uses: py-actions/flake8@v2 + with: + max-line-length: "200" + path: "components/tx_ultimate_easy" +... diff --git a/.github/workflows/validate_yamllint.yml b/.github/workflows/validate_yamllint.yml new file mode 100644 index 0000000..87a2c71 --- /dev/null +++ b/.github/workflows/validate_yamllint.yml @@ -0,0 +1,47 @@ +--- +name: Validate YAML (secondary files) + +# yamllint disable-line rule:truthy +on: + push: + paths: + - '**/*.yml' + - '**/*.yaml' + pull_request: + paths: + - '**/*.yml' + - '**/*.yaml' + workflow_dispatch: + +jobs: + code_scan: + name: Validate YAML + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@main + with: + fetch-depth: '0' + + - name: Identify changed files + uses: tj-actions/changed-files@v41 + id: changed-files + with: + files: '**/*.y*ml' + separator: "," + + - name: Validate YAML + if: steps.changed-files.outputs.any_changed == 'true' + run: | + IFS=',' read -ra FILES <<< "${{ steps.changed-files.outputs.all_changed_files }}" + for file in "${FILES[@]}"; do + if [[ "$file" =~ ^*\.yaml$ ]] || \ + [[ "$file" =~ ^ESPHome/*\.yaml$ ]]; then + echo "Skipping $file" + continue + fi + echo "::group::Validating $file" + yamllint -c "./.rules/yamllint.yml" "$file" + echo "::endgroup::" + done +... diff --git a/.rules/.markdownlint.jsonc b/.rules/.markdownlint.jsonc new file mode 100644 index 0000000..e2a6e5b --- /dev/null +++ b/.rules/.markdownlint.jsonc @@ -0,0 +1,4 @@ +{ + "MD013": { "line_length": 200 } +} + \ No newline at end of file diff --git a/.rules/markdownlint.yml b/.rules/markdownlint.yml new file mode 100644 index 0000000..77abf70 --- /dev/null +++ b/.rules/markdownlint.yml @@ -0,0 +1,2 @@ +MD013: + line_length: 200 diff --git a/.rules/mlc_config.json b/.rules/mlc_config.json new file mode 100644 index 0000000..d66eb2f --- /dev/null +++ b/.rules/mlc_config.json @@ -0,0 +1,13 @@ +{ + "ignorePatterns": [ + { + "pattern": "^http://homeassistant\\.local.*$" + }, + { + "pattern": "^https://sonoff.tech/product/central-control-panel/nspanel/$" + }, + { + "pattern": "^https://sonoff.tech/wp-content/uploads/2021/11/%E4%BA%A7%E5%93%81%E5%8F%82%E6%95%B0%E8%A1%A8-NSPanel-20210831.pdf$" + } + ] +} diff --git a/.rules/yamllint.yml b/.rules/yamllint.yml new file mode 100644 index 0000000..0c1d80e --- /dev/null +++ b/.rules/yamllint.yml @@ -0,0 +1,42 @@ +# https://yamllint.readthedocs.io/en/stable/configuration.html#default-configuration +--- +yaml-files: + - '*.yaml' + - '*.yml' + - '.yamllint' + +rules: + anchors: + forbid-undeclared-aliases: true + forbid-duplicated-anchors: true + braces: enable + brackets: enable + colons: enable + commas: enable + comments: + level: warning + comments-indentation: + level: warning + document-end: + level: warning + document-start: + level: warning + empty-lines: enable + empty-values: disable + float-values: + level: warning + hyphens: enable + indentation: enable + key-duplicates: enable + key-ordering: disable + line-length: + max: 200 + new-line-at-end-of-file: enable + new-lines: enable + octal-values: + level: warning + quoted-strings: disable + trailing-spaces: enable + truthy: + level: warning +... diff --git a/.test/esphome_ard_basic.yaml b/.test/esphome_ard_basic.yaml new file mode 100644 index 0000000..dbda8d8 --- /dev/null +++ b/.test/esphome_ard_basic.yaml @@ -0,0 +1,8 @@ +--- +packages: + basic_package: !include ../TX-Ultimate-Easy-ESPHome.yaml # Basic package + +esp32: + framework: + type: arduino +... diff --git a/.test/esphome_idf5_bluetooth_proxy.yaml b/.test/esphome_idf5_bluetooth_proxy.yaml new file mode 100644 index 0000000..1ca452f --- /dev/null +++ b/.test/esphome_idf5_bluetooth_proxy.yaml @@ -0,0 +1,11 @@ +--- +packages: + basic_package: !include ../TX-Ultimate-Easy-ESPHome.yaml # Core package + addon_bluetooth_proxy: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_addon_ble_proxy.yaml + +esp32: + framework: + type: esp-idf + version: 5.0.2 + platform_version: 6.3.2 +... diff --git a/.test/esphome_idf_bluetooth_proxy.yaml b/.test/esphome_idf_bluetooth_proxy.yaml new file mode 100644 index 0000000..a2f9a0b --- /dev/null +++ b/.test/esphome_idf_bluetooth_proxy.yaml @@ -0,0 +1,5 @@ +--- +packages: + basic_package: !include ../TX-Ultimate-Easy-ESPHome.yaml # Core package + addon_bluetooth_proxy: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_addon_ble_proxy.yaml +... diff --git a/.test/secrets.yaml b/.test/secrets.yaml new file mode 100644 index 0000000..7247276 --- /dev/null +++ b/.test/secrets.yaml @@ -0,0 +1,10 @@ +--- +api_encryption_key: MSANehPIH8kCp/vGcpPcKPSkYnv3zchyxkXCleXJinM= +mysntpserver: 192.168.0.1 +web_server_password: NotASecret01 +web_server_username: NotASecret01 +wifi_ssid: NotASecret01 +wifi_password: NotASecret01 +wifi_password_backup: NotASecret02 +wifi_ssid_backup: NotASecret02 +... diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_addon_ble_proxy.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_addon_ble_proxy.yaml new file mode 100644 index 0000000..ad0ac37 --- /dev/null +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_addon_ble_proxy.yaml @@ -0,0 +1,37 @@ +--- +bluetooth_proxy: + +esp32: + framework: + type: esp-idf + sdkconfig_options: + CONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST: "y" + CONFIG_BT_BLE_DYNAMIC_ENV_MEMORY: "y" + CONFIG_ESP32_REV_MIN_3: "y" + CONFIG_MBEDTLS_DYNAMIC_BUFFER: "y" + CONFIG_MBEDTLS_DYNAMIC_FREE_CA_CERT: "y" + CONFIG_MBEDTLS_DYNAMIC_FREE_CONFIG_DATA: "y" + CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC: "y" + CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY: "y" + CONFIG_SPIRAM_RODATA: "y" + CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP: "y" + +esphome: + platformio_options: + build_flags: + - -D TX_ULTIMATE_EASY_ADDON_BLE_PROXY + +i2s_audio: !remove + +light: + - id: !extend light_full + platform: esp32_rmt_led_strip + rgb_order: GRB + rmt_channel: 0 + chipset: ws2812 + type: !remove + variant: !remove + max_refresh_rate: 33ms + +media_player: !remove +... diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml index e0194d2..46908c0 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml @@ -1,3 +1,4 @@ +--- substitutions: name: tx-ultimate-easy friendly_name: TX Ultimate Easy @@ -6,8 +7,9 @@ substitutions: longitude: '0' zone_home: 'zone.home' default_transition_length: 500ms + invalid_cooldown: 100ms - version: '0.0.1' + version: '0.0.2' api: id: api_server @@ -36,60 +38,126 @@ binary_sensor: icon: mdi:gesture-tap-box internal: false platform: template - on_press: + on_click: then: - - switch.toggle: sw_relay_1 + - lambda: |- + if (sl_button_1_action->active_index().has_value() and sl_button_1_action->active_index().value() == 1) + sw_relay_1->toggle(); + send_event_to_ha->execute("bs_button_1", "click"); + on_double_click: + then: + - lambda: send_event_to_ha->execute("bs_button_1", "double_click"); + on_multi_click: + - timing: &long_click-timing + - ON for at least 0.8s + invalid_cooldown: ${invalid_cooldown} + then: + - lambda: send_event_to_ha->execute("bs_button_1", "long_click"); - id: bs_button_2 name: Button 2 icon: mdi:gesture-tap-box internal: true platform: template - on_press: + on_click: then: - - switch.toggle: sw_relay_2 + - lambda: |- + if (sl_button_2_action->active_index().has_value() and sl_button_2_action->active_index().value() == 1) + sw_relay_2->toggle(); + send_event_to_ha->execute("bs_button_2", "click"); + on_double_click: + then: + - lambda: send_event_to_ha->execute("bs_button_2", "double_click"); + on_multi_click: + - timing: *long_click-timing + invalid_cooldown: ${invalid_cooldown} + then: + - lambda: send_event_to_ha->execute("bs_button_2", "long_click"); - id: bs_button_3 name: Button 3 icon: mdi:gesture-tap-box internal: true platform: template - on_press: + on_click: then: - - switch.toggle: sw_relay_3 + - lambda: |- + if (sl_button_3_action->active_index().has_value() and sl_button_3_action->active_index().value() == 1) + sw_relay_3->toggle(); + send_event_to_ha->execute("bs_button_3", "click"); + on_double_click: + then: + - lambda: send_event_to_ha->execute("bs_button_3", "double_click"); + on_multi_click: + - timing: *long_click-timing + invalid_cooldown: ${invalid_cooldown} + then: + - lambda: send_event_to_ha->execute("bs_button_3", "long_click"); - id: bs_button_4 name: Button 4 icon: mdi:gesture-tap-box internal: true platform: template - on_press: + on_click: then: - - switch.toggle: sw_relay_4 + - lambda: |- + if (sl_button_4_action->active_index().has_value() and sl_button_4_action->active_index().value() == 1) + sw_relay_4->toggle(); + send_event_to_ha->execute("bs_button_4", "click"); + on_double_click: + then: + - lambda: send_event_to_ha->execute("bs_button_4", "double_click"); + on_multi_click: + - timing: *long_click-timing + invalid_cooldown: ${invalid_cooldown} + then: + - lambda: send_event_to_ha->execute("bs_button_4", "long_click"); + + - id: bs_multi_touch + name: Multi-touch + icon: mdi:gesture-two-tap + internal: false + platform: template + on_click: + then: + - lambda: send_event_to_ha->execute("multi_touch", "multi_touch"); - id: bs_swipe_left name: Swipe left icon: mdi:gesture-swipe-left internal: true platform: template + on_click: + then: + - lambda: send_event_to_ha->execute("swipe", "left"); - id: bs_swipe_right name: Swipe right icon: mdi:gesture-swipe-right internal: true platform: template + on_click: + then: + - lambda: send_event_to_ha->execute("swipe", "right"); - id: bs_swipe_up name: Swipe up icon: mdi:gesture-swipe-up internal: true platform: template + on_click: + then: + - lambda: send_event_to_ha->execute("swipe", "up"); - id: bs_swipe_down name: Swipe down icon: mdi:gesture-swipe-down internal: true platform: template + on_click: + then: + - lambda: send_event_to_ha->execute("swipe", "down"); button: - id: bt_restart @@ -107,171 +175,147 @@ button: - script.execute: vibrate captive_portal: - -debug: + id: ap_captive_portal esp32: board: esp32dev flash_size: 8MB - framework: - # type: esp-idf - type: arduino -# sdkconfig_options: -# CONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST: "y" -# CONFIG_BT_BLE_DYNAMIC_ENV_MEMORY: "y" -# CONFIG_ESP32_REV_MIN_3: "y" -# CONFIG_MBEDTLS_DYNAMIC_BUFFER: "y" -# CONFIG_MBEDTLS_DYNAMIC_FREE_CA_CERT: "y" -# CONFIG_MBEDTLS_DYNAMIC_FREE_CONFIG_DATA: "y" -# CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC: "y" -# CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY: "y" -# CONFIG_SPIRAM_RODATA: "y" -# CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP: "y" esphome: name: ${name} friendly_name: ${friendly_name} platformio_options: build_flags: - - -DCONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST - - -DCONFIG_BT_BLE_DYNAMIC_ENV_MEMORY - - -DCONFIG_ESP32_REV_MIN_3 - - -DCONFIG_MBEDTLS_DYNAMIC_BUFFER - - -DCONFIG_MBEDTLS_DYNAMIC_FREE_CA_CERT - - -DCONFIG_MBEDTLS_DYNAMIC_FREE_CONFIG_DATA - - -DCONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC - - -DCONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY - - -DCONFIG_SPIRAM_RODATA - - -DCONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP - - -DCONFIG_SPIRAM_USE - - -DCONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY - - -DCONFIG_SPIRAM_CACHE_LIBCHAR_IN_IRAM - - -DCONFIG_SPIRAM_CACHE_LIBENV_IN_IRAM - - -DCONFIG_SPIRAM_CACHE_LIBFILE_IN_IRAM - - -DCONFIG_SPIRAM_CACHE_LIBIO_IN_IRAM - - -DCONFIG_SPIRAM_CACHE_LIBJMP_IN_IRAM - - -DCONFIG_SPIRAM_CACHE_LIBMATH_IN_IRAM - - -DCONFIG_SPIRAM_CACHE_LIBMEM_IN_IRAM - - -DCONFIG_SPIRAM_CACHE_LIBMISC_IN_IRAM - - -DCONFIG_SPIRAM_CACHE_LIBNUMPARSER_IN_IRAM - - -DCONFIG_SPIRAM_CACHE_LIBRAND_IN_IRAM - - -DCONFIG_SPIRAM_CACHE_LIBSTR_IN_IRAM - - -DCONFIG_SPIRAM_CACHE_LIBTIME_IN_IRAM - + - -D TX_ULTIMATE_EASY_CORE on_boot: - - priority: 701 - then: - - wait_until: - condition: - - lambda: return sl_tx_model_format->active_index().has_value(); - - lambda: |- - auto index = sl_tx_model_format->active_index(); - if (index.has_value()) { - uint8_t idx = index.value(); - bs_swipe_down->set_internal(idx == 0); - bs_swipe_left->set_internal(idx == 1); - bs_swipe_right->set_internal(idx == 1); - bs_swipe_up->set_internal(idx == 0); - light_28->set_internal(idx == 0); - light_29->set_internal(idx == 0); - light_30->set_internal(idx == 0); - light_31->set_internal(idx == 0); - light_eu_bottom->set_internal(idx == 1); - light_eu_left->set_internal(idx == 1); - light_eu_right->set_internal(idx == 1); - light_eu_top->set_internal(idx == 1); - light_us_bottom->set_internal(idx == 0); - light_us_left->set_internal(idx == 0); - light_us_right->set_internal(idx == 0); - light_us_top->set_internal(idx == 0); - sl_relay_light_mode_eu->set_internal(idx == 1); - sl_relay_light_mode_us->set_internal(idx == 0); - } - priority: 700 - then: - - wait_until: - condition: - - lambda: return sl_tx_model_gang->active_index().has_value(); - - lambda: |- - auto index = sl_tx_model_gang->active_index(); - if (index.has_value()) { - uint8_t idx = index.value(); - sw_relay_1->set_internal(false); - sw_relay_2->set_internal(idx < 1); - sw_relay_3->set_internal(idx < 2); - sw_relay_4->set_internal(idx < 3); - bs_button_1->set_internal(false); - bs_button_2->set_internal(idx < 1); - bs_button_3->set_internal(idx < 2); - bs_button_4->set_internal(idx < 3); - bs_button_1->publish_state(false); - bs_button_2->publish_state(false); - bs_button_3->publish_state(false); - bs_button_4->publish_state(false); - bs_swipe_left->publish_state(false); - bs_swipe_right->publish_state(false); - } - - priority: 699 then: - wait_until: condition: - lambda: return sl_tx_model_format->active_index().has_value(); - lambda: return sl_tx_model_gang->active_index().has_value(); - lambda: |- - auto model_format = sl_tx_model_format->active_index(); - auto model_gang = sl_tx_model_gang->active_index(); - if (model_format.has_value() and model_gang.has_value()) { - bool us_model = (model_gang.value() == 1); - switch (model_gang.value() + 1) { - case 1: // 1 Gang - if (us_model) { - id(gb_lights_1) = { light_us_rl_1_1_left }; - id(gb_lights_2) = { light_us_rl_1_1_right }; - } else { - id(gb_lights_1) = { light_eu_rl_1_1_bottom }; - id(gb_lights_2) = { light_eu_rl_1_1_top }; - } - break; - case 2: // 2 Gang - if (us_model) { - id(gb_lights_1) = { light_us_rl_1_2_left, light_us_rl_2_2_left }; - id(gb_lights_2) = { light_us_rl_1_2_right, light_us_rl_2_2_right }; - } else { - id(gb_lights_1) = { light_eu_rl_1_2_bottom, light_eu_rl_2_2_bottom }; - id(gb_lights_2) = { light_eu_rl_1_2_top, light_eu_rl_2_2_top }; - } - break; - case 3: // 3 Gang - if (us_model) { - id(gb_lights_1) = { light_us_rl_1_3_left, light_us_rl_2_3_left, light_us_rl_3_3_left }; - id(gb_lights_2) = { light_us_rl_1_3_right, light_us_rl_2_3_right, light_us_rl_3_3_right }; - } else { - id(gb_lights_1) = { light_eu_rl_1_3_bottom, light_eu_rl_2_3_bottom, light_eu_rl_3_3_bottom }; - id(gb_lights_2) = { light_eu_rl_1_3_top, light_eu_rl_2_3_top, light_eu_rl_3_3_top }; - } - break; - case 4: // 3 Gang - if (us_model) { - id(gb_lights_1) = { light_us_rl_1_4_left, light_us_rl_2_4_left, light_us_rl_3_4_left, light_us_rl_4_4_left }; - id(gb_lights_2) = { light_us_rl_1_4_right, light_us_rl_2_4_right, light_us_rl_3_4_right, light_us_rl_4_4_right }; - } else { - id(gb_lights_1) = { light_eu_rl_1_4_bottom, light_eu_rl_2_4_bottom, light_eu_rl_3_4_bottom, light_eu_rl_4_4_bottom }; - id(gb_lights_2) = { light_eu_rl_1_4_top, light_eu_rl_2_4_top, light_eu_rl_3_4_top, light_eu_rl_4_4_top }; - } - break; - } + const bool us_model = sl_tx_model_format->active_index().has_value() and + sl_tx_model_format->active_index().value() == 1; + const uint8_t num_gangs = (sl_tx_model_gang->active_index().has_value()) ? + (sl_tx_model_gang->active_index().value() + 1) : 0; + bs_button_1->publish_state(false); + bs_button_1->set_internal(num_gangs < 1); + sl_button_1_action->set_internal(num_gangs < 1); + bs_button_2->publish_state(false); + bs_button_2->set_internal(num_gangs < 2); + sl_button_2_action->set_internal(num_gangs < 2); + bs_button_3->publish_state(false); + bs_button_3->set_internal(num_gangs < 3); + sl_button_3_action->set_internal(num_gangs < 3); + bs_button_4->publish_state(false); + bs_button_4->set_internal(num_gangs < 4); + sl_button_4_action->set_internal(num_gangs < 4); + bs_multi_touch->publish_state(false); + bs_swipe_down->publish_state(false); + bs_swipe_down->set_internal(!us_model); + bs_swipe_left->publish_state(false); + bs_swipe_left->set_internal(us_model); + bs_swipe_right->publish_state(false); + bs_swipe_right->set_internal(us_model); + bs_swipe_up->publish_state(false); + bs_swipe_up->set_internal(!us_model); + light_28->set_internal(!us_model); + light_29->set_internal(!us_model); + light_30->set_internal(!us_model); + light_31->set_internal(!us_model); + light_eu->set_internal(us_model); + light_eu_bottom->set_internal(us_model); + light_eu_left->set_internal(us_model); + light_eu_right->set_internal(us_model); + light_eu_top->set_internal(us_model); + light_us->set_internal(!us_model); + light_us_bottom->set_internal(!us_model); + light_us_left->set_internal(!us_model); + light_us_right->set_internal(!us_model); + light_us_top->set_internal(!us_model); + sl_relay_1_mode->set_internal(num_gangs < 1); + sl_relay_1_light_mode_eu->set_internal(us_model or num_gangs < 1); + sl_relay_1_light_mode_us->set_internal(!us_model or num_gangs < 1); + sl_relay_2_mode->set_internal(num_gangs < 2); + sl_relay_2_light_mode_eu->set_internal(us_model or num_gangs < 2); + sl_relay_2_light_mode_us->set_internal(!us_model or num_gangs < 2); + sl_relay_3_mode->set_internal(num_gangs < 3); + sl_relay_3_light_mode_eu->set_internal(us_model or num_gangs < 3); + sl_relay_3_light_mode_us->set_internal(!us_model or num_gangs < 3); + sl_relay_4_mode->set_internal(num_gangs < 4); + sl_relay_4_light_mode_eu->set_internal(us_model or num_gangs < 4); + sl_relay_4_light_mode_us->set_internal(!us_model or num_gangs < 4); + auto relay_mode_index = sl_relay_1_mode->active_index(); + if (relay_mode_index.has_value()) { + ligth_output_1->set_internal(false or relay_mode_index.value() != 1); + sw_relay_1->set_internal(false or relay_mode_index.value() != 0); } + relay_mode_index = sl_relay_2_mode->active_index(); + if (relay_mode_index.has_value()) { + ligth_output_2->set_internal(num_gangs < 2 or relay_mode_index.value() != 1); + sw_relay_2->set_internal(num_gangs < 2 or relay_mode_index.value() != 0); + } + relay_mode_index = sl_relay_3_mode->active_index(); + if (relay_mode_index.has_value()) { + ligth_output_3->set_internal(num_gangs < 3 or relay_mode_index.value() != 1); + sw_relay_3->set_internal(num_gangs < 3 or relay_mode_index.value() != 0); + } + relay_mode_index = sl_relay_4_mode->active_index(); + if (relay_mode_index.has_value()) { + ligth_output_4->set_internal(num_gangs < 4 or relay_mode_index.value() != 1); + sw_relay_4->set_internal(num_gangs < 4 or relay_mode_index.value() != 0); + } + switch (num_gangs) { + case 1: // 1 Gang + if (us_model) { + id(gb_lights_1) = { light_us_rl_1_1_left }; + id(gb_lights_2) = { light_us_rl_1_1_right }; + } else { + id(gb_lights_1) = { light_eu_rl_1_1_bottom }; + id(gb_lights_2) = { light_eu_rl_1_1_top }; + } + break; + case 2: // 2 Gang + if (us_model) { + id(gb_lights_1) = { light_us_rl_1_2_left, light_us_rl_2_2_left }; + id(gb_lights_2) = { light_us_rl_1_2_right, light_us_rl_2_2_right }; + } else { + id(gb_lights_1) = { light_eu_rl_1_2_bottom, light_eu_rl_2_2_bottom }; + id(gb_lights_2) = { light_eu_rl_1_2_top, light_eu_rl_2_2_top }; + } + break; + case 3: // 3 Gang + if (us_model) { + id(gb_lights_1) = { light_us_rl_1_3_left, light_us_rl_2_3_left, light_us_rl_3_3_left }; + id(gb_lights_2) = { light_us_rl_1_3_right, light_us_rl_2_3_right, light_us_rl_3_3_right }; + } else { + id(gb_lights_1) = { light_eu_rl_1_3_bottom, light_eu_rl_2_3_bottom, light_eu_rl_3_3_bottom }; + id(gb_lights_2) = { light_eu_rl_1_3_top, light_eu_rl_2_3_top, light_eu_rl_3_3_top }; + } + break; + case 4: // 3 Gang + if (us_model) { + id(gb_lights_1) = { light_us_rl_1_4_left, light_us_rl_2_4_left, light_us_rl_3_4_left, light_us_rl_4_4_left }; + id(gb_lights_2) = { light_us_rl_1_4_right, light_us_rl_2_4_right, light_us_rl_3_4_right, light_us_rl_4_4_right }; + } else { + id(gb_lights_1) = { light_eu_rl_1_4_bottom, light_eu_rl_2_4_bottom, light_eu_rl_3_4_bottom, light_eu_rl_4_4_bottom }; + id(gb_lights_2) = { light_eu_rl_1_4_top, light_eu_rl_2_4_top, light_eu_rl_3_4_top, light_eu_rl_4_4_top }; + } + break; + } + - priority: 600 # This is where most sensors are set up. then: - lambda: |- tx_fw_version->publish_state("${version}"); tx_device_name->publish_state("${name}"); + - priority: -100 # At this priority, pretty much everything should already be initialized. + then: + - lambda: send_event_to_ha->execute("boot", "done"); external_components: - - source: - type: git - url: https://github.com/edwardtfn/TX-Ultimate-Easy - ref: dev + - source: github://edwardtfn/TX-Ultimate-Easy@dev # Change this before releasing components: - tx_ultimate_easy @@ -288,17 +332,37 @@ i2s_audio: i2s_bclk_pin: GPIO2 i2s_lrclk_pin: GPIO4 +improv_serial: + id: serial_improv + light: + - id: ligth_output_1 + name: Light output 1 + internal: true + platform: binary + output: output_relay_1 + - id: ligth_output_2 + name: Light output 2 + internal: true + platform: binary + output: output_relay_2 + - id: ligth_output_3 + name: Light output 3 + internal: true + platform: binary + output: output_relay_3 + - id: ligth_output_4 + name: Light output 4 + internal: true + platform: binary + output: output_relay_4 + - id: light_full name: Light - All internal: true platform: neopixelbus type: GRB variant: WS2811 - # platform: esp32_rmt_led_strip - # rgb_order: GRB - # rmt_channel: 0 - # chipset: ws2812 num_leds: 32 pin: GPIO13 @@ -311,7 +375,7 @@ light: - id: light_full from: 0 to: 28 - effects: + effects: &light_effects_full - addressable_rainbow: - addressable_rainbow: name: "Rainbow - Fast" @@ -361,47 +425,7 @@ light: - id: light_full from: 0 to: 31 - effects: - - addressable_rainbow: - - addressable_rainbow: - name: "Rainbow - Fast" - speed: 50 - - pulse: - - pulse: - name: "Pulse - Fast" - transition_length: 0.5s - update_interval: 0.5s - - pulse: - name: "Pulse - Slow" - update_interval: 2s - - addressable_scan: - - addressable_scan: - name: Scan Effect With Custom Values - move_interval: 100ms - scan_width: 3 - - addressable_twinkle: - - addressable_twinkle: - name: Twinkle Effect With Custom Values - twinkle_probability: 5% - progress_interval: 4ms - - addressable_random_twinkle: - - addressable_random_twinkle: - name: Random Twinkle Effect With Custom Values - twinkle_probability: 20% - progress_interval: 32ms - - addressable_fireworks: - - addressable_fireworks: - name: Fireworks Effect With Custom Values - update_interval: 32ms - spark_probability: 10% - use_random_color: false - fade_out_rate: 120 - - addressable_flicker: - - addressable_flicker: - name: Flicker Effect With Custom Values - update_interval: 16ms - intensity: 5% - - random: + effects: *light_effects_full - id: light_eu_bottom name: Light - Bottom @@ -414,47 +438,7 @@ light: from: 6 to: 12 reversed: true - effects: - - addressable_rainbow: - - addressable_rainbow: - name: "Rainbow - Fast" - speed: 50 - - pulse: - - pulse: - name: "Pulse - Fast" - transition_length: 0.5s - update_interval: 0.5s - - pulse: - name: "Pulse - Slow" - update_interval: 2s - - addressable_scan: - - addressable_scan: - name: Scan Effect With Custom Values - move_interval: 100ms - scan_width: 3 - - addressable_twinkle: - - addressable_twinkle: - name: Twinkle Effect With Custom Values - twinkle_probability: 5% - progress_interval: 4ms - - addressable_random_twinkle: - - addressable_random_twinkle: - name: Random Twinkle Effect With Custom Values - twinkle_probability: 20% - progress_interval: 32ms - - addressable_fireworks: - - addressable_fireworks: - name: Fireworks Effect With Custom Values - update_interval: 32ms - spark_probability: 10% - use_random_color: false - fade_out_rate: 120 - - addressable_flicker: - - addressable_flicker: - name: Flicker Effect With Custom Values - update_interval: 16ms - intensity: 5% - - random: + effects: *light_effects_full - id: light_eu_left name: Light - Left internal: true @@ -466,47 +450,7 @@ light: from: 13 to: 19 reversed: true - effects: - - addressable_rainbow: - - addressable_rainbow: - name: "Rainbow - Fast" - speed: 50 - - pulse: - - pulse: - name: "Pulse - Fast" - transition_length: 0.5s - update_interval: 0.5s - - pulse: - name: "Pulse - Slow" - update_interval: 2s - - addressable_scan: - - addressable_scan: - name: Scan Effect With Custom Values - move_interval: 100ms - scan_width: 3 - - addressable_twinkle: - - addressable_twinkle: - name: Twinkle Effect With Custom Values - twinkle_probability: 5% - progress_interval: 4ms - - addressable_random_twinkle: - - addressable_random_twinkle: - name: Random Twinkle Effect With Custom Values - twinkle_probability: 20% - progress_interval: 32ms - - addressable_fireworks: - - addressable_fireworks: - name: Fireworks Effect With Custom Values - update_interval: 32ms - spark_probability: 10% - use_random_color: false - fade_out_rate: 120 - - addressable_flicker: - - addressable_flicker: - name: Flicker Effect With Custom Values - update_interval: 16ms - intensity: 5% - - random: + effects: *light_effects_full - id: light_eu_right name: Light - Right internal: true @@ -522,47 +466,7 @@ light: from: 0 to: 5 reversed: false - effects: - - addressable_rainbow: - - addressable_rainbow: - name: "Rainbow - Fast" - speed: 50 - - pulse: - - pulse: - name: "Pulse - Fast" - transition_length: 0.5s - update_interval: 0.5s - - pulse: - name: "Pulse - Slow" - update_interval: 2s - - addressable_scan: - - addressable_scan: - name: Scan Effect With Custom Values - move_interval: 100ms - scan_width: 3 - - addressable_twinkle: - - addressable_twinkle: - name: Twinkle Effect With Custom Values - twinkle_probability: 5% - progress_interval: 4ms - - addressable_random_twinkle: - - addressable_random_twinkle: - name: Random Twinkle Effect With Custom Values - twinkle_probability: 20% - progress_interval: 32ms - - addressable_fireworks: - - addressable_fireworks: - name: Fireworks Effect With Custom Values - update_interval: 32ms - spark_probability: 10% - use_random_color: false - fade_out_rate: 120 - - addressable_flicker: - - addressable_flicker: - name: Flicker Effect With Custom Values - update_interval: 16ms - intensity: 5% - - random: + effects: *light_effects_full - id: light_eu_top name: Light - Top internal: true @@ -574,47 +478,7 @@ light: from: 20 to: 26 reversed: false - effects: - - addressable_rainbow: - - addressable_rainbow: - name: "Rainbow - Fast" - speed: 50 - - pulse: - - pulse: - name: "Pulse - Fast" - transition_length: 0.5s - update_interval: 0.5s - - pulse: - name: "Pulse - Slow" - update_interval: 2s - - addressable_scan: - - addressable_scan: - name: Scan Effect With Custom Values - move_interval: 100ms - scan_width: 3 - - addressable_twinkle: - - addressable_twinkle: - name: Twinkle Effect With Custom Values - twinkle_probability: 5% - progress_interval: 4ms - - addressable_random_twinkle: - - addressable_random_twinkle: - name: Random Twinkle Effect With Custom Values - twinkle_probability: 20% - progress_interval: 32ms - - addressable_fireworks: - - addressable_fireworks: - name: Fireworks Effect With Custom Values - update_interval: 32ms - spark_probability: 10% - use_random_color: false - fade_out_rate: 120 - - addressable_flicker: - - addressable_flicker: - name: Flicker Effect With Custom Values - update_interval: 16ms - intensity: 5% - - random: + effects: *light_effects_full - id: light_us_bottom name: Light - Bottom internal: true @@ -626,47 +490,7 @@ light: from: 10 to: 15 reversed: true - effects: - - addressable_rainbow: - - addressable_rainbow: - name: "Rainbow - Fast" - speed: 50 - - pulse: - - pulse: - name: "Pulse - Fast" - transition_length: 0.5s - update_interval: 0.5s - - pulse: - name: "Pulse - Slow" - update_interval: 2s - - addressable_scan: - - addressable_scan: - name: Scan Effect With Custom Values - move_interval: 100ms - scan_width: 3 - - addressable_twinkle: - - addressable_twinkle: - name: Twinkle Effect With Custom Values - twinkle_probability: 5% - progress_interval: 4ms - - addressable_random_twinkle: - - addressable_random_twinkle: - name: Random Twinkle Effect With Custom Values - twinkle_probability: 20% - progress_interval: 32ms - - addressable_fireworks: - - addressable_fireworks: - name: Fireworks Effect With Custom Values - update_interval: 32ms - spark_probability: 10% - use_random_color: false - fade_out_rate: 120 - - addressable_flicker: - - addressable_flicker: - name: Flicker Effect With Custom Values - update_interval: 16ms - intensity: 5% - - random: + effects: *light_effects_full - id: light_us_left name: Light - Left internal: true @@ -678,47 +502,7 @@ light: from: 15 to: 24 reversed: true - effects: - - addressable_rainbow: - - addressable_rainbow: - name: "Rainbow - Fast" - speed: 50 - - pulse: - - pulse: - name: "Pulse - Fast" - transition_length: 0.5s - update_interval: 0.5s - - pulse: - name: "Pulse - Slow" - update_interval: 2s - - addressable_scan: - - addressable_scan: - name: Scan Effect With Custom Values - move_interval: 100ms - scan_width: 3 - - addressable_twinkle: - - addressable_twinkle: - name: Twinkle Effect With Custom Values - twinkle_probability: 5% - progress_interval: 4ms - - addressable_random_twinkle: - - addressable_random_twinkle: - name: Random Twinkle Effect With Custom Values - twinkle_probability: 20% - progress_interval: 32ms - - addressable_fireworks: - - addressable_fireworks: - name: Fireworks Effect With Custom Values - update_interval: 32ms - spark_probability: 10% - use_random_color: false - fade_out_rate: 120 - - addressable_flicker: - - addressable_flicker: - name: Flicker Effect With Custom Values - update_interval: 16ms - intensity: 5% - - random: + effects: *light_effects_full - id: light_us_right name: Light - Right internal: true @@ -734,47 +518,7 @@ light: from: 0 to: 8 reversed: false - effects: - - addressable_rainbow: - - addressable_rainbow: - name: "Rainbow - Fast" - speed: 50 - - pulse: - - pulse: - name: "Pulse - Fast" - transition_length: 0.5s - update_interval: 0.5s - - pulse: - name: "Pulse - Slow" - update_interval: 2s - - addressable_scan: - - addressable_scan: - name: Scan Effect With Custom Values - move_interval: 100ms - scan_width: 3 - - addressable_twinkle: - - addressable_twinkle: - name: Twinkle Effect With Custom Values - twinkle_probability: 5% - progress_interval: 4ms - - addressable_random_twinkle: - - addressable_random_twinkle: - name: Random Twinkle Effect With Custom Values - twinkle_probability: 20% - progress_interval: 32ms - - addressable_fireworks: - - addressable_fireworks: - name: Fireworks Effect With Custom Values - update_interval: 32ms - spark_probability: 10% - use_random_color: false - fade_out_rate: 120 - - addressable_flicker: - - addressable_flicker: - name: Flicker Effect With Custom Values - update_interval: 16ms - intensity: 5% - - random: + effects: *light_effects_full - id: light_us_top name: Light - Top internal: true @@ -786,47 +530,7 @@ light: from: 25 to: 30 reversed: false - effects: - - addressable_rainbow: - - addressable_rainbow: - name: "Rainbow - Fast" - speed: 50 - - pulse: - - pulse: - name: "Pulse - Fast" - transition_length: 0.5s - update_interval: 0.5s - - pulse: - name: "Pulse - Slow" - update_interval: 2s - - addressable_scan: - - addressable_scan: - name: Scan Effect With Custom Values - move_interval: 100ms - scan_width: 3 - - addressable_twinkle: - - addressable_twinkle: - name: Twinkle Effect With Custom Values - twinkle_probability: 5% - progress_interval: 4ms - - addressable_random_twinkle: - - addressable_random_twinkle: - name: Random Twinkle Effect With Custom Values - twinkle_probability: 20% - progress_interval: 32ms - - addressable_fireworks: - - addressable_fireworks: - name: Fireworks Effect With Custom Values - update_interval: 32ms - spark_probability: 10% - use_random_color: false - fade_out_rate: 120 - - addressable_flicker: - - addressable_flicker: - name: Flicker Effect With Custom Values - update_interval: 16ms - intensity: 5% - - random: + effects: *light_effects_full - id: light_00 name: Light - LED 00 @@ -1182,8 +886,8 @@ light: default_transition_length: ${default_transition_length} segments: - id: light_full - from: 11 - to: 11 + from: 12 + to: 12 - id: light_eu_rl_1_3_top name: Light - Relay 1 of 3 (top) internal: true @@ -1191,8 +895,8 @@ light: default_transition_length: ${default_transition_length} segments: - id: light_full - from: 21 - to: 21 + from: 20 + to: 20 - id: light_eu_rl_2_3_bottom name: Light - Relay 2 of 3 (bottom) internal: true @@ -1218,8 +922,8 @@ light: default_transition_length: ${default_transition_length} segments: - id: light_full - from: 7 - to: 7 + from: 6 + to: 6 - id: light_eu_rl_3_3_top name: Light - Relay 3 of 3 (top) internal: true @@ -1227,8 +931,8 @@ light: default_transition_length: ${default_transition_length} segments: - id: light_full - from: 25 - to: 25 + from: 26 + to: 26 - id: light_eu_rl_1_4_bottom name: Light - Relay 1 of 4 (bottom) internal: true @@ -1488,15 +1192,6 @@ light: from: 7 to: 7 -# - id: light_status -# name: Light - Status -# platform: esp32_rmt_led_strip -# rgb_order: GRB -# pin: GPIO13 -# num_leds: 1 -# rmt_channel: 0 -# chipset: ws2812 - logger: media_player: @@ -1508,10 +1203,6 @@ media_player: i2s_comm_fmt: lsb dac_type: external mode: mono - mute_pin: - number: GPIO26 - inverted: true - allow_other_uses: true internal: false number: @@ -1530,33 +1221,98 @@ number: optimistic: true restore_value: true + - id: nr_touch_duration + name: Touch duration + icon: mdi:timer-cog-outline + unit_of_measurement: ms + # mode: box + internal: false + entity_category: config + platform: template + min_value: 10 + max_value: 1500 + step: 1 + initial_value: 250 + optimistic: true + restore_value: true + ota: + platform: esphome + +output: + - id: output_relay_1 + platform: gpio + pin: GPIO18 + + - id: output_relay_2 + platform: gpio + pin: GPIO17 + + - id: output_relay_3 + platform: gpio + pin: GPIO27 + + - id: output_relay_4 + platform: gpio + pin: GPIO23 psram: script: + - id: release_buttons + mode: restart + then: + - lambda: |- + if (bs_button_1->state) bs_button_1->publish_state(false); + if (bs_button_2->state) bs_button_2->publish_state(false); + if (bs_button_3->state) bs_button_3->publish_state(false); + if (bs_button_4->state) bs_button_4->publish_state(false); + + - id: send_event_to_ha + mode: queued + parameters: + component: string + event: string + then: + - lambda: |- + esphome::api::CustomAPIDevice ha_event; + ha_event.fire_homeassistant_event("esphome.tx_ultimate_easy", + { + {"device_name", tx_device_name->state.c_str()}, + {"firmware", "${version}"}, + {"component", component}, + {"event", event}, + }); + - id: show_relay_status mode: restart then: - lambda: |- + return; + show_relay_status->stop(); auto model_format = sl_tx_model_format->active_index(); - auto light_index = ((model_format.has_value() and model_format.value() == 0) - ? sl_relay_light_mode_eu->active_index() - : sl_relay_light_mode_us->active_index()); - if (model_format.has_value() and light_index.has_value() and light_index.value() > 0) { - std::vector switches = {sw_relay_1, sw_relay_2, sw_relay_3, sw_relay_4}; - uint8_t light_idx = light_index.value(); - if (light_idx == 1 or light_idx == 3) { - for (int i = 0; i < id(gb_lights_1).size(); ++i) { - if (switches[i]->state) + std::vector relay_modes; + + if (model_format.has_value() and model_format.value() == 0) { + relay_modes = {sl_relay_1_light_mode_eu, sl_relay_2_light_mode_eu, sl_relay_3_light_mode_eu, sl_relay_4_light_mode_eu}; + } else { + relay_modes = {sl_relay_1_light_mode_us, sl_relay_2_light_mode_us, sl_relay_3_light_mode_us, sl_relay_4_light_mode_us}; + } + + std::vector switches = {sw_relay_1, sw_relay_2, sw_relay_3, sw_relay_4}; + for (int i = 0; i < relay_modes.size(); ++i) { + auto light_index = relay_modes[i]->active_index(); + if (light_index.has_value()) { + uint8_t light_idx = light_index.value(); + bool switch_state = switches[i]->state; + if (light_idx == 1 || light_idx == 3) { + if (switch_state) id(gb_lights_1)[i]->turn_on().perform(); else id(gb_lights_1)[i]->turn_off().perform(); } - } - if (light_idx == 2 or light_idx == 3) { - for (int i = 0; i < id(gb_lights_2).size(); ++i) { - if (switches[i]->state) + if (light_idx == 2 || light_idx == 3) { + if (switch_state) id(gb_lights_2)[i]->turn_on().perform(); else id(gb_lights_2)[i]->turn_off().perform(); @@ -1572,8 +1328,116 @@ script: - switch.turn_off: sw_vibration_motor select: - - id: sl_relay_light_mode_eu - name: Relay light mode + - id: sl_button_1_action + name: Button 1 action + platform: template + options: + - "None" + - "Relay 1 (toggle)" + optimistic: true + restore_value: true + internal: true + entity_category: config + disabled_by_default: false + icon: mdi:dip-switch + + - id: sl_button_2_action + name: Button 2 action + platform: template + options: + - "None" + - "Relay 2 (toggle)" + optimistic: true + restore_value: true + internal: true + entity_category: config + disabled_by_default: false + icon: mdi:dip-switch + + - id: sl_button_3_action + name: Button 3 action + platform: template + options: + - "None" + - "Relay 3 (toggle)" + optimistic: true + restore_value: true + internal: true + entity_category: config + disabled_by_default: false + icon: mdi:dip-switch + + - id: sl_button_4_action + name: Button 4 action + platform: template + options: + - "None" + - "Relay 4 (toggle)" + optimistic: true + restore_value: true + internal: true + entity_category: config + disabled_by_default: false + icon: mdi:dip-switch + + - id: sl_relay_1_mode + name: Relay 1 mode + platform: template + options: + - "Relay" + - "Light" + - "Not in use" + optimistic: true + restore_value: true + internal: true + entity_category: config + disabled_by_default: false + icon: mdi:dip-switch + + - id: sl_relay_2_mode + name: Relay 2 mode + platform: template + options: + - "Relay" + - "Light" + - "Not in use" + optimistic: true + restore_value: true + internal: true + entity_category: config + disabled_by_default: false + icon: mdi:dip-switch + + - id: sl_relay_3_mode + name: Relay 3 mode + platform: template + options: + - "Relay" + - "Light" + - "Not in use" + optimistic: true + restore_value: true + internal: true + entity_category: config + disabled_by_default: false + icon: mdi:dip-switch + + - id: sl_relay_4_mode + name: Relay 4 mode + platform: template + options: + - "Relay" + - "Light" + - "Not in use" + optimistic: true + restore_value: true + internal: true + entity_category: config + disabled_by_default: false + icon: mdi:dip-switch + + - id: sl_relay_1_light_mode_eu + name: Relay 1 light mode platform: template options: - "Disabled" @@ -1588,8 +1452,104 @@ select: disabled_by_default: false icon: mdi:dots-square - - id: sl_relay_light_mode_us - name: Relay light mode + - id: sl_relay_2_light_mode_eu + name: Relay 2 light mode + platform: template + options: + - "Disabled" + - "Bottom" + - "Top" + - "Both" + initial_option: "Bottom" + optimistic: true + restore_value: true + internal: true + entity_category: config + disabled_by_default: false + icon: mdi:dots-square + + - id: sl_relay_3_light_mode_eu + name: Relay 3 light mode + platform: template + options: + - "Disabled" + - "Bottom" + - "Top" + - "Both" + initial_option: "Bottom" + optimistic: true + restore_value: true + internal: true + entity_category: config + disabled_by_default: false + icon: mdi:dots-square + + - id: sl_relay_4_light_mode_eu + name: Relay 4 light mode + platform: template + options: + - "Disabled" + - "Bottom" + - "Top" + - "Both" + initial_option: "Bottom" + optimistic: true + restore_value: true + internal: true + entity_category: config + disabled_by_default: false + icon: mdi:dots-square + + - id: sl_relay_1_light_mode_us + name: Relay 1 light mode + platform: template + options: + - "Disabled" + - "Left" + - "Right" + - "Both" + initial_option: "Right" + 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 + platform: template + options: + - "Disabled" + - "Left" + - "Right" + - "Both" + initial_option: "Right" + optimistic: true + restore_value: true + internal: true + entity_category: config + disabled_by_default: false + icon: mdi:dots-square + + - id: sl_relay_3_light_mode_us + name: Relay 3 light mode + platform: template + options: + - "Disabled" + - "Left" + - "Right" + - "Both" + initial_option: "Right" + optimistic: true + restore_value: true + internal: true + entity_category: config + disabled_by_default: false + icon: mdi:dots-square + + - id: sl_relay_4_light_mode_us + name: Relay 4 light mode platform: template options: - "Disabled" @@ -1651,16 +1611,6 @@ select: icon: mdi:dip-switch sensor: - - platform: debug - free: - name: "Heap Free" - block: - name: "Heap Max Block" - loop_time: - name: "Loop Time" - psram: - name: "Free PSRAM" - - id: sun_elevation name: Sun Elevation platform: sun @@ -1689,23 +1639,12 @@ sun: longitude: ${longitude} switch: - - id: sw_speaker_amplifier - name: Speaker - Amplifier - platform: gpio - pin: - number: GPIO26 - inverted: true - allow_other_uses: true - restore_mode: RESTORE_DEFAULT_ON - internal: true - entity_category: config - - id: sw_relay_1 name: Relay 1 - platform: gpio - pin: GPIO18 + platform: output + output: output_relay_1 restore_mode: RESTORE_DEFAULT_OFF - internal: false + internal: true on_turn_on: then: - script.execute: show_relay_status @@ -1715,8 +1654,8 @@ switch: - id: sw_relay_2 name: Relay 2 - platform: gpio - pin: GPIO17 + platform: output + output: output_relay_2 restore_mode: RESTORE_DEFAULT_OFF internal: true on_turn_on: @@ -1728,8 +1667,8 @@ switch: - id: sw_relay_3 name: Relay 3 - platform: gpio - pin: GPIO27 + platform: output + output: output_relay_3 restore_mode: RESTORE_DEFAULT_OFF internal: true on_turn_on: @@ -1741,8 +1680,8 @@ switch: - id: sw_relay_4 name: Relay 4 - platform: gpio - pin: GPIO23 + platform: output + output: output_relay_4 restore_mode: RESTORE_DEFAULT_OFF internal: true on_turn_on: @@ -1819,6 +1758,15 @@ tx_ultimate_easy: id: tx_ultimate uart: uart_touch + on_multi_touch_release: + - lambda: |- + if (sl_touch_vibration_feedback->state == "On press" or sl_touch_vibration_feedback->state == "Always") + vibrate->execute(); + bs_multi_touch->publish_state(true); + release_buttons->execute(); + - delay: !lambda return nr_touch_duration->state; + - lambda: bs_multi_touch->publish_state(false); + on_press: - lambda: |- if (sl_touch_vibration_feedback->state == "On press" or sl_touch_vibration_feedback->state == "Always") @@ -1852,22 +1800,23 @@ tx_ultimate_easy: - lambda: |- if (sl_touch_vibration_feedback->state == "On release" or sl_touch_vibration_feedback->state == "Always") vibrate->execute(); - if (bs_button_1->state) bs_button_1->publish_state(false); - if (bs_button_2->state) bs_button_2->publish_state(false); - if (bs_button_3->state) bs_button_3->publish_state(false); - if (bs_button_4->state) bs_button_4->publish_state(false); + release_buttons->execute(); if (bs_swipe_left->state) bs_swipe_left->publish_state(false); if (bs_swipe_right->state) bs_swipe_right->publish_state(false); on_swipe_left: - - binary_sensor.template.publish: - id: bs_swipe_left - state: ON + - lambda: |- + bs_swipe_left->publish_state(true); + release_buttons->execute(); + - delay: !lambda return nr_touch_duration->state; + - lambda: bs_swipe_left->publish_state(false); on_swipe_right: - - binary_sensor.template.publish: - id: bs_swipe_right - state: ON + - lambda: |- + bs_swipe_right->publish_state(true); + release_buttons->execute(); + - delay: !lambda return nr_touch_duration->state; + - lambda: bs_swipe_right->publish_state(false); on_touch_event: - lambda: |- @@ -1885,3 +1834,4 @@ web_server: wifi: ap: +... diff --git a/TX-Ultimate-Easy-ESPHome.yaml b/TX-Ultimate-Easy-ESPHome.yaml index 958c3c4..f171b9a 100644 --- a/TX-Ultimate-Easy-ESPHome.yaml +++ b/TX-Ultimate-Easy-ESPHome.yaml @@ -1,3 +1,4 @@ +--- substitutions: name: tx-ultimate-easy friendly_name: TX Ultimate Easy @@ -10,10 +11,8 @@ packages: remote_package: url: https://github.com/edwardtfn/TX-Ultimate-Easy ref: dev - refresh: 1h + refresh: 30s files: - ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml - -### Uncomment the lines bellow if you have issues with OTA and cannot flash via serial at this moment. ### -#esp32: -# flash_size: 4MB + # - ESPHome/TX-Ultimate-Easy-ESPHome_addon_ble_proxy.yaml # Adds experimental BLE proxy support (limited) +... diff --git a/components/tx_ultimate_easy/tx_ultimate_easy.cpp b/components/tx_ultimate_easy/tx_ultimate_easy.cpp index 5443904..84150db 100644 --- a/components/tx_ultimate_easy/tx_ultimate_easy.cpp +++ b/components/tx_ultimate_easy/tx_ultimate_easy.cpp @@ -1,50 +1,61 @@ +// tx_ultimate_easy.cpp + #include "esphome/core/log.h" #include "tx_ultimate_easy.h" +#include namespace esphome { namespace tx_ultimate_easy { - static const char *TAG = "tx_ultimate_easy"; void TxUltimateEasy::setup() { - ESP_LOGI("log", "%s", "Tx Ultimate Easy is initialized"); + ESP_LOGI(TAG, "TX Ultimate Easy is initialized"); } void TxUltimateEasy::loop() { bool found = false; - int uart_received_bytes[15] = {}; + std::array uart_received_bytes{}; int byte = -1; int i = 0; + while (this->available()) { byte = this->read(); - if (byte == 170) { - handle_touch(uart_received_bytes); - i = 0; + if (byte == HEADER_BYTE_1) { + this->handle_touch(uart_received_bytes); + i = 0; } - uart_received_bytes[i] = byte; - i++; - if (byte != 0) { - found = true; + if (i < UART_RECEIVED_BYTES_SIZE) { + uart_received_bytes[i] = byte; + i++; } - }; - if (found) handle_touch(uart_received_bytes); + if (byte != 0x00) { + found = true; + } + } + if (found) this->handle_touch(uart_received_bytes); } - void TxUltimateEasy::handle_touch(int uart_received_bytes[]) { - ESP_LOGVV(TAG, "------------"); - ESP_LOGVV(TAG, "- UART-Log -"); - ESP_LOGVV(TAG, "------------"); - for (int i = 0; i < 15; i++) ESP_LOGVV(TAG, "UART - Log - Byte[%i]: %i", i, uart_received_bytes[i]); - if (is_valid_data(uart_received_bytes)) send_touch_(get_touch_point(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"); } + void TxUltimateEasy::dump_config() { + ESP_LOGCONFIG(TAG, "TX Ultimate Easy"); + } 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 = tp.x - 16; + tp.x -= 16; ESP_LOGV(TAG, "Long touch - Released (x=%d)", tp.x); this->trigger_long_touch_release_.trigger(tp); } else { @@ -78,55 +89,51 @@ namespace esphome { } } - bool TxUltimateEasy::is_valid_data(int uart_received_bytes[]) { - bool valid = true; - if (!(uart_received_bytes[0] == 170 && uart_received_bytes[1] == 85 && uart_received_bytes[2] == 1 && uart_received_bytes[3] == 2)) return false; - - int state = get_touch_state(uart_received_bytes); - if (state != TOUCH_STATE_PRESS && - state != TOUCH_STATE_RELEASE && - state != TOUCH_STATE_SWIPE_LEFT && - state != TOUCH_STATE_SWIPE_RIGHT && - state != TOUCH_STATE_MULTI_TOUCH) + 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; + } - if (uart_received_bytes[6] < 0 && state != TOUCH_STATE_MULTI_TOUCH) return false; - - return true; + 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) && + (uart_received_bytes[6] >= 0 || state == TOUCH_STATE_MULTI_TOUCH); } - int TxUltimateEasy::get_touch_position_x(int uart_received_bytes[]) { - int state = uart_received_bytes[4]; - switch (state) { + 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: case TOUCH_STATE_SWIPE_LEFT: case TOUCH_STATE_SWIPE_RIGHT: return uart_received_bytes[5]; - break; default: return uart_received_bytes[6]; - break; } } - int TxUltimateEasy::get_touch_state(int uart_received_bytes[]) { + 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) { - if (uart_received_bytes[5] == TOUCH_STATE_SWIPE_RIGHT) state = TOUCH_STATE_SWIPE_RIGHT; - else if (uart_received_bytes[5] == TOUCH_STATE_SWIPE_LEFT) state = TOUCH_STATE_SWIPE_LEFT; + 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(int uart_received_bytes[]) { + TouchPoint TxUltimateEasy::get_touch_point(const std::array &uart_received_bytes) { TouchPoint tp; - tp.x = get_touch_position_x(uart_received_bytes); - tp.state = get_touch_state(uart_received_bytes); - tp.state_str = "Unknown"; + tp.x = this->get_touch_position_x(uart_received_bytes); + tp.state = this->get_touch_state(uart_received_bytes); switch (tp.state) { case TOUCH_STATE_RELEASE: tp.state_str = "RELEASE"; @@ -146,9 +153,12 @@ namespace esphome { 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 \ No newline at end of file +} // namespace esphome diff --git a/components/tx_ultimate_easy/tx_ultimate_easy.h b/components/tx_ultimate_easy/tx_ultimate_easy.h index 033e471..5d7b3a0 100644 --- a/components/tx_ultimate_easy/tx_ultimate_easy.h +++ b/components/tx_ultimate_easy/tx_ultimate_easy.h @@ -1,3 +1,5 @@ +// tx_ultimate_easy.h + #pragma once #include "esphome/core/automation.h" @@ -7,15 +9,27 @@ #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 { - const static uint8_t TOUCH_STATE_RELEASE = 1; - const static uint8_t TOUCH_STATE_PRESS = 2; - const static uint8_t TOUCH_STATE_SWIPE = 3; - const static uint8_t TOUCH_STATE_MULTI_TOUCH = 11; - const static uint8_t TOUCH_STATE_SWIPE_RIGHT = 12; - const static uint8_t TOUCH_STATE_SWIPE_LEFT = 13; + // 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 { int8_t x = -1; @@ -41,12 +55,12 @@ namespace esphome { protected: void send_touch_(TouchPoint tp); - void handle_touch(int bytes[]); + void handle_touch(const std::array &bytes); - TouchPoint get_touch_point(int bytes[]); - bool is_valid_data(int bytes[]); - int get_touch_position_x(int bytes[]); - int get_touch_state(int 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_; @@ -59,4 +73,4 @@ namespace esphome { }; // class TxUltimateEasy } // namespace tx_ultimate_easy -} // namespace esphome \ No newline at end of file +} // namespace esphome