Skip to content

Instantly share code, notes, and snippets.

@wolph
Last active October 22, 2024 03:38
Show Gist options
  • Save wolph/42024a983e4dfb0bc1dcbe6882979d21 to your computer and use it in GitHub Desktop.
Save wolph/42024a983e4dfb0bc1dcbe6882979d21 to your computer and use it in GitHub Desktop.
ESPHome Sonoff TX Ultimate T5-3C-86 Custom button component
light:
- platform: neopixelbus
type: GRB
variant: WS2812
pin: GPIO1
num_leds: 1
name: "NeoPixel 1"
internal: true
- platform: neopixelbus
type: GRB
variant: WS2812
pin: GPIO3
num_leds: 1
name: "NeoPixel 3"
internal: true
- platform: neopixelbus
type: GRB
variant: WS2812
pin: GPIO5
num_leds: 1
name: "NeoPixel 5"
internal: true
- platform: neopixelbus
type: GRB
variant: WS2812
pin: GPIO13
num_leds: 1
name: "NeoPixel 13"
internal: true
- platform: neopixelbus
type: GRB
variant: WS2812
pin: GPIO32
num_leds: 1
name: "NeoPixel 32"
internal: true
- platform: neopixelbus
type: GRB
variant: WS2812
# pin: GPIO20
pin: GPIO33
num_leds: 28
name: "Ambience Nightlight"
id: rgb_light
effects:
- addressable_twinkle:
- addressable_random_twinkle:
- addressable_rainbow:
name: 'rainbow slow'
speed: 5
- addressable_rainbow:
name: 'rainbow fast'
speed: 50
- addressable_rainbow:
name: 'rainbow superfast'
speed: 100
restore_mode: RESTORE_DEFAULT_OFF
# - platform: partition
# id: light_left_top
# name: "light left top"
# segments:
# - id: rgb_light
# from: 20
# to: 20
# - platform: partition
# id: light_middle_top
# name: "light middle top"
# segments:
# - id: rgb_light
# from: 23
# to: 23
# - platform: partition
# id: light_right_top
# name: "light right top"
# segments:
# - id: rgb_light
# from: 26
# to: 27
# - platform: partition
# id: light_left_bottom
# name: "light left bottom"
# segments:
# - id: rgb_light
# from: 6
# to: 7
# - platform: partition
# id: light_middle_bottom
# name: "light middle bottom"
# segments:
# - id: rgb_light
# from: 8
# to: 10
# - platform: partition
# id: light_right_bottom
# name: "light right bottom"
# segments:
# - id: rgb_light
# from: 11
# to: 12
- platform: partition
restore_mode: RESTORE_DEFAULT_OFF
id: light_left_both
name: "light left both"
segments:
- id: rgb_light
from: 11
to: 21
effects:
- addressable_twinkle:
- addressable_random_twinkle:
- addressable_rainbow:
speed: 5
- platform: partition
restore_mode: RESTORE_DEFAULT_OFF
id: light_middle_both
name: "light middle both"
segments:
- id: rgb_light
from: 22
to: 24
- id: rgb_light
from: 8
to: 10
effects:
- addressable_twinkle:
- addressable_random_twinkle:
- addressable_rainbow:
speed: 5
- platform: partition
restore_mode: RESTORE_DEFAULT_OFF
id: light_right_both
name: "light right both"
segments:
- id: rgb_light
from: 0
to: 7
- id: rgb_light
from: 25
to: 27
effects:
- addressable_twinkle:
- addressable_random_twinkle:
- addressable_rainbow:
speed: 5
- platform: partition
restore_mode: RESTORE_DEFAULT_OFF
id: light_right
name: "light right"
segments:
- id: rgb_light
from: 0
to: 5
# - platform: partition
# id: light_bottom
# name: "light bottom"
# segments:
# - id: rgb_light
# from: 6
# to: 12
- platform: partition
restore_mode: RESTORE_DEFAULT_OFF
id: light_left
name: "light left"
segments:
- id: rgb_light
from: 13
to: 19
# - platform: partition
# id: light_top
# name: "light top"
# segments:
# - id: rgb_light
# from: 20
# to: 26
condition:
switch.is_on: ${switch}
then:
- light.turn_on:
id: ${light}
brightness: ${on_brightness}
red: ${on_red}
green: ${on_green}
blue: ${on_blue}
transition_length: 500ms
else:
- if:
condition:
and:
- lambda: |-
return id(htime).now().hour < ${nightlight_on_hour};
- lambda: |-
return id(htime).now().hour > ${nightlight_off_hour};
then:
- light.turn_off:
id: ${light}
transition_length: 500ms
else:
- light.turn_on:
id: ${light}
brightness: ${nightlight_brightness}
red: ${nightlight_red}
green: ${nightlight_green}
blue: ${nightlight_blue}
transition_length: 500ms
substitutions:
name: 'sonoff_tx_ultimate_00'
on_brightness: 50%
on_red: 100%
on_green: 40%
on_blue: 0%
nightlight_brightness: 20%
nightlight_red: 100%
nightlight_green: 40%
nightlight_blue: 0%
nightlight_on_hour: '19' # turn on at 7 in the evening
nightlight_off_hour: '9' # turn off at 9 in the morning
esphome:
name: $name
platform: ESP32
board: esp32dev
on_boot:
priority: -100.0
then:
- script.execute: light_relays
includes:
- touch_panel.hpp
- touch_panel.cpp
uart:
id: uart_bus
tx_pin: GPIO19
rx_pin: GPIO22
baud_rate: 115200
script:
- id: light_relays
then:
- if: !include
file: relays_light.yaml
vars:
switch: relay1
light: light_left_both
- if: !include
file: relays_light.yaml
vars:
switch: relay2
light: light_middle_both
- if: !include
file: relays_light.yaml
vars:
switch: relay3
light: light_right_both
binary_sensor:
- platform: custom
lambda: |-
auto touch_panel = new touch_panel::TouchPanel(id(uart_bus));
App.register_component(touch_panel);
return {
touch_panel->left,
touch_panel->middle,
touch_panel->right,
touch_panel->two_finger,
touch_panel->dragged_ltr,
touch_panel->dragged_rtl,
};
binary_sensors:
- id: button_left
name: "Left Button"
on_press:
- switch.toggle: relay1
- switch.turn_on: haptics
- id: button_middle
name: "Middle Button"
on_press:
- switch.toggle: relay2
- switch.turn_on: haptics
- id: button_right
name: "Right Button"
on_press:
- switch.toggle: relay3
- switch.turn_on: haptics
- id: button_two_finger
name: "Two Fingers"
- id: button_dragged_ltr
name: "Dragged Left to Right"
- id: button_dragged_rtl
name: "Dragged Right to Left"
switch:
- platform: gpio
name: "relay left"
pin: GPIO18
id: relay1
on_turn_on:
script.execute: light_relays
on_turn_off:
script.execute: light_relays
- platform: gpio
name: "relay middle"
pin: GPIO17
id: relay2
on_turn_on:
script.execute: light_relays
on_turn_off:
script.execute: light_relays
- platform: gpio
name: "relay right"
pin: GPIO27
id: relay3
on_turn_on:
script.execute: light_relays
on_turn_off:
script.execute: light_relays
- platform: gpio
name: "relay right"
pin: GPIO23
id: relay4
on_turn_on:
script.execute: light_relays
on_turn_off:
script.execute: light_relays
- platform: gpio
name: "sound amplifier power"
pin: GPIO26
id: pa_sw
- platform: gpio
name: "touch panel power"
pin:
number: GPIO5
inverted: true
id: ca51_pow
restore_mode: ALWAYS_ON
- platform: gpio
pin: GPIO21
name: "Haptics"
id: "haptics"
restore_mode: ALWAYS_OFF
on_turn_on:
- delay: 300ms
- switch.turn_off: haptics
#include "touch_panel.hpp"
#include <stdio.h>
namespace esphome
{
namespace touch_panel
{
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
const uint8_t PREFIX[] = {0xAA, 0x55, 0x01, 0x02};
const unsigned int PREFIX_LENGTH = sizeof(PREFIX) / sizeof(PREFIX[0]);
// Messages consist of a prefix, a type of action and a position.
// There's probably other stuff but that's not relevant for us
const unsigned int MESSAGE_SIZE = PREFIX_LENGTH + 4;
const unsigned int BUFFER_SIZE = 32;
TouchPanel::TouchPanel(uart::UARTComponent *parent) : uart::UARTDevice(parent) {}
void TouchPanel::setup()
{
for (int i = 0; i < button_map.size(); i++)
button_map[i]->publish_state(i < TwoFinger);
}
void TouchPanel::loop()
{
static uint8_t buffer[BUFFER_SIZE];
while (available() >= MESSAGE_SIZE)
{
read_array(buffer, MIN(available(), BUFFER_SIZE));
bool match = true;
if (memcmp(buffer, PREFIX, PREFIX_LENGTH) != 0)
{
continue;
}
if (match)
{
TouchEvent event = static_cast<TouchEvent>(buffer[4]);
uint8_t released_position = buffer[5];
uint8_t pressed_position = buffer[6];
if (event == Pressed)
{
// ESP_LOGD("custom", "Pressed: %d", pressed_position);
button_map[pressed_position]->publish_state(true);
// Always trigger the two-fingered and drag events since we can only detect when they are released
two_finger->publish_state(true);
dragged_ltr->publish_state(true);
dragged_rtl->publish_state(true);
}
else if (event == Released || event == Dragged)
{
// Make sure to reset all buttons
for (int i = 0; i < TwoFinger; i++)
button_map[i]->publish_state(false);
button_map[released_position]->publish_state(false);
}
else
{
ESP_LOGD("custom", "Unknown event: %d", event);
}
}
}
}
} // namespace touch_panel
} // namespace esphome
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/uart/uart.h"
#include "esphome/components/binary_sensor/binary_sensor.h"
#include <vector>
namespace esphome
{
namespace touch_panel
{
enum TouchEvent
{
Released = 1,
Pressed = 2,
Dragged = 3
};
enum TouchPosition
{
Left = 1,
Middle = 4,
Right = 7,
TwoFinger = 11,
DraggedLTR = 12,
DraggedRTL = 13
};
class TouchPanel : public Component, public uart::UARTDevice
{
public:
TouchPanel(uart::UARTComponent *parent);
void setup() override;
void loop() override;
float get_setup_priority() const override { return esphome::setup_priority::BUS; }
binary_sensor::BinarySensor *left = new binary_sensor::BinarySensor();
binary_sensor::BinarySensor *middle = new binary_sensor::BinarySensor();
binary_sensor::BinarySensor *right = new binary_sensor::BinarySensor();
binary_sensor::BinarySensor *two_finger = new binary_sensor::BinarySensor();
binary_sensor::BinarySensor *dragged_ltr = new binary_sensor::BinarySensor();
binary_sensor::BinarySensor *dragged_rtl = new binary_sensor::BinarySensor();
protected:
// For positional tracking
std::vector<binary_sensor::BinarySensor *> button_map = {
left, // 0 never occurs but is here for completeness
left, // 1-3 is left
left,
left,
middle, // 4-6 is middle
middle,
middle,
right, // 7-10 is right
right,
right,
right,
two_finger, // 11 is two fingers (released only)
dragged_ltr, // 12 is drag left to right
dragged_rtl, // 13 is drag right to left
};
};
} // namespace adalight
} // namespace esphome
@grrraham
Copy link

