From cdf6707d3007b8064130ac5f8857223e039c02fa Mon Sep 17 00:00:00 2001 From: Prakash S <660491+scienty@users.noreply.github.com> Date: Sun, 24 Aug 2025 21:21:07 +0530 Subject: [PATCH 1/4] Driver that is working for t5-4c-86 --- t5-4c-86/txultimate.be | 113 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 t5-4c-86/txultimate.be diff --git a/t5-4c-86/txultimate.be b/t5-4c-86/txultimate.be new file mode 100644 index 0000000..31cc4cd --- /dev/null +++ b/t5-4c-86/txultimate.be @@ -0,0 +1,113 @@ +class TXUltimate : Driver + static header = bytes('AA55') + var ser # create serial port object + + # intialize the serial port, if unspecified Tx/Rx are GPIO 16/17 + def init(tx, rx) + if !tx tx = 19 end + if !rx rx = 22 end + self.ser = serial(rx, tx, 115200, serial.SERIAL_8N1) + tasmota.add_driver(self) + end + + def split_55(b) + var ret = [] + var s = size(b) + var i = s-2 # start from last-1 + while i > 0 + if b[i] == 0xAA && b[i+1] == 0x55 + ret.push(b[i..s-1]) # push last msg to list + b = b[(0..i-1)] # write the rest back to b + end + i -= 1 + end + ret.push(b) + return ret + end + + def crc16(data, poly) + if !poly poly = 0x1021 end + # CRC-16/CCITT-FALSE HASHING ALGORITHM + var crc = 0xFFFF + for i:0..size(data)-1 + crc = crc ^ data[i] << 8 + for j:0..7 + if crc & 0x8000 + crc = (crc << 1) ^ poly + else + crc = crc << 1 + end + end + end + return crc & 0xFFFF + end + + def encode(payload) + var msg_crc = self.crc16(bytes(payload)) # calc crc + var b = bytes('AA55') # add header + b += bytes(payload) # add payload + b.add(msg_crc, -2) # add calculated crc 2 bytes, big endian + return b + end + + + # send a string payload (needs to be a valid json string) + def send(payload) + print("TXU: Sent =", payload) + var payload_bin = self.encode(payload) + self.ser.write(payload_bin) + log("TXU: Sent = " + str(payload_bin), 2) + end + + # read serial port + def every_50ms() + if self.ser.available() > 0 + var msg = self.ser.read() # read bytes from serial as bytes + import string + if size(msg) > 0 + # print("TXU: Raw =", msg) + if msg[0..1] == self.header + print("TXU: Message", msg, msg[4], msg[5]) + var lst = self.split_55(msg) + for i:0..size(lst)-1 + msg = lst[i] + var event = "" + var params = "" + if msg[2] == 0x01 && msg[3] == 0x02 # 02 signifies a touch event + print('Message Type:', msg[4], msg[5]) + if (msg[4] == 0x02 && msg[5] != 0) || msg[4] == 0x01 # 2 is a press and 1 is release + if msg[5] < 0x0B + event= "Short" + params = ',"Channel":' + str(msg[5]) + elif msg[5] == 0x0B + event= "Multi" + elif msg[5] > 0x0B + event = "Long" + params = ',"Channel":' + str(msg[5]-16) + end + elif msg[4] == 0x03 # 3 is a swipe + if msg[5] == 0x0C + event = "Swipe right" + params = ',"From":' + str(msg[6]) + ',"To":' + str(msg[7]) + print('Swipe left-right', msg[6], '->', msg[7]) + elif msg[5] == 0x0D + event = "Swipe left" + params = ',"From":' + str(msg[6]) + ',"To":' + str(msg[7]) + print('Swipe right-left', msg[6], '->', msg[7]) + end + end + if event != "" + var jm = string.format("{\"TXUltimate\":{\"Action\":\"%s\"%s}}",event,params) + tasmota.publish_result(jm, "RESULT") + jm = string.format("{\"TXUltimate\":{\"Action\":\"Touch\",\"Event\":\"%s\"}}",event) + tasmota.publish_result(jm, "RESULT") + end + end + end + end + end + end + end +end + +txu=TXUltimate() From 8afcd7046e847a61fe527d0f5147712ca8a849f7 Mon Sep 17 00:00:00 2001 From: Prakash S <660491+scienty@users.noreply.github.com> Date: Sun, 24 Aug 2025 21:22:55 +0530 Subject: [PATCH 2/4] Rules with buzzer and led animitation This is based on original PR https://github.com/blakadder/tx-ultimate/pull/3 by @mmv-ru. Thanks to @mmv-ru --- t5-4c-86/rules.be | 170 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100644 t5-4c-86/rules.be diff --git a/t5-4c-86/rules.be b/t5-4c-86/rules.be new file mode 100644 index 0000000..5507097 --- /dev/null +++ b/t5-4c-86/rules.be @@ -0,0 +1,170 @@ +# Touch events handlers + +def get_leds_for_channel(ch) + if ch <= 2 + return [13,21] + elif ch >= 3 && ch <= 5 + return [11,23] + elif ch >= 6 && ch <= 8 + return [9,25] + else + return [7,27] + end +end + +def get_leds_for_power(power) + if power == 0 + return [13,21] + elif power == 1 + return [11,23] + elif power == 2 + return [9,25] + else + return [7,27] + end +end + +var eventtopic = "stat/tasmota_E09620" +var led_state = [] +for i:0..27 + led_state.push('#000000') +end + +def TXUltimate_Touch(value, trigger, msg) + tasmota.cmd('Buzzer') +end + +def TXUltimate_Led(leds, state) + import string + var color + print("Set LED", leds, "to", state) + if state == 'ON' + color = '#14f240' + elif state == 'OFF' + color = '#ef3716' + else + color = state + end + + + for i:0..size(leds)-1 + led_state[leds[i]-1] = color + end + for i:0..4 + tasmota.cmd("Color #F0F0F0") + tasmota.cmd("Color #000000") + end + for i:0..size(led_state)-1 + tasmota.cmd(string.format("Led%d %s", i+1, led_state[i])) + end +end + +def TXUltimate_Short(value, trigger, msg) + #print("message:") + #print(msg) + import mqtt + var Channel = msg['TXUltimate']['Channel'] + var ind + var leds = [] + if Channel <= 2 + ind = 0 + leds = [13,21] + elif Channel >= 3 && Channel <= 5 + ind = 1 + leds = [11,23] + elif Channel >= 6 && Channel <= 8 + ind = 2 + leds = [9,25] + else + ind = 3 + leds = [7,27] + end + tasmota.set_power(ind, ! tasmota.get_power(ind)) + if tasmota.get_power(ind) + TXUltimate_Led(leds, 'ON') + else + TXUltimate_Led(leds, 'OFF') + end + mqtt.publish(eventtopic .. "/Short", str(ind+1), false) +end + +def TXUltimate_Long(value, trigger, msg) + import mqtt + var Channel = msg['TXUltimate']['Channel'] + var ind + if Channel <= 2 + ind = 0 + elif Channel >= 3 && Channel <= 5 + ind = 1 + elif Channel >= 6 && Channel <= 8 + ind = 2 + else + ind = 3 + end + mqtt.publish(eventtopic .. "/Long", str(ind+1), false) +end + +def TXUltimate_Swipe(value, trigger, msg) + #print("message:") + #print(msg) + import mqtt + var Action = msg['TXUltimate']['Action'] + var targetstate + if Action == 'Swipe right' + targetstate = '1' + var From = msg['TXUltimate']['From'] + var To = msg['TXUltimate']['To'] + mqtt.publish(eventtopic .. "/EVENT", "SwipeRight", false) + # NOTE: Power0 manage all channels include backlight + tasmota.cmd('Power0 1') + elif Action == 'Swipe left' + targetstate = '0' + var From = msg['TXUltimate']['From'] + var To = msg['TXUltimate']['To'] + mqtt.publish(eventtopic .. "/EVENT", "SwipeLeft", false) + # NOTE: Power0 manage all channels include backlight + tasmota.cmd('Power0 0') + end +end + + +def TXUltimate_Multi(value, trigger, msg) + print("Handler Multi.") + #print("message:") + #print(msg) + import mqtt + mqtt.publish("stat/tasmota_E09620/EVENT", "Multi", false) + # tasmota.publish_result("{'Action': 'Multi'}", "EVENT") +end + + +# Set initial state of leds +var power_state = [tasmota.get_power(0), tasmota.get_power(1), tasmota.get_power(2), tasmota.get_power(3), tasmota.get_power(4)] +for i:0..size(power_state)-1 + if power_state[i] + var leds = get_leds_for_power(i) + TXUltimate_Led(leds, 'ON') + else + var leds = get_leds_for_power(i) + TXUltimate_Led(leds, 'OFF') + end +end + +#tasmota.remove_rule("TXUltimate#Action=Touch", "TXUltimate_Touch") +tasmota.add_rule("TXUltimate#Action=Touch", TXUltimate_Touch, "TXUltimate_Touch") + +#tasmota.remove_rule("TXUltimate#Action=Short", "TXUltimate_Short") +tasmota.add_rule("TXUltimate#Action=Short", TXUltimate_Short, "TXUltimate_Short") + +#tasmota.remove_rule("TXUltimate#Action=Long", "TXUltimate_Long") +tasmota.add_rule("TXUltimate#Action=Long", TXUltimate_Long, "TXUltimate_Long") + +#tasmota.remove_rule("TXUltimate#Action=Swipe right", "TXUltimate_Swipe_right") +tasmota.add_rule("TXUltimate#Action=Swipe right", TXUltimate_Swipe, "TXUltimate_Swipe_right") +#tasmota.remove_rule("TXUltimate#Action=Swipe left", "TXUltimate_Swipe_left") +tasmota.add_rule("TXUltimate#Action=Swipe left", TXUltimate_Swipe, "TXUltimate_Swipe_left") + +#tasmota.remove_rule("TXUltimate#Action=Multi", "TXUltimate_Multi") +tasmota.add_rule("TXUltimate#Action=Multi", TXUltimate_Multi, "TXUltimate_Multi") +#tasmota.cmd('Buzzer') +print("Events loaded.") From 24473754ef381c5c2acb331858c3cdda2422fbbd Mon Sep 17 00:00:00 2001 From: Prakash S <660491+scienty@users.noreply.github.com> Date: Sun, 24 Aug 2025 21:44:13 +0530 Subject: [PATCH 3/4] Bit more efficient with visual clue --- t5-4c-86/rules.be | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/t5-4c-86/rules.be b/t5-4c-86/rules.be index 5507097..a6177bc 100644 --- a/t5-4c-86/rules.be +++ b/t5-4c-86/rules.be @@ -24,7 +24,7 @@ def get_leds_for_power(power) end end -var eventtopic = "stat/tasmota_E09620" +var eventtopic = "stat/lrms01" var led_state = [] for i:0..27 led_state.push('#000000') @@ -50,7 +50,7 @@ def TXUltimate_Led(leds, state) for i:0..size(leds)-1 led_state[leds[i]-1] = color end - for i:0..4 + for i:0..2 tasmota.cmd("Color #F0F0F0") tasmota.cmd("Color #000000") end From c21014e4a434798f854f9dfb2888ff6a41c9df65 Mon Sep 17 00:00:00 2001 From: Prakash S <660491+scienty@users.noreply.github.com> Date: Sun, 24 Aug 2025 22:11:07 +0530 Subject: [PATCH 4/4] Few more improvements --- t5-4c-86/rules.be | 44 ++++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/t5-4c-86/rules.be b/t5-4c-86/rules.be index a6177bc..d5965dc 100644 --- a/t5-4c-86/rules.be +++ b/t5-4c-86/rules.be @@ -1,4 +1,9 @@ # Touch events handlers +var eventtopic = "stat/lrms01" +var led_state = [] +for i:0..27 + led_state += ['#000000'] +end def get_leds_for_channel(ch) if ch <= 2 @@ -24,15 +29,20 @@ def get_leds_for_power(power) end end -var eventtopic = "stat/lrms01" -var led_state = [] -for i:0..27 - led_state.push('#000000') +# Set initial state of leds +var power_state = [tasmota.get_power(0), tasmota.get_power(1), tasmota.get_power(2), tasmota.get_power(3), tasmota.get_power(4)] +def TXUltimate_UpdateLeds() + for i:0..size(power_state)-1 + if power_state[i] + var leds = get_leds_for_power(i) + TXUltimate_Led(leds, 'ON') + else + var leds = get_leds_for_power(i) + TXUltimate_Led(leds, 'OFF') + end + end end -def TXUltimate_Touch(value, trigger, msg) - tasmota.cmd('Buzzer') -end def TXUltimate_Led(leds, state) import string @@ -59,6 +69,10 @@ def TXUltimate_Led(leds, state) end end +def TXUltimate_Touch(value, trigger, msg) + tasmota.cmd('Buzzer') +end + def TXUltimate_Short(value, trigger, msg) #print("message:") #print(msg) @@ -125,6 +139,7 @@ def TXUltimate_Swipe(value, trigger, msg) # NOTE: Power0 manage all channels include backlight tasmota.cmd('Power0 0') end + TXUltimate_UpdateLeds() end @@ -136,20 +151,9 @@ def TXUltimate_Multi(value, trigger, msg) mqtt.publish("stat/tasmota_E09620/EVENT", "Multi", false) # tasmota.publish_result("{'Action': 'Multi'}", "EVENT") end +TXUltimate_UpdateLeds() -# Set initial state of leds -var power_state = [tasmota.get_power(0), tasmota.get_power(1), tasmota.get_power(2), tasmota.get_power(3), tasmota.get_power(4)] -for i:0..size(power_state)-1 - if power_state[i] - var leds = get_leds_for_power(i) - TXUltimate_Led(leds, 'ON') - else - var leds = get_leds_for_power(i) - TXUltimate_Led(leds, 'OFF') - end -end - #tasmota.remove_rule("TXUltimate#Action=Touch", "TXUltimate_Touch") tasmota.add_rule("TXUltimate#Action=Touch", TXUltimate_Touch, "TXUltimate_Touch") @@ -166,5 +170,5 @@ tasmota.add_rule("TXUltimate#Action=Swipe left", TXUltimate_Swipe, "TXUltimate_S #tasmota.remove_rule("TXUltimate#Action=Multi", "TXUltimate_Multi") tasmota.add_rule("TXUltimate#Action=Multi", TXUltimate_Multi, "TXUltimate_Multi") -#tasmota.cmd('Buzzer') +tasmota.cmd('Buzzer') print("Events loaded.")