├── .gitignore
├── README.md
├── generator.js
├── hex.js
├── index.html
├── index.js
├── pageActions.js
├── protocol.png
├── rolcode.html
└── rolcode.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by .ignore support plugin (hsz.mobi)
2 | ### JetBrains template
3 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
4 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
5 |
6 | # User-specific stuff:
7 | .idea/**/workspace.xml
8 | .idea/**/tasks.xml
9 | .idea/dictionaries
10 |
11 | # Sensitive or high-churn files:
12 | .idea/**/dataSources/
13 | .idea/**/dataSources.ids
14 | .idea/**/dataSources.xml
15 | .idea/**/dataSources.local.xml
16 | .idea/**/sqlDataSources.xml
17 | .idea/**/dynamic.xml
18 | .idea/**/uiDesigner.xml
19 |
20 | # Gradle:
21 | .idea/**/gradle.xml
22 | .idea/**/libraries
23 |
24 | # CMake
25 | cmake-build-debug/
26 |
27 | # Mongo Explorer plugin:
28 | .idea/**/mongoSettings.xml
29 |
30 | ## File-based project format:
31 | *.iws
32 |
33 | ## Plugin-specific files:
34 |
35 | # IntelliJ
36 | out/
37 |
38 | # mpeltonen/sbt-idea plugin
39 | .idea_modules/
40 |
41 | # JIRA plugin
42 | atlassian-ide-plugin.xml
43 |
44 | # Cursive Clojure plugin
45 | .idea/replstate.xml
46 |
47 | # Crashlytics plugin (for Android Studio and IntelliJ)
48 | com_crashlytics_export_strings.xml
49 | crashlytics.properties
50 | crashlytics-build.properties
51 | fabric.properties
52 |
53 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Random Broadlink RM Code Generator And Utils
2 |
3 | Every time i want to buy some new smart component for my smart home, i need a remote, just to have
4 | some random RF code for the component. why should i buy a remote that i use only once just to learn the code?
5 | why cant i just generate some random one?
6 |
7 | So I Looked at the net for a long time and did not find any generator for RF codes. so i just created one.
8 |
9 | use https://dimagoltsman.github.io/Random-Broadlink-RM-Code-Generator/
10 |
11 | to generate your codes
12 |
13 |
14 |
15 | also, you are more than welcome to contribute and add more types (IR).
16 | you can use the protocol description here:
17 | https://github.com/mjg59/python-broadlink/blob/master/protocol.md
18 |
19 |
--------------------------------------------------------------------------------
/generator.js:
--------------------------------------------------------------------------------
1 | /*
2 | PROTOCOL:
3 |
4 | b2 RF
5 |
6 | 0c repeats
7 |
8 | 34 00 52 bytes follow (big endian) 24 pairs + 4 for the footer
9 |
10 | ## ## 24 0d for a 1, 0d 24 for a 0
11 |
12 | 0c 00 01 6f (Footer)
13 |
14 | */
15 |
16 | const HIGH_BIT = "240d";
17 | const LOW_BIT = "0d24";
18 | const BITS_ARRAY = [HIGH_BIT, LOW_BIT];
19 | const RF433 = "b2";
20 | const RF315 = "d7";
21 | const FOOTER = "0c00016f00000000";
22 | const REPEATS = "0c";
23 | const LONG_REPEAT = "5c";
24 | const BYTES = 24;
25 | const DATA_LENGTH = "3400";
26 |
27 | String.prototype.rightJustify = function (length, char) {
28 | var fill = [];
29 | while (fill.length + this.length < length) {
30 | fill[fill.length] = char;
31 | }
32 | return this + fill.join('');
33 | }
34 |
35 | String.prototype.leftJustify = function (length, char) {
36 | var fill = [];
37 | while (fill.length + this.length < length) {
38 | fill[fill.length] = char;
39 | }
40 | return fill.join('') + this;
41 | }
42 |
43 | function typePrefixOf(type) {
44 | if (type === "RF433") {
45 | return RF433;
46 | } else if (type === "RF315") {
47 | return RF315;
48 | } else {
49 | throw new Error("Unsupported transmission type.");
50 | }
51 | }
52 |
53 | function randomPulse() {
54 | return BITS_ARRAY[Math.floor(Math.random() * 2)];
55 | }
56 |
57 | function generate(type) {
58 | var code = "";
59 | for (i = 0; i < BYTES; i++) {
60 | var rand = randomPulse();
61 | code = code + rand;
62 | }
63 |
64 | var typePrefix = typePrefixOf(type);
65 |
66 | var res = typePrefix + REPEATS + DATA_LENGTH + code + FOOTER;
67 | var resWithRepeat = typePrefix + LONG_REPEAT + DATA_LENGTH + code + FOOTER;
68 |
69 | return {
70 | regular: hexToBase64(res),
71 | long: hexToBase64(resWithRepeat)
72 | }
73 | }
74 |
75 | function getRepeats(b64) {
76 | var hex = base64ToHex(b64).replace(/ /g, '');
77 | var repeats = hex.substr(2, 2);
78 | var decimal = parseInt(repeats, 16);
79 | return decimal;
80 | }
81 |
82 | function getNewCode(b64, repeats) {
83 | var hex = base64ToHex(b64).replace(/ /g, '');
84 | var start = hex.substr(0, 2);
85 | var end = hex.substr(4);
86 |
87 | var hexrepeats = parseInt(repeats).toString(16);
88 |
89 | if (hexrepeats.length == 1) {
90 | hexrepeats = "0" + hexrepeats;
91 | }
92 |
93 | var res = (start + hexrepeats + end);
94 | return hexToBase64(res);
95 | }
96 |
97 | function generateLivolo(remoteId, btn) {
98 | // the livolo code came from https://www.tyjtyj.com/livolo.php, dont know who wrote it, but big thanx
99 | header = "b280260013";
100 | id_bin = (+remoteId).toString(2);
101 | id_bin = id_bin.leftJustify(16, 0);
102 | btn_bin = (+btn).toString(2);
103 | btn_bin = btn_bin.leftJustify(7, 0);
104 |
105 | id_btn_bin = id_bin.concat(btn_bin);
106 |
107 | id_btn_bin = id_btn_bin.replace(/0/g, "0606");
108 | id_btn_bin = id_btn_bin.replace(/1/g, "0c");
109 |
110 | hex_out = header + id_btn_bin;
111 |
112 | pad_len = 32 - (hex_out.length - 24) % 32;
113 |
114 | hex_out = hex_out + ('').leftJustify(pad_len, 0);
115 |
116 | return hexToBase64(hex_out);
117 |
118 | }
119 |
120 | function generateEnergenie() {
121 |
122 | function randomBinary(n) {
123 | for (var i = "", a = 0; a < n; a++) i += parseInt(2 * Math.random(), 10).toString();
124 | return i
125 | }
126 |
127 | function generateCode(d0d3) {
128 | // Generate transmission binary
129 | const binary = remoteIDBinary.toString() + d0d3.toString();
130 | const dec = parseInt(binary, 2);
131 |
132 | const ENER_HIGH = "1507";
133 | const ENER_LOW = "0815";
134 | const ENER_ARRAY = [ENER_LOW, ENER_HIGH];
135 | const ENER_FOOTER = "08dc000000000000";
136 | const ENER_APP_REPEATS = "08";
137 |
138 | // Turn transmission binary in to HIGH/LOW hex transmission code
139 | let code = '';
140 | for (var i = 0; i < binary.length; i++) {
141 | code += ENER_ARRAY[parseInt(binary.charAt(i), 10)];
142 | }
143 |
144 | // Construct outputs
145 | broadlink_hex = RF433 + ENER_APP_REPEATS + DATA_LENGTH + code + ENER_FOOTER;
146 | broadlink = hexToBase64(broadlink_hex);
147 | broadlink_long_hex = (RF433 + LONG_REPEAT + DATA_LENGTH + code + ENER_FOOTER);
148 | broadlink_long = hexToBase64(broadlink_long_hex);
149 |
150 | return { binary, dec, broadlink, broadlink_long, broadlink_hex, broadlink_long_hex };
151 | }
152 |
153 | let remoteIDBinary = randomBinary(20);
154 | let remoteID = parseInt(remoteIDBinary, 2)
155 |
156 | let sockets = [
157 | ['1', '1111', '1110'],
158 | ['2', '0111', '0110'],
159 | ['3', '1011', '1010'],
160 | ['4', '0011', '0010'],
161 | ['Group', '1101', '1100']
162 | ]
163 |
164 | $('#gentable, #genjson').html('');
165 |
166 | for (i in sockets) {
167 | let socket = sockets[i];
168 | let name = socket[0];
169 | let d0d3_on = socket[1];
170 | let d0d3_off = socket[2];
171 |
172 | let on_codes = generateCode(d0d3_on);
173 | let off_codes = generateCode(d0d3_off);
174 |
175 | const on_html = `
${name} On ${remoteID} ${on_codes['binary']} // ${on_codes['dec']} ${on_codes['broadlink']} ${on_codes['broadlink_long']} `;
176 | $('#gentable').append(on_html);
177 | const off_html = `${name} Off ${remoteID} ${off_codes['binary']} // ${off_codes['dec']} ${off_codes['broadlink']} ${off_codes['broadlink_long']} `;
178 | $('#gentable').append(off_html);
179 |
180 | var json = `{\n "name": "Switch ${name}",\n "type": "switch",\n "resendHexAfterReload": false,\n "data": {\n "on": "${on_codes['broadlink_long_hex']}",\n "off": "${off_codes['broadlink_long_hex']}"\n }\n},\n`;
181 | $('#genjson').append(json);
182 |
183 | }
184 |
185 | }
--------------------------------------------------------------------------------
/hex.js:
--------------------------------------------------------------------------------
1 |
2 | if (!window.atob) {
3 | var tableStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
4 | var table = tableStr.split("");
5 |
6 | window.atob = function (base64) {
7 | if (/(=[^=]+|={3,})$/.test(base64)) throw new Error("String contains an invalid character");
8 | base64 = base64.replace(/=/g, "");
9 | var n = base64.length & 3;
10 | if (n === 1) throw new Error("String contains an invalid character");
11 | for (var i = 0, j = 0, len = base64.length / 4, bin = []; i < len; ++i) {
12 | var a = tableStr.indexOf(base64[j++] || "A"), b = tableStr.indexOf(base64[j++] || "A");
13 | var c = tableStr.indexOf(base64[j++] || "A"), d = tableStr.indexOf(base64[j++] || "A");
14 | if ((a | b | c | d) < 0) throw new Error("String contains an invalid character");
15 | bin[bin.length] = ((a << 2) | (b >> 4)) & 255;
16 | bin[bin.length] = ((b << 4) | (c >> 2)) & 255;
17 | bin[bin.length] = ((c << 6) | d) & 255;
18 | };
19 | return String.fromCharCode.apply(null, bin).substr(0, bin.length + n - 4);
20 | };
21 |
22 | window.btoa = function (bin) {
23 | for (var i = 0, j = 0, len = bin.length / 3, base64 = []; i < len; ++i) {
24 | var a = bin.charCodeAt(j++), b = bin.charCodeAt(j++), c = bin.charCodeAt(j++);
25 | if ((a | b | c) > 255) throw new Error("String contains an invalid character");
26 | base64[base64.length] = table[a >> 2] + table[((a << 4) & 63) | (b >> 4)] +
27 | (isNaN(b) ? "=" : table[((b << 2) & 63) | (c >> 6)]) +
28 | (isNaN(b + c) ? "=" : table[c & 63]);
29 | }
30 | return base64.join("");
31 | };
32 |
33 | }
34 |
35 | function hexToBase64(str) {
36 | return btoa(String.fromCharCode.apply(null,
37 | str.replace(/\r|\n/g, "").replace(/([\da-fA-F]{2}) ?/g, "0x$1 ").replace(/ +$/, "").split(" "))
38 | );
39 | }
40 |
41 | function base64ToHex(str) {
42 | for (var i = 0, bin = atob(str.replace(/[ \r\n]+$/, "")), hex = []; i < bin.length; ++i) {
43 | var tmp = bin.charCodeAt(i).toString(16);
44 | if (tmp.length === 1) tmp = "0" + tmp;
45 | hex[hex.length] = tmp;
46 | }
47 | return hex.join(" ");
48 | }
49 |
50 |
51 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Broadlink RM Random Code Generator
7 |
8 |
9 |
10 |
11 |
12 |
15 |
18 |
20 |
23 |
24 |
40 |
41 |
42 |
43 |
44 |
45 |
Random Broadlink RM Code Generator
46 |
47 |
61 |
62 |
63 |
64 |
65 |
Generate Random Code
66 |
67 | type:
68 |
69 | RF433
70 | RF315
71 |
72 |
73 |
74 |
Generate
75 |
76 |
77 |
78 |
79 |
80 |
81 | Type
82 | Regular code
83 | same code with long repeat (for learning)
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
Generate Random Livolo Code
97 |
128 |
129 |
130 |
131 | Type
132 | Code
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
Generate Random Energenie Socket Set
146 |
147 |
148 |
149 | While I've written this for my Energenie remote-control sockets, it should work for any "Type D" socket as defined in the rc-switch wiki - i.e, 433mhz learning sockets.
150 | Many thanks to Energenie for documenting their accessories ; extremely helpful.
151 |
152 | Here's what I sniffed using rc-switch/figured out by bashing my head against IHC for iOS backups:
153 | RF HIGH BIT: 1507, RF LOW BIT: 0815, RF FOOTER: 08dc000000000000
154 | Format: RF (b2), Repeats: 08 (8), Data: 20 pairs (remote ID), 4 pairs (socket command), 4 pairs (footer)
155 |
156 |
157 | I was thrown for a while looking at RF dumps as HIGH
(1507) could come in as 1506 or 1508, and LOW
(0816) could be 0815 or 0817. Realise now that's just the radios being vague, not actual important information. Once we had that figured out (and rounded off!), here's the steps to this generation:
158 |
159 | Generate 20 random binary bits. This is our shiny new remote group ID!
160 | Construct the transmission hexadecimal: RF + REPEATS + DATA LENGTH + DATA + FOOTER
.
161 |
162 | RF: b2
, which is RF433 in the world of Broadlink
163 | REPEATS: 08
, 8 repeats; this is what IHC for iOS recorded when I pressed the buttons, but it's probably a bit conservative for a good transmission.
164 | DATA LENGTH: 34 00
, 52 hex pairs follow (big endian). For us, that's 40 pairs (remote group ID), 4 pairs (socket command), 8 pairs (RF footer)
165 | DATA: 1507 0815 (...)
, the HIGH/LOW bits to transmit as defined above - remote group, socket command.
166 | FOOTER: 08dc000000000000
, the footer extracted from the IHC for IOS app backups. Always the same.
167 |
168 |
169 | Generate commands with the above formula for each switch group entry: 1 On, 1 Off, 2 On, 2 Off, 3 On, 3 Off, 4 On, 4 Off, All On, All Off.
170 | Optionally, convert the hexadecimal to base64. Done!
171 |
172 |
173 |
174 |
175 |
176 |
Generate
177 |
178 |
179 |
180 |
181 |
182 | Type
183 | Remote ID
184 | Binary // rc-switch numerical
185 | Broadlink Regular Code
186 | Broadlink long repeat (for learning)
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
Change number of repeats in existing code (also works for IR codes)
204 |
205 |
223 |
224 |
225 |
226 |
227 |
228 |
234 |
235 |
236 |
237 |
238 |
239 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | function startGenerate() {
2 |
3 | var type = $('#signal_type').val();
4 | var generated = generate(type);
5 | console.log(generated.regular);
6 | console.log(generated.long);
7 |
8 | var html = '' + type + ' ' + generated.regular + ' ' + generated.long + ' ';
9 |
10 | $('#restable').append(html);
11 | }
12 |
13 | function calcRepeats() {
14 | var code = $("#usercode").val();
15 | var repeats = getRepeats(code);
16 | $("#repeats").html(repeats);
17 | }
18 |
19 | function generateNewRepeat() {
20 | var code = $("#usercode").val();
21 | var repeats = $("#newrepeat").val();
22 | var newCode = getNewCode(code, repeats);
23 | $("#newcode").val(newCode);
24 | }
25 |
26 |
27 | function startGenerateLivolo(){
28 | var remote = $('#remoteId').val();
29 | if(remote == ""){
30 | alert("please select remote id");
31 | return;
32 | }
33 | var btn = $('#liv_btn').val();
34 | var code = generateLivolo(remote, btn);
35 | var html = ''+ '' + 'Livolo' + ' ' +'' + code + ' ';
36 |
37 | $('#livtable').append(html);
38 | }
--------------------------------------------------------------------------------
/pageActions.js:
--------------------------------------------------------------------------------
1 | window.onload = function() {
2 | showRandomCodeTab();
3 | };
4 |
5 | function showRandomCodeTab() {
6 | hideAll();
7 | $('#random-code-section').show();
8 | $('#tab1').addClass('active');
9 | };
10 |
11 |
12 | function showLivoloTab() {
13 | hideAll();
14 | $('#livolo-section').show();
15 | $('#tab2').addClass('active');
16 | };
17 |
18 | function showEnergenieTab() {
19 | hideAll();
20 | $('#energenie-section').show();
21 | generateEnergenie();
22 | $('#tab3').addClass('active');
23 | };
24 |
25 | function showRepeatsTab() {
26 | hideAll();
27 | $('#repeats-section').show();
28 | $('#tab4').addClass('active');
29 | };
30 |
31 | function hideAll() {
32 | $('#repeats-section').hide();
33 | $('#random-code-section').hide();
34 | $('#energenie-section').hide();
35 | $('#livolo-section').hide();
36 | $('#tab1, #tab2, #tab3, #tab4').removeClass('active');
37 | }
--------------------------------------------------------------------------------
/protocol.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimagoltsman/Random-Broadlink-RM-Code-Generator/4d3e4edf8ea249d0a862adf6367df6000d1bf6a9/protocol.png
--------------------------------------------------------------------------------
/rolcode.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Rolling code rev eng test
6 |
7 |
8 |
9 |
10 |
12 |
14 |
16 |
18 |
19 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
Rolling code rev eng
37 |
38 |
39 |
40 |
41 |
42 |
43 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
133 |
134 |
135 |
136 |
--------------------------------------------------------------------------------
/rolcode.js:
--------------------------------------------------------------------------------
1 |
2 | var startHeader = "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c89";
3 |
4 | function startDecode() {
5 |
6 | var b64 = $("#usercode").val();
7 | var hex = base64ToHex(b64)
8 | var hexArr = hex.split(" ");
9 | var strippedBroadlink = hexArr.slice(4);
10 | var normilized = normilizeArray(strippedBroadlink);
11 | var joined = normilized.join("");
12 |
13 | if(joined.indexOf(startHeader) == -1){
14 | throw new Error("cant find start header")
15 | }
16 |
17 | var codeStartIndex = joined.indexOf(startHeader) + startHeader.length;
18 | var code = joined.substr(codeStartIndex);
19 | var singleCode = code.substr(0, 66 * 4 );
20 | var singleSplitted = singleCode.match(/.{1,4}/g);
21 | var bitstream = reverse(singleSplitted.map(toBits).join("")); //reverse because code sent with LSB
22 | var encrypted = bitstream.substr(34)
23 | var fixed = bitstream.substr(0,34)
24 |
25 | // console.log(fixed.length + " " + encrypted.length)
26 |
27 | var rv = fixed.substr(0, 2);
28 | var buttons = fixed.substr(2, 4);
29 | var sn = fixed.substr(4, 28);
30 |
31 | $("#hex").val(hex);
32 | $("#singlehex").val(singleSplitted.join(" "));
33 | $("#fullbits").val(bitstream);
34 | $("#encpart").val(encrypted);
35 | $("#fixedpart").val(fixed);
36 | $("#rv").val(rv);
37 | $("#buttons").val(buttons);
38 | $("#sn").val(sn);
39 |
40 | }
41 |
42 | function toBits(byte) {
43 | if(byte == "0c18"){
44 | return "1";
45 | }
46 | if(byte == "180c"){
47 | return "0";
48 | }
49 |
50 | if(byte.substr(2, 2) == "00"){
51 | return "1";
52 | }
53 |
54 | throw new Error("cant decode " + byte)
55 | }
56 |
57 |
58 | function normilizeArray(arr) {
59 | return arr.map(normilizeByte)
60 | }
61 |
62 |
63 | function normilizeByte(byte) {
64 | if(is0c(byte)){
65 | return "0c";
66 | }else if(is18(byte)){
67 | return "18";
68 | }else if(is89(byte)){
69 | return "89";
70 | }
71 |
72 | else{
73 | return byte;
74 | }
75 | }
76 |
77 |
78 | function is0c(byte){
79 | var is = Array("0c", "0d", "0e", "0f", "0b", "0a", "09");
80 | return (is.indexOf(byte) > -1);
81 | }
82 |
83 | function is18(byte){
84 | var is = Array("19", "1a", "1b", "1c", "18", "17", "16");
85 | return (is.indexOf(byte) > -1);
86 | }
87 |
88 | function is89(byte){
89 | var is = Array("89", "8a", "8b", "8c", "88", "87", "86", "7c", "7d", "7e", "7b", "7a", "79");
90 | return (is.indexOf(byte) > -1);
91 | }
92 |
93 |
94 | function reverse(s){
95 | return s.split("").reverse().join("");
96 | }
97 |
--------------------------------------------------------------------------------