I've been trying out the code on a 1 gang version but I always get touch press trigger together with two finger or drag gestures.

Do you have any notes on the UART protocol used?

Sound is working but it's quite terrible https://twitter.com/blakadder_/status/1661863695788433409?s=20

On the other hand you can use the motor as a buzzer ;) https://twitter.com/blakadder_/status/1661866776303480832?s=20

I got the sound working ok and commented on https://www.youtube.com/watch?v=naDLhX89enQ&t
Copy his i2saudio and media player settings, then add "i2s_comm_fmt: lsb"
media_player:

  • platform: i2s_audio
    i2s_comm_fmt: lsb

@iUnstable0
Copy link

does this work with the 4 gang switch?

@wolph
Copy link
Author

wolph commented Dec 18, 2023

does this work with the 4 gang switch?

You'll have to modify it slightly to do that. Currently it goes up to relay3 but relay4 is already defined because I suspected that they would come out with a 4-relay version. To modify it you'll have to adjust which section of the lights you want to turn on and what part of the button it should respond to.

@MantasGSXR
Copy link

Can somebody with the original firmware verify how much the volume can be pushed to? I've been able to play music in the TX, but volume is a little bit too low: https://streamable.com/mxw6ms

I've got a backup of the original firmware that I can restore, but I'm not entirely sure what you want me to do. I have a sound level meter that I can keep at a fixed distance before I click the button but I'm not sure it's loud enough to give any useful results at 1 meter

Please, please could you share your backup with me. I wasn't able to make a backup before I flashed my test unit and would like to go back to factory for a bit.

@wolph
Copy link
Author

wolph commented Apr 11, 2024

@MantasGSXR sorry to tell you but that backup won't help you. The firmwares are locked to the mac address so the backup won't work on your device unless it was made by your device

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment