Skip to content

Instantly share code, notes, and snippets.

Created August 13, 2023 19:44
Show Gist options
  • Save andrewcchen/f16eb20d19ea64d9f997c470e2addeaa to your computer and use it in GitHub Desktop.
Save andrewcchen/f16eb20d19ea64d9f997c470e2addeaa to your computer and use it in GitHub Desktop.
Encode an NEC IR command into code for Tuya ZS06/ZS08/TS1201
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.
encode_nec("04fb08f7") // encodes nec address 0x04 and command 0x08
Tuya ZS06 uses a custom encoding of ir codes. I figured enough of the encoding to generate arbitary ir codes, but there are additional encoding features to save bytes that I haven't figured out.
The encoding is base64 encoding of any number of concatenated command blocks, where each block is:
- 1 byte of [length]-1 (length <= 32 bytes is supported)
- [length] number of bytes of little endian 16 bit integers
- Each integer describes the length of time in microseconds to keep the tramsitter's current on/off state, before flipping
The initial state is on, so the first 16 bit integer describes on time, the second off time, the third on time again, etc.
function encode_nec(hex) {
function le(x) {
x = x & 0xffff;
return [ x & 0xff, x >> 8 ];
let output = [ 4-1, ...le(9000), ...le(4500) ];
for (const x of Buffer.from(hex, 'hex')) {
for (let i = 0; i < 8; i++) {
if (x & (1 << i)) {
} else {
output.push(2-1, ...le(560));
return Buffer.from(new Uint8Array(output)).toString('base64');
Copy link

mak-42 commented Aug 20, 2023

I tried to test it with Tuya TS1201 for DUNE HD Player and failed. @andrewcchen, could you please prompt me where I was wrong?

The documentation of Dune HD player says:

All Dune HD IR Remotes use NEC IR protocol. Different generations of Remotes use different NEC IR customer codes:
Previous generation (Premium IR Remote + discontinued models): 00 BF
IR HEX codes (latest Remote models use CF CF instead 00 BF):

I called the script with "00bf32bc" and it returned "AygjlBEfMAI1AjACNQIwAjUCMAI1AjACNQIwAjUCMAI1AjACNQIfMAKaBjACmgYwApoGMAKaBjACmgYwApoGMAI1AjACmgYfMAKaBjACmgYwAjUCMAI1AjACNQIwAjUCMAKaBjACNQIfMAI1AjACNQIwApoGMAKaBjACmgYwApoGMAI1AjACmgYBMAI=". I sent it but DUNE HD Player did not respond. I tried "cfcf43bc" and "01be43bc", but it did not help as well.
The sequence generated by the script does not look like the sequence I captured: "B08jRhFwAvsB4BUDAVwG4A0DQDfgAxtAD+ADA0Ab4AcPQBPAAwf7AXACXAZwAg==".

Copy link

@mak-42 Some codes online have bit endian order swapped (per byte), try 00df2cd3

The captured sequences uses commands that I haven't managed to decoded to encode more efficiently.

Copy link

mak-42 commented Aug 20, 2023

Thank you for the answer. I tried "00df2cd3" (AygjlBEfMAI1AjACNQIwAjUCMAI1AjACNQIwAjUCMAI1AjACNQIfMAKaBjACmgYwApoGMAKaBjACmgYwAjUCMAKaBjACmgYfMAI1AjACNQIwApoGMAKaBjACNQIwApoGMAI1AjACNQIfMAKaBjACmgYwAjUCMAI1AjACmgYwAjUCMAKaBjACmgYBMAI=), "3f3f2cd3" (AygjlBEfMAKaBjACmgYwApoGMAKaBjACmgYwApoGMAI1AjACNQIfMAKaBjACmgYwApoGMAKaBjACmgYwApoGMAI1AjACNQIfMAI1AjACNQIwApoGMAKaBjACNQIwApoGMAI1AjACNQIfMAKaBjACmgYwAjUCMAI1AjACmgYwAjUCMAKaBjACmgYBMAI=) and "08d72cd3" (AygjlBEfMAI1AjACNQIwAjUCMAKaBjACNQIwAjUCMAI1AjACNQIfMAKaBjACmgYwApoGMAI1AjACmgYwAjUCMAKaBjACmgYfMAI1AjACNQIwApoGMAKaBjACNQIwApoGMAI1AjACNQIfMAKaBjACmgYwAjUCMAI1AjACmgYwAjUCMAKaBjACmgYBMAI=). The last one is a representation "01be34bc" used by very old DUNE players. Unfortunately, the player didn't respond for any of the sequences.

Copy link

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:

Then copy pasted it onto

volume down:
node index.js 22220Ff7
volume up:
node index.js 22220Ef7
node index.js 222201f7
node index.js 222207f7

Copy link

mildsunrise commented Feb 6, 2024

for anyone landing here: I've managed to understand the weird compression scheme used by this thing!
I've documented everything here:

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