I'm using my clanker to control an AC, and we found a bug while attempting to control a TCL AC (2024 model). I've asked it to do a write-up, and here it is:
Summary
server.py:175 uses max(0, pat[i+1]) to process IR pulse arrays, converting all space (gap) durations to 0µs. This collapses distinct commands from pulse-distance protocols (NEC, Sony SIRC, RC-5, etc.) into identical compressed output.
Root Cause
In _compress_pulses() (server.py, approximately line 174-176):
for i in range(0, len(pat) - 1, 2):
on = max(0, pat[i]) # mark — positive, preserved
off = max(0, pat[i+1]) # space — BUG: negative values → 0
IR pulse arrays use alternating positive (mark) and negative (space) values from the learn handler:
signed = [t if i%2==0 else -t for i,t in enumerate(timings)]
# Result: [3120, -1568, 528, -288, 528, -1056, ...]
max(0, -288) → 0, max(0, -1056) → 0. Every space becomes 0 regardless of duration. Since pulse-distance protocols encode information in space duration (e.g., short=0, long=1), this destroys the bit stream.
Reproduction
Tested with TCL AC remote on D552 variant (ElkSMART VID 045C, PID 0195):
import json
from server import encode_ir # or irblaster in refactored version
# Pre-fix: four distinct codes produce identical compressed output
for name in ['Fan_Speed_Min', 'Fan_Speed_Turbo', 'Temp_19', 'Temp_26']:
d = json.load(open(f'codes/{name}.json'))
frames = encode_ir(38000, d['pulses'], 'd552')
sig = bytes(b for f in frames for b in f)[10:25].hex()
print(f'{name:20s} {sig}')
# Output (pre-fix):
# Fan_Speed_Min 01002100ffffff0100000000000000
# Fan_Speed_Turbo 01002100ffffff0100000000000000 ← same
# Temp_19 01002100ffffff0100000000000000 ← same
# Temp_26 01002100ffffff0100000000000000 ← same
Impact
Any pulse-distance IR protocol is affected when used with the D552 variant. The compression collapses codes that differ only in space timing, making it impossible to:
- Send absolute temperature commands to AC units (only toggle works)
- Distinguish related commands (e.g., fan speed 1 vs fan speed 2)
- Use any remote that encodes data in gap duration
Four of nine TCL AC codes collapsed into one compressed signature. The same would happen with NEC, Sony SIRC, RC-5, and other common protocols.
Fix
One line at server.py:175:
# Before:
off = max(0, pat[i+1]) if i+1 < len(pat) else 10000
# After:
off = abs(pat[i+1]) if i+1 < len(pat) else 10000
Verification (Post-Fix)
D552 firmware confirmed to accept non-zero space values (ACK on all codes). Post-fix, all 8 codes produce unique compressed output:
Power_On 122012ffffffc20161214220420000
Power_Off 422012ffffffc201621f431f431f13
Fan_Speed_Min 122111ffffffc30161214121410101
Fan_Speed_Turbo 122111ffffffc30161214121420100
Fan_Speed_Variable 122111ffffffc30162204220422012
Temp_19 122111ffffffc30160214121410000
Temp_24 122111ffffffc30160214121410101
Temp_26 122111ffffffc30161214121410000
All verified working on live TCL AC unit.
Additional Context
max(0, x) is a sign-handling anti-pattern when x can be negative — abs() is the correct primitive
- The D552 protocol format supports non-zero space values; the firmware handles them without issue
- This affects the D552 variant specifically (identified by handshake byte
0x70 0x01). D226 variant uses Huffman encoding path and may or may not be affected
- Full postmortem and investigation: available on request
I'm using my clanker to control an AC, and we found a bug while attempting to control a TCL AC (2024 model). I've asked it to do a write-up, and here it is:
Summary
server.py:175usesmax(0, pat[i+1])to process IR pulse arrays, converting all space (gap) durations to 0µs. This collapses distinct commands from pulse-distance protocols (NEC, Sony SIRC, RC-5, etc.) into identical compressed output.Root Cause
In
_compress_pulses()(server.py, approximately line 174-176):IR pulse arrays use alternating positive (mark) and negative (space) values from the learn handler:
max(0, -288)→ 0,max(0, -1056)→ 0. Every space becomes 0 regardless of duration. Since pulse-distance protocols encode information in space duration (e.g., short=0, long=1), this destroys the bit stream.Reproduction
Tested with TCL AC remote on D552 variant (ElkSMART VID 045C, PID 0195):
Impact
Any pulse-distance IR protocol is affected when used with the D552 variant. The compression collapses codes that differ only in space timing, making it impossible to:
Four of nine TCL AC codes collapsed into one compressed signature. The same would happen with NEC, Sony SIRC, RC-5, and other common protocols.
Fix
One line at
server.py:175:Verification (Post-Fix)
D552 firmware confirmed to accept non-zero space values (ACK on all codes). Post-fix, all 8 codes produce unique compressed output:
All verified working on live TCL AC unit.
Additional Context
max(0, x)is a sign-handling anti-pattern whenxcan be negative —abs()is the correct primitive0x70 0x01). D226 variant uses Huffman encoding path and may or may not be affected