Last active
November 1, 2025 16:08
-
-
Save andrewcchen/f16eb20d19ea64d9f997c470e2addeaa to your computer and use it in GitHub Desktop.
Encode an NEC IR command into code for Tuya ZS06/ZS08/TS1201
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| Encode an NEC IR command into code for Tuya ZS06/ZS08/TS1201 | |
| Usage: encode_nec("<four bytes in hex, two bytes of address followed by two bytes of command>") | |
| If your address and/or command is just one byte (8 bits), append the complement of the byte after it to make it two bytes. | |
| Example: | |
| encode_nec("04fb08f7") // encodes nec address 0x04 and command 0x08 | |
| See: | |
| https://www.sbprojects.net/knowledge/ir/nec.php | |
| https://www.zigbee2mqtt.io/devices/ZS06.html | |
| https://github.com/Koenkk/zigbee2mqtt/issues/11633 | |
| mildsunrise describes how the encoding works in: | |
| https://gist.github.com/mildsunrise/1d576669b63a260d2cff35fda63ec0b5 | |
| In short, the signal is described in terms of alternating on/off (first on) times in microseconds encoded in little-endian 16-bit integers, which is then compressed with FastLZ. | |
| This encoded only generates literal blocks, which are up to 32 bytes of data long each, prefixed by a 1 byte length field. | |
| */ | |
| function encode_nec(hex) { | |
| function le(x) { | |
| x = x & 0xffff; | |
| return [ x & 0xff, x >> 8 ]; | |
| } | |
| // for NECx2 devices (e.g. samsung tv) replace 9000 with 4500 | |
| let output = [ 4-1, ...le(9000), ...le(4500) ]; | |
| for (const x of Buffer.from(hex, 'hex')) { | |
| output.push(32-1); | |
| for (let i = 0; i < 8; i++) { | |
| output.push(...le(560)); | |
| if (x & (1 << i)) { | |
| output.push(...le(2250-560)); | |
| } else { | |
| output.push(...le(1125-560)); | |
| } | |
| } | |
| } | |
| output.push(2-1, ...le(560)); | |
| return Buffer.from(new Uint8Array(output)).toString('base64'); | |
| } |
for anyone landing here: I've managed to understand the weird compression scheme used by this thing!
I've documented everything here:
https://gist.github.com/mildsunrise/1d576669b63a260d2cff35fda63ec0b5
and also provided a function to decompress the signal in case someone wants to investigate what's in their learnt codes.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Just used this to convert the Loxjie D30/A30 DAC IR codes and worked like a charm, thanks!
I've added this line to the bottom of the script for easier usage:
console.log(encode_nec(process.argv));Then copy pasted it onto https://stackblitz.com/edit/node-trcord?file=index.js
Reference:
https://www.audiosciencereview.com/forum/index.php?attachments/1630327364341-png.150422/
volume down:
node index.js 22220Ff7volume up:
node index.js 22220Ef7power:
node index.js 222201f7input:
node index.js 222207f7