From 90560331debcd685186f19f749bec211d5eb6eee Mon Sep 17 00:00:00 2001 From: MaxineMuster Date: Wed, 21 Jan 2026 14:28:44 +0100 Subject: [PATCH 1/5] Try getting slightly closer to Easy Flash Introducing finding and processing keys by regex pattern matching: Try fixing rl_on_pin / rl_off_pin (for all numbers) removing restriction to channels 0-9 for several roles (e.g. rl_pin, led_pin, bt_pin) Added kpin_pin --- templateParser.js | 184 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 184 insertions(+) diff --git a/templateParser.js b/templateParser.js index bc86d06d..87d3e02a 100644 --- a/templateParser.js +++ b/templateParser.js @@ -8,6 +8,48 @@ function findUserParamKey(js) return js; } + +// test new function to use regexp to find objects +// use e.g. for matching rl1_on, rl3_on ... by /^rl\d+_on/ +function findMatchingKeys(jsonObj, regex) { + const matchingKeys = []; + + // Loop through the keys of the JSON object + for (const key in jsonObj) { + // Check if the key matches the regex + if (regex.test(key)) { + matchingKeys.push(key); + } + } + + return matchingKeys; +} + +function processPins(user_param_key, regexPattern, descTemplate, pinRole) { + const json = user_param_key; // Use the input JSON object + + // Find matching keys + const matches = findMatchingKeys(json, regexPattern); + + // Process each match + matches.forEach(key => { + const match = regexPattern.exec(key); + if (match) { + const number = match[1]; // Captured number from the regex + + // Access the corresponding value + const value = json[key]; + + // Construct the description and script lines + desc += descTemplate.replace("{number}", number).replace("{value}", value) + "\n"; + scr += `backlog setPinRole ${value} ${pinRole}; `; + scr += `setPinChannel ${value} ${number}\n`; + tmpl.pins[value] = `${pinRole};${number}`; + } + }); +} + + function processJSON_UserParamKeyStyle(js,user_param_key) { console.log("Entering processJSON_UserParamKeyStyle"); let tmpl = { @@ -50,7 +92,34 @@ function processJSON_UserParamKeyStyle(js,user_param_key) { desc += "Device seems to be using " + tmpl.board + " module, which is " + tmpl.chip + " chip."+"\n"; } + function processPins(user_param_key, regexPattern, descTemplate, pinRole,nochan=0) { + const json = user_param_key; // Use the input JSON object + + // Find matching keys + const matches = findMatchingKeys(json, regexPattern); + + // Process each match + matches.forEach(key => { + const match = regexPattern.exec(key); + if (match) { + const number = match[1]; // Captured number from the regex + if (number == "") nochan = 1; + // Access the corresponding value + const value = json[key]; + + // Construct the description and script lines + desc += descTemplate.replace("{number}", number).replace("{value}", value) + "\n"; + scr += `backlog setPinRole ${value} ${pinRole}; `; + if (!nochan) scr += `setPinChannel ${value} ${number}\n`; + tmpl.pins[value] = `${pinRole};${number}`; + } + }); +} + let value; +/* +// moved to "regex" search code below + for(let i = 0; i < 10; i++) { let name = "rl"+i+"_pin"; value = user_param_key[name]; @@ -75,6 +144,7 @@ function processJSON_UserParamKeyStyle(js,user_param_key) { tmpl.pins[""+value] = "WifiLED_n;0"; } } + for(let i = 0; i < 10; i++) { let name = "door"+i+"_magt_pin"; value = user_param_key[name]; @@ -115,6 +185,8 @@ function processJSON_UserParamKeyStyle(js,user_param_key) { tmpl.pins[""+value] = "TglChanOnTgl;"+i+""; } } + +*/ value = user_param_key.gate_sensor_pin_pin; if(value != undefined) { desc += "- Door/Gate Sensor on P"+value+"\n"; @@ -248,6 +320,9 @@ function processJSON_UserParamKeyStyle(js,user_param_key) { scr += "setPinChannel "+value+" "+ch+"\n"; tmpl.pins[""+value] = "PWM;"+ch; } + + +/* value = user_param_key.rl_on1_pin; if(value != undefined) { let ch = 1; @@ -264,6 +339,115 @@ function processJSON_UserParamKeyStyle(js,user_param_key) { scr += "setPinChannel "+value+" "+ch+"\n"; tmpl.pins[""+value] = "BridgeREV;"+ch; } + +// first try (like other keys) + + for(let i = 0; i < 10; i++) { + let name = "rl_on"+i+"_pin"; + value = user_param_key[name]; + if(value != undefined) { + desc += "- Bridge Relay On (channel " +i + ") on P"+value+"\n"; + scr += "backlog setPinRole "+value+" Rel"+"; "; + scr += "setPinChannel "+value+" "+i+"\n"; + tmpl.pins[""+value] = "Rel;"+i+""; + } + } + for(let i = 0; i < 10; i++) { + let name = "rl_off"+i+"_pin"; + value = user_param_key[name]; + if(value != undefined) { + desc += "- Bridge Relay Off (channel " +i + ") on P"+value+"\n"; + scr += "backlog setPinRole "+value+" Rel_n"+"; "; + scr += "setPinChannel "+value+" "+i+"\n"; + tmpl.pins[""+value] = "Rel_n;"+i+""; + } + } + +*/ +/* +// more general try with regexp + // regex pattern for 'rl_on_pin' + regexPattern = /^rl_on(\d+)_pin/; + matches = findMatchingKeys(user_param_key, regexPattern); + matches.forEach(key => { + // Extract number using the regex + const match = regexPattern.exec(key); + if (match) { + const number = match[1]; // Captured number from the regex +// console.log(`Key: ${key}, Number: ${number}`); + value = user_param_key[key]; +// console.log(`Value: ${value}`); + desc += "- Bridge Relay On (channel " + number + ") on P" + value + "\n"; + scr += "backlog setPinRole " + value + " Rel" + "; "; + scr += "setPinChannel " + value + " " + number + "\n"; + tmpl.pins["" + value] = "Rel;" + number + ""; + } + }); + + // for 'rl_off_pin' in a similar manner + regexPattern = /^rl_off(\d+)_pin/; + matches = findMatchingKeys(user_param_key, regexPattern); + matches.forEach(key => { + const match = regexPattern.exec(key); + if (match) { + const number = match[1]; // Extract number +// console.log(`Key: ${key}, Number: ${number}`); + value = user_param_key[key]; +// console.log(`Value: ${value}`); + desc += "- Bridge Relay Off (channel " + number + ") on P" + value + "\n"; + scr += "backlog setPinRole " + value + " Off" + "; "; + scr += "setPinChannel " + value + " " + number + "\n"; + tmpl.pins["" + value] = "Rel_n;" + number + ""; + } + }); +*/ + // for 'rl_on_pin' + Pattern = /^rl_on(\d+)_pin/; + DescTemplate = "- Bridge Relay On (channel {number}) on P{value}"; + processPins(user_param_key, Pattern, DescTemplate, "Rel"); + + // for 'rl_off_pin' + Pattern = /^rl_off(\d+)_pin/; + DescTemplate = "- Bridge Relay Off (channel {number}) on P{value}"; + processPins(user_param_key, Pattern, DescTemplate, "Rel_n"); + + // for 'rl_pin' + Pattern = /^rl(\d+)_pin/; + DescTemplate = "- Relay (channel {number}) on P{value}"; + processPins(user_param_key, Pattern, DescTemplate, "Rel"); + + // for 'led_pin' + Pattern = /^led(\d+)_pin/; + DescTemplate = "- LED (channel {number}) on P{value}"; + processPins(user_param_key, Pattern, DescTemplate, "LED"); + + // for 'door_magt_pin' + Pattern = /^door(\d+)_magt_pin/; + DescTemplate = "- Door Sensor (channel {number}) on P{value}"; + processPins(user_param_key, Pattern, DescTemplate, "dInput"); + + // for 'netled[]_pin' + Pattern = /^netled(\d*)_pin/; + DescTemplate = "- WiFi LED on P{value}"; + // WiFi LED has no channel, so use nochan=1 (last argument) + processPins(user_param_key, Pattern, DescTemplate, "WifiLED_n", 1); + + // for 'bt_pin' + Pattern = /^bt(\d+)_pin/; + DescTemplate = "- Button (channel {number}) on P{value}"; + processPins(user_param_key, Pattern, DescTemplate, "Btn"); + + // for 'onoff_magt_pin' + Pattern = /^onoff(\d+)/; + DescTemplate = "- TglChannelToggle (channel {number}) on P{value}"; + processPins(user_param_key, Pattern, DescTemplate, "TglChanOnTgl"); + + // for 'kpin_pin' + Pattern = /^k(\d+)pin_pin/; + DescTemplate = "- Button (channel {number}) on P{value}"; + processPins(user_param_key, Pattern, DescTemplate, "Btn"); + + // LED if(user_param_key.iicscl != undefined) { From f803f3af19f1f98a1101180d546361eecdb70742 Mon Sep 17 00:00:00 2001 From: MaxineMuster Date: Wed, 21 Jan 2026 14:44:25 +0100 Subject: [PATCH 2/5] Fix old function still present --- templateParser.js | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/templateParser.js b/templateParser.js index 87d3e02a..9c3b89d4 100644 --- a/templateParser.js +++ b/templateParser.js @@ -25,31 +25,6 @@ function findMatchingKeys(jsonObj, regex) { return matchingKeys; } -function processPins(user_param_key, regexPattern, descTemplate, pinRole) { - const json = user_param_key; // Use the input JSON object - - // Find matching keys - const matches = findMatchingKeys(json, regexPattern); - - // Process each match - matches.forEach(key => { - const match = regexPattern.exec(key); - if (match) { - const number = match[1]; // Captured number from the regex - - // Access the corresponding value - const value = json[key]; - - // Construct the description and script lines - desc += descTemplate.replace("{number}", number).replace("{value}", value) + "\n"; - scr += `backlog setPinRole ${value} ${pinRole}; `; - scr += `setPinChannel ${value} ${number}\n`; - tmpl.pins[value] = `${pinRole};${number}`; - } - }); -} - - function processJSON_UserParamKeyStyle(js,user_param_key) { console.log("Entering processJSON_UserParamKeyStyle"); let tmpl = { From 999d738c13af0db94b4ef4980737f97172a3d842 Mon Sep 17 00:00:00 2001 From: MaxineMuster Date: Tue, 27 Jan 2026 16:51:51 +0100 Subject: [PATCH 3/5] Rework of templateParser.js Try fixing import.vue ignoring Web-App source but always loading from openbekeniot.github.io. --- templateParser.js | 1140 ++++++++++++++++++++++----------------------- vue/import.vue | 14 +- 2 files changed, 557 insertions(+), 597 deletions(-) diff --git a/templateParser.js b/templateParser.js index 9c3b89d4..164d7319 100644 --- a/templateParser.js +++ b/templateParser.js @@ -1,594 +1,546 @@ - -function findUserParamKey(js) -{ - if(js.user_param_key != undefined) - return js.user_param_key; - if(js.device_configuration != undefined) - return js.device_configuration; - - return js; -} - -// test new function to use regexp to find objects -// use e.g. for matching rl1_on, rl3_on ... by /^rl\d+_on/ -function findMatchingKeys(jsonObj, regex) { - const matchingKeys = []; - - // Loop through the keys of the JSON object - for (const key in jsonObj) { - // Check if the key matches the regex - if (regex.test(key)) { - matchingKeys.push(key); - } - } - - return matchingKeys; -} - -function processJSON_UserParamKeyStyle(js,user_param_key) { - console.log("Entering processJSON_UserParamKeyStyle"); - let tmpl = { - vendor: "Tuya", - bDetailed: "0", - name: "TODO", - model: "TODO", - chip: "BK7231T", - board: "TODO", - keywords: [], - pins:{}, - command: "", - image: "https://obrazki.elektroda.pl/YOUR_IMAGE.jpg", - wiki: "https://www.elektroda.com/rtvforum/topic_YOUR_TOPIC.html" - }; - - let desc = ""; - let scr = ""; - if(js.name!=undefined){ - tmpl.name = js.name; - desc += "Device name seems to be " + js.name +"\n"; - } - if(js.manufacturer!=undefined){ - tmpl.vendor = js.manufacturer; - desc += "Device manufacturer seems to be " + js.manufacturer +"\n"; - } - if(js.module != undefined) - { - tmpl.board = js.module; - if(tmpl.board[0] == 'C') { - tmpl.chip = "BK7231N"; - } - if(tmpl.board[0] == 'T') { - // T34 - tmpl.chip = "BK7231N"; - } - if(tmpl.board[0] == 'W') { - tmpl.chip = "BK7231T"; - } - desc += "Device seems to be using " + tmpl.board + " module, which is " + tmpl.chip + " chip."+"\n"; - } - - function processPins(user_param_key, regexPattern, descTemplate, pinRole,nochan=0) { - const json = user_param_key; // Use the input JSON object - - // Find matching keys - const matches = findMatchingKeys(json, regexPattern); - - // Process each match - matches.forEach(key => { - const match = regexPattern.exec(key); - if (match) { - const number = match[1]; // Captured number from the regex - if (number == "") nochan = 1; - // Access the corresponding value - const value = json[key]; - - // Construct the description and script lines - desc += descTemplate.replace("{number}", number).replace("{value}", value) + "\n"; - scr += `backlog setPinRole ${value} ${pinRole}; `; - if (!nochan) scr += `setPinChannel ${value} ${number}\n`; - tmpl.pins[value] = `${pinRole};${number}`; - } - }); -} - - let value; -/* -// moved to "regex" search code below - - for(let i = 0; i < 10; i++) { - let name = "rl"+i+"_pin"; - value = user_param_key[name]; - if(value != undefined) { - desc += "- Relay (channel " +i + ") on P"+value+"\n"; - scr += "backlog setPinRole "+value+" Rel"+"; "; - scr += "setPinChannel "+value+" "+i+"\n"; - tmpl.pins[""+value] = "Rel;"+i+""; - } - } - for(let i = -1; i < 10; i++) { - let name; - name = "netled"; - if(i != -1) { - name += i; - } - name += "_pin"; - value = user_param_key[name]; - if(value != undefined) { - desc += "- WiFi LED on P"+value+"\n"; - scr += "setPinRole "+value+" WifiLED_n"+"\n"; - tmpl.pins[""+value] = "WifiLED_n;0"; - } - } - - for(let i = 0; i < 10; i++) { - let name = "door"+i+"_magt_pin"; - value = user_param_key[name]; - if(value != undefined) { - desc += "- Door Sensor (channel " +i + ") on P"+value+"\n"; - scr += "backlog setPinRole "+value+" dInput"+"; "; - scr += "setPinChannel "+value+" "+i+"\n"; - tmpl.pins[""+value] = "dInput;"+i+""; - } - } - for(let i = 0; i < 10; i++) { - let name = "led"+i+"_pin"; - value = user_param_key[name]; - if(value != undefined) { - desc += "- LED (channel " +i + ") on P"+value+"\n"; - scr += "backlog setPinRole "+value+" LED"+"; "; - scr += "setPinChannel "+value+" "+i+"\n"; - tmpl.pins[""+value] = "LED;"+i+""; - } - } - for(let i = 0; i < 10; i++) { - let name = "bt"+i+"_pin"; - value = user_param_key[name]; - if(value != undefined) { - desc += "- Button (channel " +i + ") on P"+value+"\n"; - scr += "backlog setPinRole "+value+" Btn"+"; "; - scr += "setPinChannel "+value+" "+i+"\n"; - tmpl.pins[""+value] = "Btn;"+i+""; - } - } - for(let i = 0; i < 10; i++) { - let name = "onoff"+i+""; - value = user_param_key[name]; - if(value != undefined) { - desc += "- TglChannelToggle (channel " +i + ") on P"+value+"\n"; - scr += "backlog setPinRole "+value+" TglChanOnTgl"+"; "; - scr += "setPinChannel "+value+" "+i+"\n"; - tmpl.pins[""+value] = "TglChanOnTgl;"+i+""; - } - } - -*/ - value = user_param_key.gate_sensor_pin_pin; - if(value != undefined) { - desc += "- Door/Gate Sensor on P"+value+"\n"; - scr += "setPinRole "+value+" dInput"+"\n"; - tmpl.pins[""+value] = "dInput;0"; - } - value = user_param_key.ele_pin; - if(value != undefined) { - desc += "- BL0937 ELE on P"+value+"\n"; - scr += "setPinRole "+value+" BL0937CF"+"\n"; - tmpl.pins[""+value] = "BL0937CF;0"; - } - value = user_param_key.vi_pin; - if(value != undefined) { - desc += "- BL0937 VI on P"+value+"\n"; - scr += "setPinRole "+value+" BL0937CF1"+"\n"; - tmpl.pins[""+value] = "BL0937CF1;0"; - } - value = user_param_key.sel_pin_pin; - if(value != undefined) { - desc += "- BL0937 SEL on P"+value+"\n"; - scr += "setPinRole "+value+" BL0937SEL"+"\n"; - tmpl.pins[""+value] = "BL0937SEL;0"; - } - value = user_param_key.wfst_pin; - if(value != undefined) { - desc += "- WiFi LED on P"+value+"\n"; - scr += "backlog setPinRole "+value+" WifiLED_n"+"; "; - tmpl.pins[""+value] = "WifiLED_n;0"; - } - value = user_param_key.infrr; - if(value != undefined) { - desc += "- IR Receiver on P"+value+"\n"; - scr += "backlog setPinRole "+value+" IRRecv"+"; "; - tmpl.pins[""+value] = "IRRecv;0"; - } - value = user_param_key.infre; - if(value != undefined) { - desc += "- IR Sender on P"+value+"\n"; - scr += "backlog setPinRole "+value+" IRSend"+"; "; - tmpl.pins[""+value] = "IRSend;0"; - } - value = user_param_key.remote_io; - if(value != undefined) { - desc += "- RF Pin on P"+value+"\n"; - scr += "backlog setPinRole "+value+" RCRecv"+"; "; - tmpl.pins[""+value] = "RCRecv;0"; - } - value = user_param_key.r_pin; - if(value != undefined) { - let ch = 1; - desc += "- LED Red (Channel 1) on P"+value+"\n"; - scr += "backlog setPinRole "+value+" PWM"+"; "; - scr += "setPinChannel "+value+" "+ch+"\n"; - tmpl.pins[""+value] = "PWM;"+ch; - } - value = user_param_key.g_pin; - if(value != undefined) { - let ch = 2; - desc += "- LED Green (Channel 2) on P"+value+"\n"; - scr += "backlog setPinRole "+value+" PWM"+"; "; - scr += "setPinChannel "+value+" "+ch+"\n"; - tmpl.pins[""+value] = "PWM;"+ch; - } - value = user_param_key.b_pin; - if(value != undefined) { - let ch = 3; - desc += "- LED Blue (Channel 3) on P"+value+"\n"; - scr += "backlog setPinRole "+value+" PWM"+"; "; - scr += "setPinChannel "+value+" "+ch+"\n"; - tmpl.pins[""+value] = "PWM;"+ch; - } - value = user_param_key.c_pin; - if(value != undefined) { - let ch = 4; - desc += "- LED Cool (Channel 4) on P"+value+"\n"; - scr += "backlog setPinRole "+value+" PWM"+"; "; - scr += "setPinChannel "+value+" "+ch+"\n"; - tmpl.pins[""+value] = "PWM;"+ch; - } - value = user_param_key.w_pin; - if(value != undefined) { - let ch = 5; - desc += "- LED Warm (Channel 5) on P"+value+"\n"; - scr += "backlog setPinRole "+value+" PWM"+"; "; - scr += "setPinChannel "+value+" "+ch+"\n"; - tmpl.pins[""+value] = "PWM;"+ch; - } - value = user_param_key.ctrl_pin; - if(value != undefined) { - desc += "- Control Pin (TODO) on P"+value+"\n"; - scr += "// TODO: ctrl on "+value+""+"\n"; - } - value = user_param_key.total_bt_pin; - if(value != undefined) { - desc += "- Pair/Toggle All Pin on P"+value+"\n"; - scr += "setPinRole "+value+" Btn_Tgl_All"+"\n"; - tmpl.pins[""+value] = "Btn_Tgl_All;0"; - } - value = user_param_key.reset_pin; - if(value != undefined) { - desc += "- Pair/Reset All Pin on P"+value+"\n"; - scr += "setPinRole "+value+" Btn"+"\n"; - tmpl.pins[""+value] = "Btn;0"; - } - value = user_param_key.key_pin; - if(value != undefined) { - desc += "- Pair/Toggle All Pin on P"+value+"\n"; - scr += "setPinRole "+value+" Btn_Tgl_All"+"\n"; - tmpl.pins[""+value] = "Btn_Tgl_All;0"; - } - value = user_param_key.mic; - if(value != undefined) { - desc += "- Microphone (ADC?) Pin on P"+value+"\n"; - scr += "setPinRole "+value+" ADC"+"\n"; - tmpl.pins[""+value] = "ADC;0"; - } - value = user_param_key.firstpin_pin; - if(value != undefined) { - let ch = 1; - desc += "- PWM (?) on P"+value+"\n"; - scr += "backlog setPinRole "+value+" PWM"+"; "; - scr += "setPinChannel "+value+" "+ch+"\n"; - tmpl.pins[""+value] = "PWM;"+ch; - } - value = user_param_key.secpin_pin; - if(value != undefined) { - let ch = 2; - desc += "- PWM (?) on P"+value+"\n"; - scr += "backlog setPinRole "+value+" PWM"+"; "; - scr += "setPinChannel "+value+" "+ch+"\n"; - tmpl.pins[""+value] = "PWM;"+ch; - } - - -/* - value = user_param_key.rl_on1_pin; - if(value != undefined) { - let ch = 1; - desc += "- Bridge Relay On on P"+value+"\n"; - scr += "backlog setPinRole "+value+" BridgeFWD"+"; "; - scr += "setPinChannel "+value+" "+ch+"\n"; - tmpl.pins[""+value] = "BridgeFWD;"+ch; - } - value = user_param_key.rl_off1_pin; - if(value != undefined) { - let ch = 1; - desc += "- Bridge Relay Off on P"+value+"\n"; - scr += "backlog setPinRole "+value+" BridgeREV"+"; "; - scr += "setPinChannel "+value+" "+ch+"\n"; - tmpl.pins[""+value] = "BridgeREV;"+ch; - } - -// first try (like other keys) - - for(let i = 0; i < 10; i++) { - let name = "rl_on"+i+"_pin"; - value = user_param_key[name]; - if(value != undefined) { - desc += "- Bridge Relay On (channel " +i + ") on P"+value+"\n"; - scr += "backlog setPinRole "+value+" Rel"+"; "; - scr += "setPinChannel "+value+" "+i+"\n"; - tmpl.pins[""+value] = "Rel;"+i+""; - } - } - for(let i = 0; i < 10; i++) { - let name = "rl_off"+i+"_pin"; - value = user_param_key[name]; - if(value != undefined) { - desc += "- Bridge Relay Off (channel " +i + ") on P"+value+"\n"; - scr += "backlog setPinRole "+value+" Rel_n"+"; "; - scr += "setPinChannel "+value+" "+i+"\n"; - tmpl.pins[""+value] = "Rel_n;"+i+""; - } - } - -*/ -/* -// more general try with regexp - // regex pattern for 'rl_on_pin' - regexPattern = /^rl_on(\d+)_pin/; - matches = findMatchingKeys(user_param_key, regexPattern); - matches.forEach(key => { - // Extract number using the regex - const match = regexPattern.exec(key); - if (match) { - const number = match[1]; // Captured number from the regex -// console.log(`Key: ${key}, Number: ${number}`); - value = user_param_key[key]; -// console.log(`Value: ${value}`); - desc += "- Bridge Relay On (channel " + number + ") on P" + value + "\n"; - scr += "backlog setPinRole " + value + " Rel" + "; "; - scr += "setPinChannel " + value + " " + number + "\n"; - tmpl.pins["" + value] = "Rel;" + number + ""; - } - }); - - // for 'rl_off_pin' in a similar manner - regexPattern = /^rl_off(\d+)_pin/; - matches = findMatchingKeys(user_param_key, regexPattern); - matches.forEach(key => { - const match = regexPattern.exec(key); - if (match) { - const number = match[1]; // Extract number -// console.log(`Key: ${key}, Number: ${number}`); - value = user_param_key[key]; -// console.log(`Value: ${value}`); - desc += "- Bridge Relay Off (channel " + number + ") on P" + value + "\n"; - scr += "backlog setPinRole " + value + " Off" + "; "; - scr += "setPinChannel " + value + " " + number + "\n"; - tmpl.pins["" + value] = "Rel_n;" + number + ""; - } - }); -*/ - // for 'rl_on_pin' - Pattern = /^rl_on(\d+)_pin/; - DescTemplate = "- Bridge Relay On (channel {number}) on P{value}"; - processPins(user_param_key, Pattern, DescTemplate, "Rel"); - - // for 'rl_off_pin' - Pattern = /^rl_off(\d+)_pin/; - DescTemplate = "- Bridge Relay Off (channel {number}) on P{value}"; - processPins(user_param_key, Pattern, DescTemplate, "Rel_n"); - - // for 'rl_pin' - Pattern = /^rl(\d+)_pin/; - DescTemplate = "- Relay (channel {number}) on P{value}"; - processPins(user_param_key, Pattern, DescTemplate, "Rel"); - - // for 'led_pin' - Pattern = /^led(\d+)_pin/; - DescTemplate = "- LED (channel {number}) on P{value}"; - processPins(user_param_key, Pattern, DescTemplate, "LED"); - - // for 'door_magt_pin' - Pattern = /^door(\d+)_magt_pin/; - DescTemplate = "- Door Sensor (channel {number}) on P{value}"; - processPins(user_param_key, Pattern, DescTemplate, "dInput"); - - // for 'netled[]_pin' - Pattern = /^netled(\d*)_pin/; - DescTemplate = "- WiFi LED on P{value}"; - // WiFi LED has no channel, so use nochan=1 (last argument) - processPins(user_param_key, Pattern, DescTemplate, "WifiLED_n", 1); - - // for 'bt_pin' - Pattern = /^bt(\d+)_pin/; - DescTemplate = "- Button (channel {number}) on P{value}"; - processPins(user_param_key, Pattern, DescTemplate, "Btn"); - - // for 'onoff_magt_pin' - Pattern = /^onoff(\d+)/; - DescTemplate = "- TglChannelToggle (channel {number}) on P{value}"; - processPins(user_param_key, Pattern, DescTemplate, "TglChanOnTgl"); - - // for 'kpin_pin' - Pattern = /^k(\d+)pin_pin/; - DescTemplate = "- Button (channel {number}) on P{value}"; - processPins(user_param_key, Pattern, DescTemplate, "Btn"); - - - // LED - if(user_param_key.iicscl != undefined) - { - let map = ""+user_param_key.iicr + " "+user_param_key.iicg + " "+user_param_key.iicb + " "+user_param_key.iicc + " "+user_param_key.iicw; - - let iicscl = user_param_key.iicscl; - let iicsda = user_param_key.iicsda; - let ledType = "Unknown"; - // use current (color/cw) setting - if(user_param_key.ehccur != undefined || user_param_key.wampere != undefined || user_param_key.iicccur != undefined) { - ledType = "SM2135"; - } else if(user_param_key.dccur != undefined) { - ledType = "BP5758D_"; - } else if(user_param_key.cjwcur != undefined) { - ledType = "BP1658CJ_"; - } else if(user_param_key["2235ccur"] != undefined) { - ledType = "SM2235"; - } else if(user_param_key["2335ccur"] != undefined) { - // fallback - ledType = "SM2235"; - } else { - - } - scr += "startDriver "+ledType.replace("_","")+" // so we have led_map available\n"; - let dat_name = ledType+"DAT"; - let clk_name = ledType+"CLK"; - desc += "- "+dat_name+" on P"+iicsda+"\n"; - desc += "- "+clk_name+" on P"+iicscl+"\n"; - desc += "- LED remap is " + map + "\n"; - scr += "setPinRole "+iicsda+" "+dat_name+"\n"; - scr += "setPinRole "+iicscl+" "+clk_name+"\n"; - scr += "LED_Map "+map+" \n"; - tmpl.pins[""+iicsda] = ""+dat_name+";0"; - tmpl.pins[""+iicscl] = ""+clk_name+";0"; - - - } - if(user_param_key["baud"] != undefined) { - let baud = user_param_key["baud"]; - desc += "This device seems to be using UART at "+baud+", maybe it's TuyaMCU or BL0942?\n"; - } - if(user_param_key["buzzer_io"] != undefined) { - let buzzer_pin = user_param_key["buzzer_io"]; - desc += "There is a buzzer on P"+buzzer_pin+"\n"; - } - if(user_param_key["buzzer_pwm"] != undefined) { - let buzzer_f = user_param_key["buzzer_pwm"]; - desc += "Buzzer frequency is "+buzzer_f+"Hz\n"; - } - let ele_rx = user_param_key.ele_rx; - let ele_tx = user_param_key.ele_tx; - if(user_param_key.ele_rx != undefined) { - desc += "- BL0942 (?) RX on P"+ele_rx+"\n"; - desc += "- BL0942 (?) TX on P"+ele_tx+"\n"; - scr += "StartupCommand \"startDriver BL0942\""+"\n"; - } - const res = { - desc: desc, - scr: scr, - tmpl: tmpl - }; - return res; -} -function processJSON_OpenBekenTemplateStyle(tmpl) { - let pins = tmpl.pins; - - let desc = ""; - let scr = ""; - for (const pin in pins) { - pinDesc = pins[pin]; - console.log(`Pin ${pin} is connected to ${pinDesc}.`); - [roleName, channel, channel2] = pinDesc.split(';'); - // remap some old convention - if(roleName == "Button") {roleName = "Btn"; } - if(roleName == "Button_n") {roleName = "Btn_n"; } - if(roleName == "Relay") {roleName = "Rel"; } - if(roleName == "Relay_n") {roleName = "Rel_n"; } - desc += "- P"+pin+" is " + roleName + " on channel " + channel +"\n"; - scr += "backlog setPinRole "+pin+" "+roleName+"; setPinChannel " + pin + " " +channel; - // setPinChannel command can take now third argument, which is optional - if(channel2 != undefined && channel2 != 0) - { - scr += " " +channel2+""; - } - scr += "\n"; - } - if(tmpl.flags != undefined) { - scr += "Flags "+tmpl.flags+"\n"; - desc += "- Flags are set to " + tmpl.flags +"\n"; - } - if(tmpl.command != undefined) { - if(tmpl.command.length > 0) { - scr += "StartupCommand \""+tmpl.command+"\"\n"; - desc += "- StartupCommand is set to " + tmpl.command +"\n"; - } - } - const res = { - desc: desc, - scr: scr, - tmpl: tmpl - }; - return res; -} -function fetchJSONSync(url) { - var xhr = new XMLHttpRequest(); - xhr.open('GET', url, false); // false parameter makes the request synchronous - xhr.send(); - - if (xhr.status === 200) { - return xhr.responseText; - } else { - throw new Error('Failed to fetch JSON: ' + xhr.status); - } - } -function processJSONInternal(txt) { - let js = JSON.parse(txt); - if(js.pins != undefined && js.chip != undefined && js.board != undefined) { - return processJSON_OpenBekenTemplateStyle(js); - } - console.log(js); - let user_param_key = findUserParamKey(js); - console.log(user_param_key); - return processJSON_UserParamKeyStyle(js,user_param_key); -} -function processJSON(txt) { - if (txt.startsWith("http")) { - txt = fetchJSONSync(txt); - } - return processJSONInternal(txt); -} -// Helper to make safe filenames -function sanitizeFilename(name) { - if (!name) name = "unknown"; - name = name.replace(/[<>:"/\\|?* .,&#+-]/g, "_"); - name = name.replace(/_+/g, "_"); - name = name.replace(/^_+|_+$/g, ""); - return name || "unknown"; -} - - - - -function pageNameForDevice(device) { - let start = (device.vendor || "Unknown"); - let sub = (device.model || device.name || "NA"); - let baseName; - if(sub.startsWith(start)) { - baseName = sub; - } else { - baseName = start + "_" + sub; - } - return sanitizeFilename(baseName); -} - - -(function (root, factory) { - if (typeof module !== 'undefined' && module.exports) { - module.exports = factory(); // Node.js - } else { - root.MyModule = factory(); // Browser - } -}(typeof self !== 'undefined' ? self : this, function () { - - return { - processJSON_OpenBekenTemplateStyle, - pageNameForDevice, - sanitizeFilename - }; -})); +/* templateParser.js + Single-table approach where each entry's `search` can be: + - plain string (fast exact key lookup) + - RegExp (regex lookup) + - string in slash form "/.../" (converted to RegExp) + + I2C / LED-driver detection logic integrated. + This version restores the exact informational lines present in the original + templateParser.js (device name, manufacturer, module/chip) so no output is + suppressed compared to the original. +*/ + +function escapeRegExp(s) { + return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); +} + +function findUserParamKey(js) { + if (js.user_param_key !== undefined) return js.user_param_key; + if (js.device_configuration !== undefined) return js.device_configuration; + return js; +} + +function normalizeSearch(spec) { + // spec.search: RegExp | string ("/.../") | plain key + if (spec.search instanceof RegExp) { + const flags = spec.search.flags.replace('g', ''); + return { ...spec, _searchType: 'regex', _regex: new RegExp(spec.search.source, flags) }; + } + if (typeof spec.search === 'string') { + const s = spec.search; + // slash-literal string -> RegExp + if (s.length >= 2 && s[0] === '/' && s[s.length - 1] === '/') { + const body = s.slice(1, -1); + return { ...spec, _searchType: 'regex', _regex: new RegExp(body) }; + } + // plain string -> exact key lookup + return { ...spec, _searchType: 'key', _key: s }; + } + throw new Error('spec.search must be RegExp or string'); +} + +function extractNumberFromMatch(match, spec) { + if (!match) return null; + if (spec && spec.groupName && match.groups && match.groups[spec.groupName] !== undefined) { + const val = match.groups[spec.groupName]; + return val === '' ? null : Number(val); + } + const idx = (spec && typeof spec.group === 'number') ? spec.group : 1; + const raw = match[idx]; + return (raw === '' || raw === undefined) ? null : Number(raw); +} + +function makeSetPinLines(pinId, role, channel, nochan) { + if (nochan) return [`setPinRole ${pinId} ${role}`]; + return [`backlog setPinRole ${pinId} ${role}; setPinChannel ${pinId} ${channel}`]; +} + +/* + PROCESSING_TABLE_RAW + - search: string | RegExp | "/regex/" form + - role: string or null + - desc: template using {number} and {value} + - nochan: boolean (optional) + - channel: fixed channel (optional) + - group / groupName: capture group specification for regex (optional) + - special: "i2c" for the I2C LED/driver handler +*/ + +/* Many keys/patterns below were derived from TuyaConfig.cs (BK7231Flasher) analysis. + Comments mark where entries correspond to matches in that C# file. +*/ +const PROCESSING_TABLE_RAW = [ + // Regex patterns (channel-capturing) + { search: /^rl_on(\d+)_pin$/, role: "Rel", desc: "- Bridge Relay On (channel {number}) on P{value}" }, // C#: ^rl_on\d+_pin$ + { search: /^rl_off(\d+)_pin$/, role: "Rel_n", desc: "- Bridge Relay Off (channel {number}) on P{value}" }, // C#: ^rl_off\d+_pin$ + { search: /^rl(\d+)_pin$/, role: "Rel", desc: "- Relay (channel {number}) on P{value}" }, // C#: ^rl\d+_pin$ + { search: /^led(\d+)_pin$/, role: "LED", desc: "- LED (channel {number}) on P{value}" }, // C#: ^led\d+_pin$ + { search: /^door(\d+)_magt_pin$/, role: "dInput", desc: "- Door Sensor (channel {number}) on P{value}" }, // C#: ^door\d+_magt_pin$ + { search: /^bt(\d+)_pin$/, role: "Btn", desc: "- Button (channel {number}) on P{value}" }, // C#: ^bt\d+_pin$ + { search: /^k(\d+)pin_pin$/, role: "Btn", desc: "- Button (channel {number}) on P{value}" }, // C#: ^k\d+pin_pin$ + { search: /^onoff(\d+)$/, role: "TglChanOnTgl", desc: "- TglChannelToggle (channel {number}) on P{value}" }, // C#: ^onoff\d+$ + + // netled may be netled_pin or netled1_pin etc. use regex form (optional number) + { search: "/^netled(\\d*)_pin$/", role: "WifiLED_n", desc: "- WiFi LED on P{value}", nochan: true }, // C# + + // Exact keys and aliases (straight lookup) discovered in C#: + { search: "gate_sensor_pin_pin", role: "dInput", desc: "- Door/Gate Sensor on P{value}", nochan: true }, // C# + { search: "basic_pin_pin", role: "dInput", desc: "- PIR sensor on P{value}", nochan: true }, // C# + { search: "ele_pin", role: "BL0937CF", desc: "- BL0937 ELE on P{value}", nochan: true }, // C# + { search: "epin", role: "BL0937CF", desc: "- EPIN (alias for ele_pin) on P{value}", nochan: true }, // C# alias + { search: "vi_pin", role: "BL0937CF1", desc: "- BL0937 VI on P{value}", nochan: true }, // C# + { search: "ivpin", role: "BL0937CF1", desc: "- BL0937 VI (ivpin) on P{value}", nochan: true }, // C# alias + { search: "sel_pin_pin", role: "BL0937SEL", desc: "- BL0937 SEL on P{value}", nochan: true }, // C# + { search: "ivcpin", role: "BL0937SEL", desc: "- BL0937 SEL (ivcpin) on P{value}", nochan: true }, // C# alias + { search: "wfst_pin", role: "WifiLED_n", desc: "- WiFi LED on P{value}", nochan: true }, // C# + { search: "wfst", role: "WifiLED_n", desc: "- WiFi LED (wfst) on P{value}", nochan: true }, // C# plain 'wfst' alternative + { search: "infrr", role: "IRRecv", desc: "- IR Receiver on P{value}", nochan: true }, // C# + { search: "infre", role: "IRSend", desc: "- IR Sender on P{value}", nochan: true }, // C# + { search: "remote_io", role: "RCRecv", desc: "- RF Remote on P{value}", nochan: true }, // C# + { search: "r_pin", role: "PWM", desc: "- LED Red (Channel 1) on P{value}", channel: 1 }, // C# used channel 0 in BK code !!! Also next ones keep one below + { search: "g_pin", role: "PWM", desc: "- LED Green (Channel 2) on P{value}", channel: 2 }, // C# + { search: "b_pin", role: "PWM", desc: "- LED Blue (Channel 3) on P{value}", channel: 3 }, // C# + { search: "c_pin", role: "PWM", desc: "- LED Cool (Channel 4) on P{value}", channel: 4 }, // C# + { search: "w_pin", role: "PWM", desc: "- LED Warm (Channel 5) on P{value}", channel: 5 }, // C# + { search: "mic", role: "ADC", desc: "- Microphone (ADC?) Pin on P{value}", nochan: true }, // C# + { search: "micpin", role: "ADC", desc: "- Microphone (micpin) on P{value}", nochan: true }, // C# alias + { search: "ctrl_pin", role: null, desc: "- Control Pin (TODO) on P{value}", nochan: true }, // C# + { search: "total_bt_pin", role: "Btn_Tgl_All", desc: "- Pair/Toggle All Pin on P{value}", nochan: true }, // C# + { search: "reset_pin", role: "Btn", desc: "- Pair/Reset All Pin on P{value}", nochan: true }, // C# + { search: "key_pin", role: "Btn_Tgl_All", desc: "- Pair/Toggle All Pin on P{value}", nochan: true }, // C# + { search: "bt_pin", role: "Btn", desc: "- Button (channel 0) on P{value}", channel: 0 }, // C# + { search: "bt", role: "Btn", desc: "- Button (bt) on P{value}", channel: 0 }, // C# + { search: "rl", role: "Rel", desc: "- Relay (channel 0) on P{value}", channel: 0 }, // C# + { search: "samp_sw_pin", role: "BAT_Relay", desc: "- Battery Relay on P{value}", nochan: true }, // C# + { search: "samp_pin", role: "BAT_ADC", desc: "- Battery ADC on P{value}", nochan: true }, // C# + { search: "i2c_scl_pin", role: "I2C_SCL", desc: "- I2C SCL on P{value}", nochan: true }, // C# + { search: "i2c_sda_pin", role: "I2C_SDA", desc: "- I2C SDA on P{value}", nochan: true }, // C# + { search: "alt_pin_pin", role: "ALT", desc: "- ALT pin on P{value}", nochan: true }, // C# + { search: "one_wire_pin", role: "OneWire", desc: "- OneWire IO pin on P{value}", nochan: true }, // C# + { search: "backlit_io_pin", role: "LED", desc: "- Backlit IO pin on P{value}", nochan: true }, // C# + { search: "max_V", role: null, desc: "- Battery Max Voltage: {value}", nochan: true }, // C# + { search: "min_V", role: null, desc: "- Battery Min Voltage: {value}", nochan: true }, // C# + { search: "pwmhz", role: null, desc: "- PWM Frequency {value}", nochan: true }, // C# + // PIR-related keys (settings, not pins) + { search: "pirsense_pin", role: null, desc: "- PIR Sensitivity {value}", nochan: true }, // C# + { search: "pirlduty", role: null, desc: "- PIR Low Duty {value}", nochan: true }, // C# + { search: "pirfreq", role: null, desc: "- PIR Frequency {value}", nochan: true }, // C# + { search: "pirmduty", role: null, desc: "- PIR High Duty {value}", nochan: true }, // C# + { search: "pirin_pin", role: null, desc: "- PIR Input {value}", nochan: true }, // C# + + // SPI pins (mosi/miso/SCL/CS) + { search: "mosi", role: "SM16703P_DIN", desc: "- SPI MOSI P{value}", nochan: true }, // C# + { search: "miso", role: null, desc: "- SPI MISO P{value}", nochan: true }, // C# + { search: "SCL", role: null, desc: "- SPI SCL P{value}", nochan: true }, // C# + { search: "CS", role: null, desc: "- SPI CS P{value}", nochan: true }, // C# + + // Buzzer related keys (from C# cases) + { search: "buzzer_io", role: null, desc: "- Buzzer Pin (TODO) on P{value}", nochan: true }, // C# + { search: "bz_pin_pin", role: null, desc: "- Buzzer Pin (bz_pin_pin) on P{value}", nochan: true }, // C# alias + { search: "status_led_pin", role: "WifiLED_n", desc: "- Status LED on P{value}", nochan: true }, // C# + + // Baud and related + { search: "baud", role: null, desc: "UART baud {value}", nochan: true }, // C# looked at baud + { search: "baud_cfg", role: null, desc: "baud_cfg present", nochan: true }, // C# references baud_cfg earlier + + // I2C detection trigger - handled by special handler after table processing + { search: "iicscl", role: null, desc: "- I2C SCL (iicscl) on P{value}", nochan: true, special: "i2c" }, // original code used iicscl/iicsda + { search: "iicsda", role: null, desc: "- I2C SDA (iicsda) on P{value}", nochan: true, special: "i2c" } // original code used iicscl/iicsda +]; + +// Normalize once to speed runtime +const PROCESSING_TABLE = PROCESSING_TABLE_RAW.map(normalizeSearch); + +// I2C/LED detection handler (moved from earlier special block) +// It examines many iic... keys to decide ledType and produce structured entries + script/desc +function handleI2cBlock(user_param_key, pinEntries, description, script, tmpl) { + const iicscl = user_param_key.iicscl ?? user_param_key["iicscl"]; + const iicsda = user_param_key.iicsda ?? user_param_key["iicsda"]; + if (iicscl === undefined || iicsda === undefined) return; + + const iicr = user_param_key.iicr ?? user_param_key["iicr"] ?? "-1"; + const iicg = user_param_key.iicg ?? user_param_key["iicg"] ?? "-1"; + const iicb = user_param_key.iicb ?? user_param_key["iicb"] ?? "-1"; + const iicc = user_param_key.iicc ?? user_param_key["iicc"] ?? "-1"; + const iicw = user_param_key.iicw ?? user_param_key["iicw"] ?? "-1"; + + let ledType = "Unknown"; + const iicccur = user_param_key.iicccur ?? ""; + const iicwcur = user_param_key.iicwcur ?? ""; + const campere = user_param_key.campere ?? ""; + const wampere = user_param_key.wampere ?? ""; + const ehccur = user_param_key.ehccur ?? ""; + const ehwcur = user_param_key.ehwcur ?? ""; + const drgbcur = user_param_key.drgbcur ?? ""; + const dwcur = user_param_key.dwcur ?? ""; + const dccur = user_param_key.dccur ?? ""; + const cjwcur = user_param_key.cjwcur ?? ""; + const cjccur = user_param_key.cjccur ?? ""; + const _2235ccur = user_param_key["2235ccur"] ?? ""; + const _2235wcur = user_param_key["2235wcur"] ?? ""; + const _2335ccur = user_param_key["2335ccur"] ?? ""; + const kp58wcur = user_param_key["kp58wcur"] ?? ""; + const kp58ccur = user_param_key["kp58ccur"] ?? ""; + + // Use current (color/cw) settings to decide driver + if (ehccur.length > 0 || wampere.length > 0 || iicccur.length > 0) { + ledType = "SM2135"; + // emit init lines if numeric values present + let rgbcurrent = 1, cwcurrent = 1; + try { + rgbcurrent = ehccur.length > 0 ? Number(ehccur) : (iicccur.length > 0 ? Number(iicccur) : (campere.length > 0 ? Number(campere) : 1)); + cwcurrent = ehwcur.length > 0 ? Number(ehwcur) : (iicwcur.length > 0 ? Number(iicwcur) : (wampere.length > 0 ? Number(wampere) : 1)); + script.push(`SM2135_Current ${rgbcurrent} ${cwcurrent}`); + } catch (ex) { + // ignore numeric parse errors + } + } else if (dccur.length > 0) { + ledType = "BP5758D_"; + try { + const rgbcurrent = drgbcur.length > 0 ? Number(drgbcur) : 1; + const wcurrent = dwcur.length > 0 ? Number(dwcur) : 1; + const ccurrent = dccur.length > 0 ? Number(dccur) : 1; + script.push(`BP5758D_Current ${rgbcurrent} ${Math.max(wcurrent, ccurrent)}`); + } catch { } + } else if (cjwcur.length > 0) { + ledType = "BP1658CJ_"; + try { + const rgbcurrent = cjccur.length > 0 ? Number(cjccur) : 1; + const cwcurrent = cjwcur.length > 0 ? Number(cjwcur) : 1; + script.push(`BP1658CJ_Current ${rgbcurrent} ${cwcurrent}`); + } catch { } + } else if (_2235ccur.length > 0) { + ledType = "SM2235"; + try { + const rgbcurrent = Number(_2235ccur || "1"); + const cwcurrent = Number(_2235wcur || "1"); + script.push(`SM2235_Current ${rgbcurrent} ${cwcurrent}`); + } catch { } + } else if (kp58wcur.length > 0) { + ledType = "KP18058_"; + try { + const rgbcurrent = Number(kp58wcur || "1"); + const cwcurrent = Number(kp58ccur || "1"); + script.push(`KP18058_Current ${rgbcurrent} ${cwcurrent}`); + } catch { } + } else { + // leave Unknown + } + + const dat_name = `${ledType}DAT`; + const clk_name = `${ledType}CLK`; + + description.push(`- ${dat_name} on P${iicsda}`); + description.push(`- ${clk_name} on P${iicscl}`); + + // Build map string; try numeric parse but fallback to original + let map = `${iicr} ${iicg} ${iicb} ${iicc} ${iicw}`; + try { + map = `${Number(iicr)} ${Number(iicg)} ${Number(iicb)} ${Number(iicc)} ${Number(iicw)}`; + script.push(`LED_Map ${map}`); + } catch { + script.push(`LED_Map ${map}`); + } + + // push driver start and setPinRole lines + script.unshift(`startDriver ${ledType.replace("_", "")} // so we have led_map available`); + script.push(`setPinRole ${iicsda} ${dat_name}`); + script.push(`setPinRole ${iicscl} ${clk_name}`); + + // push structured pin entries for iics pins + pinEntries.push({ + key: "iicsda", + value: iicsda, + role: dat_name, + number: 0, + nochan: true, + desc: `- ${dat_name} on P${iicsda}`, + scriptLines: [`setPinRole ${iicsda} ${dat_name}`], + pinId: String(iicsda) + }); + pinEntries.push({ + key: "iicscl", + value: iicscl, + role: clk_name, + number: 0, + nochan: true, + desc: `- ${clk_name} on P${iicscl}`, + scriptLines: [`setPinRole ${iicscl} ${clk_name}`], + pinId: String(iicscl) + }); + + // also record in tmpl.pins for compatibility + tmpl.pins[String(iicsda)] = `${dat_name};0`; + tmpl.pins[String(iicscl)] = `${clk_name};0`; +} + +function processTableEntries(user_param_key, tmpl) { + const pinEntries = []; + const description = []; + const script = []; + + // --- special handling for BL0937SEL_n (if sel_pin_lv == 0) --- + const useBL0937SEL_n = (user_param_key.sel_pin_lv !== undefined && Number(user_param_key.sel_pin_lv) === 0) ? 1 : 0; + + + for (spec of PROCESSING_TABLE) { + if (spec._searchType === 'key') { + // exact lookup + const k = spec._key; + const val = user_param_key[k]; + if (val === undefined) continue; + // if this spec is special i2c trigger, we'll handle later (but still collect desc) + if (spec.special === "i2c") { + // add a small description so it's visible; full handling below + description.push((spec.desc || "").replace("{value}", val)); + // don't add script lines here - i2c block will produce them + // but still add a placeholder entry (so user sees the pin mapped) + pinEntries.push({ key: k, value: val, role: spec.role, number: null, nochan: true, desc: (spec.desc || "").replace("{value}", val), scriptLines: [], pinId: String(val) }); + continue; + } + // --- special handling for BL0937SEL_n (if sel_pin_lv == 0) --- + if (spec.role == "BL0937SEL" && useBL0937SEL_n == 1){ + spec.role = "BL0937SEL_n"; + spec.desc = spec.desc.replace("SEL","SEL_n"); + } + + const channel = (typeof spec.channel === 'number') ? spec.channel : (spec.nochan ? null : 0); + const nochan = !!spec.nochan; + const descLine = (spec.desc || "").replace("{value}", val).replace("{number}", channel === null ? "" : String(channel)); + const scriptLines = spec.role ? makeSetPinLines(val, spec.role, channel || 0, nochan) : (k === "ctrl_pin" ? [`// TODO: ctrl on ${val}`] : []); + const entry = { key: k, value: val, role: spec.role, number: channel, nochan, desc: descLine, scriptLines, pinId: String(val) }; + pinEntries.push(entry); + description.push(descLine); + scriptLines.forEach(l => script.push(l)); + } else if (spec._searchType === 'regex') { + // iterate keys and match regex + for (const k in user_param_key) { + const m = k.match(spec._regex); + if (!m) continue; + const val = user_param_key[k]; + if (val === undefined) continue; + const number = extractNumberFromMatch(m, spec); + const nochan = !!spec.nochan || number === null; + const descLine = (spec.desc || "").replace("{value}", val).replace("{number}", number === null ? "" : String(number)); + const channelForScript = number === null ? 0 : number; + const scriptLines = spec.role ? makeSetPinLines(val, spec.role, channelForScript, nochan) : []; + const pinEntry = { key: k, value: val, role: spec.role, number, nochan, desc: descLine, scriptLines, pinId: String(val) }; + pinEntries.push(pinEntry); + description.push(descLine); + scriptLines.forEach(l => script.push(l)); + } + } + } + + // After processing all specs, run special I2C handler if iicscl/iicsda present + if ((user_param_key.iicscl !== undefined || user_param_key.iicsda !== undefined)) { + handleI2cBlock(user_param_key, pinEntries, description, script, tmpl); + } else { + // Also check "i2c_scl_pin" / "i2c_sda_pin" variants (C# used these names) + if (user_param_key.i2c_scl_pin !== undefined || user_param_key.i2c_sda_pin !== undefined) { + if (user_param_key.i2c_scl_pin !== undefined) user_param_key.iicscl = user_param_key.i2c_scl_pin; + if (user_param_key.i2c_sda_pin !== undefined) user_param_key.iicsda = user_param_key.i2c_sda_pin; + handleI2cBlock(user_param_key, pinEntries, description, script, tmpl); + } + } + + return { pinEntries, description, script }; +} + +function processJSON_UserParamKeyStyle(js, user_param_key) { + const tmpl = { + vendor: "Tuya", + bDetailed: "0", + name: "TODO", + model: "TODO", + chip: "BK7231T", + board: "TODO", + keywords: [], + pins: {}, + command: "", + image: "https://obrazki.elektroda.pl/YOUR_IMAGE.jpg", + wiki: "https://www.elektroda.com/rtvforum/topic_YOUR_TOPIC.html", + flags: js.flags + }; + + // preserve original informational lines from the original templateParser.js + const description = []; + const script = []; + + if (js.name !== undefined) { + tmpl.name = js.name; + // Original code: desc += "Device name seems to be " + js.name +"\n"; + description.push("Device name seems to be " + js.name); + } + if (js.manufacturer !== undefined) { + tmpl.vendor = js.manufacturer; + // Original code: desc += "Device manufacturer seems to be " + js.manufacturer +"\n"; + description.push("Device manufacturer seems to be " + js.manufacturer); + } + if (js.module !== undefined) { + tmpl.board = js.module; + if (tmpl.board[0] === 'C' || tmpl.board[0] === 'T') tmpl.chip = "BK7231N"; + if (tmpl.board[0] === 'W') tmpl.chip = "BK7231T"; + // Original code: desc += "Device seems to be using " + tmpl.board + " module, which is " + tmpl.chip + " chip."+"\n"; + description.push("Device seems to be using " + tmpl.board + " module, which is " + tmpl.chip + " chip."); + } + + // Run table (pass tmpl so i2c handler can modify tmpl.pins) + const { pinEntries, description: descFromTable, script: scriptFromTable } = processTableEntries(user_param_key, tmpl); + + // merge table-generated description/script into initial description/script preserving order: + // original behavior appended pin descriptions after those top lines, so do the same. + descFromTable.forEach(d => description.push(d)); + scriptFromTable.forEach(s => script.push(s)); + + // misc info lines (these were previously appended after pin processing) + if (user_param_key["baud"] !== undefined) { + description.push(`This device seems to be using UART at ${user_param_key["baud"]}, maybe it's TuyaMCU or BL0942?`); + } + if (user_param_key["buzzer_io"] !== undefined) { + description.push(`There is a buzzer on P${user_param_key["buzzer_io"]}`); + } + if (user_param_key["buzzer_pwm"] !== undefined) { + description.push(`Buzzer frequency is ${user_param_key["buzzer_pwm"]}Hz`); + } + if (user_param_key.ele_rx !== undefined) { + description.push(`- BL0942 (?) RX on P${user_param_key.ele_rx}`); + description.push(`- BL0942 (?) TX on P${user_param_key.ele_tx}`); + script.push(`StartupCommand "startDriver BL0942"`); + } + + + // Build tmpl.pins in legacy "role;channel" string format for compatibility + pinEntries.forEach(e => { + tmpl.pins[e.pinId] = `${e.role || "Unknown"};${e.number === null ? 0 : e.number}`; + }); + + return { + tmpl, + pins: pinEntries, + description, + script, + // legacy strings kept for compatibility with callers expecting desc/scr + desc: description.join("\n"), + scr: script.join("\n") + }; +} + +function processJSON_OpenBekenTemplateStyle(tmpl) { + const pinEntries = []; + const description = []; + const script = []; + + for (const pin in tmpl.pins) { + const pinDesc = tmpl.pins[pin]; + const [roleNameRaw, channelRaw, channel2Raw] = pinDesc.split(';'); + let roleName = roleNameRaw; + const channel = channelRaw !== undefined ? Number(channelRaw) : 0; + const channel2 = channel2Raw !== undefined ? Number(channel2Raw) : 0; + + // remap some old convention + if (roleName === "Button") roleName = "Btn"; + if (roleName === "Button_n") roleName = "Btn_n"; + if (roleName === "Relay") roleName = "Rel"; + if (roleName === "Relay_n") roleName = "Rel_n"; + + const descLine = `- P${pin} is ${roleName} on channel ${channel}`; + description.push(descLine); + + let scriptLine = `backlog setPinRole ${pin} ${roleName}; setPinChannel ${pin} ${channel}`; + if (channel2 !== 0 && !Number.isNaN(channel2)) scriptLine += ` ${channel2}`; + script.push(scriptLine); + + pinEntries.push({ + key: `P${pin}`, + value: pin, + role: roleName, + number: channel, + extra: channel2, + nochan: false, + desc: descLine, + scriptLines: [scriptLine], + pinId: String(pin) + }); + } + + if (tmpl.flags !== undefined) { + script.push(`Flags ${tmpl.flags}`); + description.push(`- Flags are set to ${tmpl.flags}`); + } + if (tmpl.command !== undefined && tmpl.command.length > 0) { + script.push(`StartupCommand "${tmpl.command}"`); + description.push(`- StartupCommand is set to ${tmpl.command}`); + } + + return { + tmpl, + pins: pinEntries, + description, + script, + desc: description.join("\n"), + scr: script.join("\n") + }; +} + +function fetchJSONSync(url) { + const xhr = new XMLHttpRequest(); + xhr.open('GET', url, false); // synchronous + xhr.send(); + if (xhr.status === 200) return xhr.responseText; + throw new Error('Failed to fetch JSON: ' + xhr.status); +} + +function processJSONInternal(txt) { + const js = JSON.parse(txt); + if (js.pins !== undefined && js.chip !== undefined && js.board !== undefined) { + return processJSON_OpenBekenTemplateStyle(js); + } + const user_param_key = findUserParamKey(js); + return processJSON_UserParamKeyStyle(js, user_param_key); +} + +function processJSON(txt) { + if (typeof txt === "string" && txt.startsWith("http")) { + txt = fetchJSONSync(txt); + } + return processJSONInternal(txt); +} + +// Helper to make safe filenames +function sanitizeFilename(name) { + if (!name) name = "unknown"; + name = name.replace(/[<>:"/\\|?*\s.,&#+-]/g, "_"); + name = name.replace(/_+/g, "_"); + name = name.replace(/^_+|_+$/g, ""); + return name || "unknown"; +} + +function pageNameForDevice(device) { + const start = (device.vendor || "Unknown"); + const sub = (device.model || device.name || "NA"); + const baseName = (sub.startsWith(start) ? sub : `${start}_${sub}`); + return sanitizeFilename(baseName); +} + +// UMD export wrapper +(function (root, factory) { + if (typeof module !== 'undefined' && module.exports) { + module.exports = factory(); // Node.js + } else { + root.TemplateParser = factory(); // Browser + } +}(typeof self !== 'undefined' ? self : this, function () { + + return { + processJSON_OpenBekenTemplateStyle, + processJSON_UserParamKeyStyle, + processJSON, + processJSONInternal, + pageNameForDevice, + sanitizeFilename, + // Export the raw and normalized table so callers can inspect/extend + PROCESSING_TABLE_RAW, + PROCESSING_TABLE + }; +})); diff --git a/vue/import.vue b/vue/import.vue index 0fcef7d9..194d6778 100644 --- a/vue/import.vue +++ b/vue/import.vue @@ -280,11 +280,18 @@ cryptoScript.async = true; document.head.appendChild(cryptoScript); + // we don't want to load it always from git page! + // if we define another webapp location, ALL content shall be loaded from there! + // so, let's go, find out the source of the initial js file, "startup.js" + // and build base URL of this script URL + const startupscript = Array.from(document.scripts).find(script => script.src.includes("startup.js")); + const baseUrl = startupscript.src.substring(0, startupscript.src.lastIndexOf('/')); + const plugin = document.createElement("script"); plugin.setAttribute( "src", - "https://openbekeniot.github.io/webapp/templateParser.js" - //"../templateParser.js" + //"https://openbekeniot.github.io/webapp/templateParser.js" + `${baseUrl}/templateParser.js` // Now uses actual server we used for initial script ); plugin.async = true; document.head.appendChild(plugin); @@ -292,7 +299,8 @@ const plugin2 = document.createElement("script"); plugin2.setAttribute( "src", - "https://openbekeniot.github.io/webapp/tuyaExporter.js" + //"https://openbekeniot.github.io/webapp/tuyaExporter.js" + `${baseUrl}/tuyaExporter.js` // Now uses actual server we used for initial script ); plugin2.async = true; document.head.appendChild(plugin2); From f94f1bf0aae483d230d0e8f7a0c2a66b9b1197c0 Mon Sep 17 00:00:00 2001 From: maxinemuster Date: Mon, 9 Feb 2026 20:11:19 +0100 Subject: [PATCH 4/5] WIP for new templateParser.js Introduced an imported json file holding all information --- spec/tuya-spec.json | 203 ++++++++++ templateImporter.html | 30 +- templateParser.js | 867 ++++++++++++++++++------------------------ vue/import.vue | 15 + 4 files changed, 621 insertions(+), 494 deletions(-) create mode 100644 spec/tuya-spec.json diff --git a/spec/tuya-spec.json b/spec/tuya-spec.json new file mode 100644 index 00000000..aaac19f9 --- /dev/null +++ b/spec/tuya-spec.json @@ -0,0 +1,203 @@ +{ + "meta": { + "name": "Tuya pin and parameter spec", + "source": "collected from templateParser.js, TuyaConfig.cs (BK7231Flasher) and LibreTiny tuya-pin-config", + "version": "1.0" + }, + "mappings": [ + { "search": "/^rl_on(\\d+)_pin$/", "role": "BridgeFWD", "desc": "- Bridge Relay On (channel {number}) on P{value}" }, + { "search": "/^rl_off(\\d+)_pin$/", "role": "BridgeREV", "desc": "- Bridge Relay Off (channel {number}) on P{value}" }, + { "search": "/^rl(\\d+)_pin$/", "role": "Rel", "desc": "- Relay (channel {number}) on P{value}" }, + { "search": "/^led(\\d+)_pin$/", "role": "LED", "desc": "- LED (channel {number}) on P{value}" }, + { "search": "/^door(\\d+)_magt_pin$/", "role": "dInput", "desc": "- Door Sensor (channel {number}) on P{value}" }, + { "search": "/^bt(\\d+)_pin$/", "role": "Btn", "desc": "- Button (channel {number}) on P{value}" }, + { "search": "/^k(\\d+)pin_pin$/", "role": "Btn", "desc": "- Button (channel {number}) on P{value}" }, + { "search": "/^onoff(\\d+)$/", "role": "TglChanOnTgl", "desc": "- TglChannelToggle (channel {number}) on P{value}" }, + + { "search": "/^netled(\\d*)_pin$/", "role": "WifiLED_n", "desc": "- WiFi LED on P{value}", "nochan": true }, + + { "search": "gate_sensor_pin_pin", "role": "dInput", "desc": "- Door/Gate Sensor on P{value}", "nochan": true }, + { "search": "basic_pin_pin", "role": "dInput", "desc": "- PIR sensor on P{value}", "nochan": true }, + { "search": "ele_pin", "role": "BL0937CF", "desc": "- BL0937 ELE on P{value}", "nochan": true }, + { "search": "epin", "role": "BL0937CF", "desc": "- EPIN (alias for ele_pin) on P{value}", "nochan": true }, + { "search": "vi_pin", "role": "BL0937CF1", "desc": "- BL0937 VI on P{value}", "nochan": true }, + { "search": "ivpin", "role": "BL0937CF1", "desc": "- BL0937 VI (ivpin) on P{value}", "nochan": true }, + { "search": "sel_pin_pin", "role": "BL0937SEL", "desc": "- BL0937 SEL on P{value}", "nochan": true }, + { "search": "ivcpin", "role": "BL0937SEL", "desc": "- BL0937 SEL (ivcpin) on P{value}", "nochan": true }, + + { "search": "wfst_pin", "role": "WifiLED_n", "desc": "- WiFi LED on P{value}", "nochan": true }, + { "search": "wfst", "role": "WifiLED_n", "desc": "- WiFi LED (wfst) on P{value}", "nochan": true }, + { "search": "infrr", "role": "IRRecv", "desc": "- IR Receiver on P{value}", "nochan": true }, + { "search": "infre", "role": "IRSend", "desc": "- IR Sender on P{value}", "nochan": true }, + { "search": "remote_io", "role": "RCRecv", "desc": "- RF Remote on P{value}", "nochan": true }, + + { "search": "r_pin", "role": "PWM", "desc": "- LED Red (Channel 1) on P{value}", "channel": 1 }, + { "search": "g_pin", "role": "PWM", "desc": "- LED Green (Channel 2) on P{value}", "channel": 2 }, + { "search": "b_pin", "role": "PWM", "desc": "- LED Blue (Channel 3) on P{value}", "channel": 3 }, + { "search": "c_pin", "role": "PWM", "desc": "- LED Cool (Channel 4) on P{value}", "channel": 4 }, + { "search": "w_pin", "role": "PWM", "desc": "- LED Warm (Channel 5) on P{value}", "channel": 5 }, + + { "search": "mic", "role": "ADC", "desc": "- Microphone (ADC?) Pin on P{value}", "nochan": true }, + { "search": "micpin", "role": "ADC", "desc": "- Microphone (micpin) on P{value}", "nochan": true }, + + { "search": "ctrl_pin", "role": null, "desc": "- Control Pin (TODO) on P{value}", "nochan": true }, + { "search": "total_bt_pin", "role": "Btn_Tgl_All", "desc": "- Pair/Toggle All Pin on P{value}", "nochan": true }, + { "search": "reset_pin", "role": "Btn", "desc": "- Pair/Reset All Pin on P{value}", "nochan": true }, + { "search": "key_pin", "role": "Btn_Tgl_All", "desc": "- Pair/Toggle All Pin on P{value}", "nochan": true }, + + { "search": "bt_pin", "role": "Btn", "desc": "- Button (channel 0) on P{value}", "channel": 0 }, + { "search": "bt", "role": "Btn", "desc": "- Button (bt) on P{value}", "channel": 0 }, + + { "search": "rl", "role": "Rel", "desc": "- Relay (channel 0) on P{value}", "channel": 0 }, + + { "search": "samp_sw_pin", "role": "BAT_Relay", "desc": "- Battery Relay on P{value}", "nochan": true }, + { "search": "samp_pin", "role": "BAT_ADC", "desc": "- Battery ADC on P{value}", "nochan": true }, + + { "search": "i2c_scl_pin", "role": "I2C_SCL", "desc": "- I2C SCL on P{value}", "nochan": true }, + { "search": "i2c_sda_pin", "role": "I2C_SDA", "desc": "- I2C SDA on P{value}", "nochan": true }, + + { "search": "alt_pin_pin", "role": "ALT", "desc": "- ALT pin on P{value}", "nochan": true }, + { "search": "one_wire_pin", "role": "OneWire", "desc": "- OneWire IO pin on P{value}", "nochan": true }, + { "search": "backlit_io_pin", "role": "LED", "desc": "- Backlit IO pin on P{value}", "nochan": true }, + + { "search": "max_V", "role": "VALUEONLY", "desc": "- Battery Max Voltage: {value}", "nochan": true }, + { "search": "min_V", "role": "VALUEONLY", "desc": "- Battery Min Voltage: {value}", "nochan": true }, + + { "search": "pwmhz", "role": "VALUEONLY", "desc": "- PWM Frequency {value}", "nochan": true }, + + { "search": "pirsense_pin", "role": null, "desc": "- PIR Sensitivity on P{value}", "nochan": true }, + { "search": "pirlduty", "role": "VALUEONLY", "desc": "- PIR Low Duty {value}", "nochan": true }, + { "search": "pirfreq", "role": "VALUEONLY", "desc": "- PIR Frequency {value}", "nochan": true }, + { "search": "pirmduty", "role": "VALUEONLY", "desc": "- PIR High Duty {value}", "nochan": true }, + { "search": "pirin_pin", "role": null, "desc": "- PIR Input on P{value}", "nochan": true }, + + { "search": "mosi", "role": "SM16703P_DIN", "desc": "- SPI MOSI P{value}", "nochan": true }, + { "search": "miso", "role": null, "desc": "- SPI MISO P{value}", "nochan": true }, + { "search": "SCL", "role": null, "desc": "- SPI SCL P{value}", "nochan": true }, + { "search": "CS", "role": null, "desc": "- SPI CS P{value}", "nochan": true }, + + { "search": "buzzer_io", "role": null, "desc": "- Buzzer Pin (TODO) on P{value}", "nochan": true }, + { "search": "bz_pin_pin", "role": null, "desc": "- Buzzer Pin (bz_pin_pin) on P{value}", "nochan": true }, + { "search": "status_led_pin", "role": "WifiLED_n", "desc": "- Status LED on P{value}", "nochan": true }, + + { "search": "baud", "role": "VALUEONLY", "desc": "UART baud {value}", "nochan": true }, + { "search": "baud_cfg", "role": null, "desc": "baud_cfg present", "nochan": true }, + + { "search": "iicscl", "role": null, "desc": "- I2C SCL (iicscl) on P{value}", "nochan": true, "special": "i2c" }, + { "search": "iicsda", "role": null, "desc": "- I2C SDA (iicsda) on P{value}", "nochan": true, "special": "i2c" }, + + { "search": "crc", "role": "VALUEONLY", "desc": "crc = {value}", "nochan": true }, + { "search": "category", "role": "VALUEONLY", "desc": "category = {value}", "nochan": true }, + { "search": "Jsonver", "role": "VALUEONLY", "desc": "Jsonver = {value}", "nochan": true }, + { "search": "jv", "role": "VALUEONLY", "desc": "jv = {value}", "nochan": true }, + + { "search": "netled_lv", "role": "VALUEONLY", "desc": "netled_lv = {value}", "nochan": true }, + { "search": "netled_reuse", "role": "VALUEONLY", "desc": "netled_reuse = {value}", "nochan": true }, + { "search": "net_trig", "role": "VALUEONLY", "desc": "net_trig = {value}", "nochan": true }, + { "search": "net_type", "role": "VALUEONLY", "desc": "net_type = {value}", "nochan": true }, + { "search": "wfct", "role": "VALUEONLY", "desc": "wfct = {value}", "nochan": true }, + + { "search": "reset_lv", "role": "VALUEONLY", "desc": "reset_lv = {value}", "nochan": true }, + { "search": "reset_t", "role": "VALUEONLY", "desc": "reset_t = {value}", "nochan": true }, + + { "search": "cmod", "role": "VALUEONLY", "desc": "cmod = {value}", "nochan": true }, + { "search": "dmod", "role": "VALUEONLY", "desc": "dmod = {value}", "nochan": true }, + { "search": "cwtype", "role": "VALUEONLY", "desc": "cwtype = {value}", "nochan": true }, + { "search": "onoffmode", "role": "VALUEONLY", "desc": "onoffmode = {value}", "nochan": true }, + { "search": "pmemory", "role": "VALUEONLY", "desc": "pmemory = {value}", "nochan": true }, + { "search": "defcolor", "role": "VALUEONLY", "desc": "defcolor = {value}", "nochan": true }, + { "search": "defbright", "role": "VALUEONLY", "desc": "defbright = {value}", "nochan": true }, + { "search": "deftemp", "role": "VALUEONLY", "desc": "deftemp = {value}", "nochan": true }, + { "search": "cwmaxp", "role": "VALUEONLY", "desc": "cwmaxp = {value}", "nochan": true }, + { "search": "brightmin", "role": "VALUEONLY", "desc": "brightmin = {value}", "nochan": true }, + { "search": "brightmax", "role": "VALUEONLY", "desc": "brightmax = {value}", "nochan": true }, + { "search": "colormin", "role": "VALUEONLY", "desc": "colormin = {value}", "nochan": true }, + { "search": "colormax", "role": "VALUEONLY", "desc": "colormax = {value}", "nochan": true }, + { "search": "cwmin", "role": "VALUEONLY", "desc": "cwmin = {value}", "nochan": true }, + { "search": "cwmax", "role": "VALUEONLY", "desc": "cwmax = {value}", "nochan": true }, + { "search": "colormaxp", "role": "VALUEONLY", "desc": "colormaxp = {value}", "nochan": true }, + { "search": "colorpfun", "role": "VALUEONLY", "desc": "colorpfun = {value}", "nochan": true }, + { "search": "brightstep", "role": "VALUEONLY", "desc": "brightstep = {value}", "nochan": true }, + { "search": "bristep", "role": "VALUEONLY", "desc": "bristep = {value}", "nochan": true }, + { "search": "hsvstep", "role": "VALUEONLY", "desc": "hsvstep = {value}", "nochan": true }, + { "search": "rgbt", "role": "VALUEONLY", "desc": "rgbt = {value}", "nochan": true }, + { "search": "title20", "role": "VALUEONLY", "desc": "title20 = {value}", "nochan": true }, + + { "search": "gmr", "role": "VALUEONLY", "desc": "gmr = {value}", "nochan": true }, + { "search": "gmg", "role": "VALUEONLY", "desc": "gmg = {value}", "nochan": true }, + { "search": "gmb", "role": "VALUEONLY", "desc": "gmb = {value}", "nochan": true }, + { "search": "gmkr", "role": "VALUEONLY", "desc": "gmkr = {value}", "nochan": true }, + { "search": "gmkg", "role": "VALUEONLY", "desc": "gmkg = {value}", "nochan": true }, + { "search": "gmkb", "role": "VALUEONLY", "desc": "gmkb = {value}", "nochan": true }, + { "search": "gmwr", "role": "VALUEONLY", "desc": "gmwr = {value}", "nochan": true }, + { "search": "gmwg", "role": "VALUEONLY", "desc": "gmwg = {value}", "nochan": true }, + { "search": "gmwb", "role": "VALUEONLY", "desc": "gmwb = {value}", "nochan": true }, + + { "search": "r_lv", "role": "VALUEONLY", "desc": "r_lv = {value}", "nochan": true }, + { "search": "g_lv", "role": "VALUEONLY", "desc": "g_lv = {value}", "nochan": true }, + { "search": "b_lv", "role": "VALUEONLY", "desc": "b_lv = {value}", "nochan": true }, + { "search": "c_lv", "role": "VALUEONLY", "desc": "c_lv = {value}", "nochan": true }, + { "search": "w_lv", "role": "VALUEONLY", "desc": "w_lv = {value}", "nochan": true }, + + { "search": "dccur", "role": "VALUEONLY", "desc": "dccur = {value}", "nochan": true }, + { "search": "ehccur", "role": "VALUEONLY", "desc": "ehccur = {value}", "nochan": true }, + { "search": "cjccur", "role": "VALUEONLY", "desc": "cjccur = {value}", "nochan": true }, + { "search": "dwcur", "role": "VALUEONLY", "desc": "dwcur = {value}", "nochan": true }, + { "search": "ehwcur", "role": "VALUEONLY", "desc": "ehwcur = {value}", "nochan": true }, + { "search": "cjwcur", "role": "VALUEONLY", "desc": "cjwcur = {value}", "nochan": true }, + { "search": "drgbcur", "role": "VALUEONLY", "desc": "drgbcur = {value}", "nochan": true }, + { "search": "campere", "role": "VALUEONLY", "desc": "campere = {value}", "nochan": true }, + { "search": "wampere", "role": "VALUEONLY", "desc": "wampere = {value}", "nochan": true }, + + { "search": "iicr", "role": "VALUEONLY", "desc": "iicr = {value}", "nochan": true }, + { "search": "iicg", "role": "VALUEONLY", "desc": "iicg = {value}", "nochan": true }, + { "search": "iicb", "role": "VALUEONLY", "desc": "iicb = {value}", "nochan": true }, + { "search": "iicc", "role": "VALUEONLY", "desc": "iicc = {value}", "nochan": true }, + { "search": "iicw", "role": "VALUEONLY", "desc": "iicw = {value}", "nochan": true }, + + { "search": "iicccur", "role": "VALUEONLY", "desc": "iicccur = {value}", "nochan": true }, + { "search": "iicwcur", "role": "VALUEONLY", "desc": "iicwcur = {value}", "nochan": true }, + + { "search": "/^bt(\\d+)_lv$/", "role": null, "desc": "bt{number}_lv = {value}", "group": 1, "nochan": true }, + { "search": "/^bt(\\d+)_type$/", "role": null, "desc": "bt{number}_type = {value}", "group": 1, "nochan": true }, + { "search": "bt_type", "role": "VALUEONLY", "desc": "bt_type = {value}", "nochan": true }, + + { "search": "/^rl(\\d+)_lv$/", "role": null, "desc": "rl{number}_lv = {value}", "group": 1, "nochan": true }, + { "search": "/^rl(\\d+)_type$/", "role": null, "desc": "rl{number}_type = {value}", "group": 1, "nochan": true }, + { "search": "/^rl_on(\\d+)_lv$/", "role": null, "desc": "rl_on{number}_lv = {value}", "group": 1, "nochan": true }, + { "search": "/^rl_off(\\d+)_lv$/", "role": null, "desc": "rl_off{number}_lv = {value}", "group": 1, "nochan": true }, + + { "search": "rl1_dr_type", "role": "VALUEONLY", "desc": "rl1_dr_type = {value}", "nochan": true }, + { "search": "rl_drvtime", "role": "VALUEONLY", "desc": "rl_drvtime = {value}", "nochan": true }, + + { "search": "total_bt_lv", "role": "VALUEONLY", "desc": "total_bt_lv = {value}", "nochan": true }, + + { "search": "ele_fun_en", "role": "VALUEONLY", "desc": "ele_fun_en = {value}", "nochan": true }, + { "search": "chip_type", "role": "VALUEONLY", "desc": "chip_type = {value}", "nochan": true }, + + { "search": "sel_pin_lv", "role": "VALUEONLY", "desc": "sel_pin_lv = {value}", "nochan": true }, + + { "search": "netled1_lv", "role": "VALUEONLY", "desc": "netled1_lv = {value}", "nochan": true }, + { "search": "netled_reuse", "role": "VALUEONLY", "desc": "netled_reuse = {value}", "nochan": true }, + + { "search": "ele_rx", "role": null, "desc": "ele_rx on RX Pin P{value}", "nochan": true }, + { "search": "ele_tx", "role": null, "desc": "ele_tx on TX Pin P{value}", "nochan": true } + ], + "valueMaps": { + "chip_type": { + "0": "BL0937", + "1": "HLW8012", + "2": "HLW8032", + "3": "BL0942", + "4": "BL0942" + }, + "sel_pin_lv": { + "0": "active-low", + "1": "active-high" + }, + "netled_lv": { + "0": "active-low", + "1": "active-high" + } + } +} diff --git a/templateImporter.html b/templateImporter.html index 27a99e87..8bc60424 100644 --- a/templateImporter.html +++ b/templateImporter.html @@ -165,7 +165,33 @@

OpenBeken Configuration Generator - Parse Tuya JSON or Binary

- + + + - \ No newline at end of file + diff --git a/templateParser.js b/templateParser.js index 164d7319..283abe95 100644 --- a/templateParser.js +++ b/templateParser.js @@ -1,546 +1,429 @@ -/* templateParser.js - Single-table approach where each entry's `search` can be: - - plain string (fast exact key lookup) - - RegExp (regex lookup) - - string in slash form "/.../" (converted to RegExp) - - I2C / LED-driver detection logic integrated. - This version restores the exact informational lines present in the original - templateParser.js (device name, manufacturer, module/chip) so no output is - suppressed compared to the original. -*/ - -function escapeRegExp(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); -} -function findUserParamKey(js) { - if (js.user_param_key !== undefined) return js.user_param_key; - if (js.device_configuration !== undefined) return js.device_configuration; - return js; + +const GLOBAL = (typeof globalThis !== 'undefined') ? globalThis + : (typeof window !== 'undefined') ? window + : (typeof global !== 'undefined') ? global : this; + +if (!GLOBAL.TUYA_SPEC) { + // Fail loudly — no fallback behavior + console.error('templateParser: window.TUYA_SPEC not found. Make sure the async bootstrap preloaded spec/tuya-spec.json and injected templateParser.js.'); + throw new Error('templateParser: TUYA_SPEC not found. See console for details.'); } -function normalizeSearch(spec) { - // spec.search: RegExp | string ("/.../") | plain key - if (spec.search instanceof RegExp) { - const flags = spec.search.flags.replace('g', ''); - return { ...spec, _searchType: 'regex', _regex: new RegExp(spec.search.source, flags) }; +// PUll runtime arrays from the spec +const PROCESSING_TABLE_RAW = GLOBAL.TUYA_SPEC.mappings || []; +const VALUE_MAPS = GLOBAL.TUYA_SPEC.valueMaps || {}; + + + + // --------------------------- + // Normalize table entries for runtime + // --------------------------- + function normalizeSearch(spec) { + if (spec._searchType) return spec; + if (spec.search instanceof RegExp) { + spec._searchType = 'regex'; + spec._regex = spec.search; + return spec; + } + if (typeof spec.search === 'string') { + const s = spec.search; + if (s.length >= 2 && s[0] === '/' && s[s.length - 1] === '/') { + const body = s.slice(1, -1); + spec._searchType = 'regex'; + spec._regex = new RegExp(body); + return spec; + } + spec._searchType = 'key'; + spec._key = spec.search; + return spec; + } + throw new Error('spec.search must be RegExp or string'); } - if (typeof spec.search === 'string') { - const s = spec.search; - // slash-literal string -> RegExp - if (s.length >= 2 && s[0] === '/' && s[s.length - 1] === '/') { - const body = s.slice(1, -1); - return { ...spec, _searchType: 'regex', _regex: new RegExp(body) }; + +// const PROCESSING_TABLE = GENERATED_PROCESSING_TABLE_RAW.map(normalizeSearch); + const PROCESSING_TABLE = PROCESSING_TABLE_RAW.map(normalizeSearch); + + // --------------------------- + // Parser core + // --------------------------- + function extractNumberFromMatch(match, spec) { + if (!match) return null; + if (spec && spec.groupName && match.groups && match.groups[spec.groupName] !== undefined) { + const val = match.groups[spec.groupName]; + return val === '' ? null : Number(val); } - // plain string -> exact key lookup - return { ...spec, _searchType: 'key', _key: s }; + const idx = (spec && typeof spec.group === 'number') ? spec.group : 1; + const raw = match[idx]; + return (raw === '' || raw === undefined) ? null : Number(raw); } - throw new Error('spec.search must be RegExp or string'); -} -function extractNumberFromMatch(match, spec) { - if (!match) return null; - if (spec && spec.groupName && match.groups && match.groups[spec.groupName] !== undefined) { - const val = match.groups[spec.groupName]; - return val === '' ? null : Number(val); + function makeSetPinLines(pinId, role, channel, nochan) { + if (role === "VALUEONLY") return []; + if (nochan) return [`setPinRole ${pinId} ${role}`]; + return [`backlog setPinRole ${pinId} ${role}; setPinChannel ${pinId} ${channel}`]; } - const idx = (spec && typeof spec.group === 'number') ? spec.group : 1; - const raw = match[idx]; - return (raw === '' || raw === undefined) ? null : Number(raw); -} -function makeSetPinLines(pinId, role, channel, nochan) { - if (nochan) return [`setPinRole ${pinId} ${role}`]; - return [`backlog setPinRole ${pinId} ${role}; setPinChannel ${pinId} ${channel}`]; -} + function handleI2cBlock(user_param_key, pinEntries, description, script, tmpl) { + const iicscl = user_param_key.iicscl ?? user_param_key["iicscl"]; + const iicsda = user_param_key.iicsda ?? user_param_key["iicsda"]; + if (iicscl === undefined || iicsda === undefined) return; + + const iicr = user_param_key.iicr ?? user_param_key["iicr"] ?? "-1"; + const iicg = user_param_key.iicg ?? user_param_key["iicg"] ?? "-1"; + const iicb = user_param_key.iicb ?? user_param_key["iicb"] ?? "-1"; + const iicc = user_param_key.iicc ?? user_param_key["iicc"] ?? "-1"; + const iicw = user_param_key.iicw ?? user_param_key["iicw"] ?? "-1"; + + let ledType = "Unknown"; + const iicccur = user_param_key.iicccur ?? ""; + const iicwcur = user_param_key.iicwcur ?? ""; + const campere = user_param_key.campere ?? ""; + const wampere = user_param_key.wampere ?? ""; + const ehccur = user_param_key.ehccur ?? ""; + const ehwcur = user_param_key.ehwcur ?? ""; + const drgbcur = user_param_key.drgbcur ?? ""; + const dwcur = user_param_key.dwcur ?? ""; + const dccur = user_param_key.dccur ?? ""; + const cjwcur = user_param_key.cjwcur ?? ""; + const cjccur = user_param_key.cjccur ?? ""; + const _2235ccur = user_param_key["2235ccur"] ?? ""; + const _2235wcur = user_param_key["2235wcur"] ?? ""; + const _2335ccur = user_param_key["2335ccur"] ?? ""; + const kp58wcur = user_param_key["kp58wcur"] ?? ""; + const kp58ccur = user_param_key["kp58ccur"] ?? ""; + + if (ehccur.length > 0 || wampere.length > 0 || iicccur.length > 0) { + ledType = "SM2135"; + let rgbcurrent = 1, cwcurrent = 1; + try { + rgbcurrent = ehccur.length > 0 ? Number(ehccur) : (iicccur.length > 0 ? Number(iicccur) : (campere.length > 0 ? Number(campere) : 1)); + cwcurrent = ehwcur.length > 0 ? Number(ehwcur) : (iicwcur.length > 0 ? Number(iicwcur) : (wampere.length > 0 ? Number(wampere) : 1)); + script.push(`SM2135_Current ${rgbcurrent} ${cwcurrent}`); + } catch (ex) { /* ignore */ } + } else if (dccur.length > 0) { + ledType = "BP5758D_"; + try { + const rgbcurrent = drgbcur.length > 0 ? Number(drgbcur) : 1; + const wcurrent = dwcur.length > 0 ? Number(dwcur) : 1; + const ccurrent = dccur.length > 0 ? Number(dccur) : 1; + script.push(`BP5758D_Current ${rgbcurrent} ${Math.max(wcurrent, ccurrent)}`); + } catch { } + } else if (cjwcur.length > 0) { + ledType = "BP1658CJ_"; + try { + const rgbcurrent = cjccur.length > 0 ? Number(cjccur) : 1; + const cwcurrent = cjwcur.length > 0 ? Number(cjwcur) : 1; + script.push(`BP1658CJ_Current ${rgbcurrent} ${cwcurrent}`); + } catch { } + } else if (_2235ccur.length > 0) { + ledType = "SM2235"; + try { + const rgbcurrent = Number(_2235ccur || "1"); + const cwcurrent = Number(_2235wcur || "1"); + script.push(`SM2235_Current ${rgbcurrent} ${cwcurrent}`); + } catch { } + } else if (kp58wcur.length > 0) { + ledType = "KP18058_"; + try { + const rgbcurrent = Number(kp58wcur || "1"); + const cwcurrent = Number(kp58ccur || "1"); + script.push(`KP18058_Current ${rgbcurrent} ${cwcurrent}`); + } catch { } + } + + const dat_name = `${ledType}DAT`; + const clk_name = `${ledType}CLK`; -/* - PROCESSING_TABLE_RAW - - search: string | RegExp | "/regex/" form - - role: string or null - - desc: template using {number} and {value} - - nochan: boolean (optional) - - channel: fixed channel (optional) - - group / groupName: capture group specification for regex (optional) - - special: "i2c" for the I2C LED/driver handler -*/ - -/* Many keys/patterns below were derived from TuyaConfig.cs (BK7231Flasher) analysis. - Comments mark where entries correspond to matches in that C# file. -*/ -const PROCESSING_TABLE_RAW = [ - // Regex patterns (channel-capturing) - { search: /^rl_on(\d+)_pin$/, role: "Rel", desc: "- Bridge Relay On (channel {number}) on P{value}" }, // C#: ^rl_on\d+_pin$ - { search: /^rl_off(\d+)_pin$/, role: "Rel_n", desc: "- Bridge Relay Off (channel {number}) on P{value}" }, // C#: ^rl_off\d+_pin$ - { search: /^rl(\d+)_pin$/, role: "Rel", desc: "- Relay (channel {number}) on P{value}" }, // C#: ^rl\d+_pin$ - { search: /^led(\d+)_pin$/, role: "LED", desc: "- LED (channel {number}) on P{value}" }, // C#: ^led\d+_pin$ - { search: /^door(\d+)_magt_pin$/, role: "dInput", desc: "- Door Sensor (channel {number}) on P{value}" }, // C#: ^door\d+_magt_pin$ - { search: /^bt(\d+)_pin$/, role: "Btn", desc: "- Button (channel {number}) on P{value}" }, // C#: ^bt\d+_pin$ - { search: /^k(\d+)pin_pin$/, role: "Btn", desc: "- Button (channel {number}) on P{value}" }, // C#: ^k\d+pin_pin$ - { search: /^onoff(\d+)$/, role: "TglChanOnTgl", desc: "- TglChannelToggle (channel {number}) on P{value}" }, // C#: ^onoff\d+$ - - // netled may be netled_pin or netled1_pin etc. use regex form (optional number) - { search: "/^netled(\\d*)_pin$/", role: "WifiLED_n", desc: "- WiFi LED on P{value}", nochan: true }, // C# - - // Exact keys and aliases (straight lookup) discovered in C#: - { search: "gate_sensor_pin_pin", role: "dInput", desc: "- Door/Gate Sensor on P{value}", nochan: true }, // C# - { search: "basic_pin_pin", role: "dInput", desc: "- PIR sensor on P{value}", nochan: true }, // C# - { search: "ele_pin", role: "BL0937CF", desc: "- BL0937 ELE on P{value}", nochan: true }, // C# - { search: "epin", role: "BL0937CF", desc: "- EPIN (alias for ele_pin) on P{value}", nochan: true }, // C# alias - { search: "vi_pin", role: "BL0937CF1", desc: "- BL0937 VI on P{value}", nochan: true }, // C# - { search: "ivpin", role: "BL0937CF1", desc: "- BL0937 VI (ivpin) on P{value}", nochan: true }, // C# alias - { search: "sel_pin_pin", role: "BL0937SEL", desc: "- BL0937 SEL on P{value}", nochan: true }, // C# - { search: "ivcpin", role: "BL0937SEL", desc: "- BL0937 SEL (ivcpin) on P{value}", nochan: true }, // C# alias - { search: "wfst_pin", role: "WifiLED_n", desc: "- WiFi LED on P{value}", nochan: true }, // C# - { search: "wfst", role: "WifiLED_n", desc: "- WiFi LED (wfst) on P{value}", nochan: true }, // C# plain 'wfst' alternative - { search: "infrr", role: "IRRecv", desc: "- IR Receiver on P{value}", nochan: true }, // C# - { search: "infre", role: "IRSend", desc: "- IR Sender on P{value}", nochan: true }, // C# - { search: "remote_io", role: "RCRecv", desc: "- RF Remote on P{value}", nochan: true }, // C# - { search: "r_pin", role: "PWM", desc: "- LED Red (Channel 1) on P{value}", channel: 1 }, // C# used channel 0 in BK code !!! Also next ones keep one below - { search: "g_pin", role: "PWM", desc: "- LED Green (Channel 2) on P{value}", channel: 2 }, // C# - { search: "b_pin", role: "PWM", desc: "- LED Blue (Channel 3) on P{value}", channel: 3 }, // C# - { search: "c_pin", role: "PWM", desc: "- LED Cool (Channel 4) on P{value}", channel: 4 }, // C# - { search: "w_pin", role: "PWM", desc: "- LED Warm (Channel 5) on P{value}", channel: 5 }, // C# - { search: "mic", role: "ADC", desc: "- Microphone (ADC?) Pin on P{value}", nochan: true }, // C# - { search: "micpin", role: "ADC", desc: "- Microphone (micpin) on P{value}", nochan: true }, // C# alias - { search: "ctrl_pin", role: null, desc: "- Control Pin (TODO) on P{value}", nochan: true }, // C# - { search: "total_bt_pin", role: "Btn_Tgl_All", desc: "- Pair/Toggle All Pin on P{value}", nochan: true }, // C# - { search: "reset_pin", role: "Btn", desc: "- Pair/Reset All Pin on P{value}", nochan: true }, // C# - { search: "key_pin", role: "Btn_Tgl_All", desc: "- Pair/Toggle All Pin on P{value}", nochan: true }, // C# - { search: "bt_pin", role: "Btn", desc: "- Button (channel 0) on P{value}", channel: 0 }, // C# - { search: "bt", role: "Btn", desc: "- Button (bt) on P{value}", channel: 0 }, // C# - { search: "rl", role: "Rel", desc: "- Relay (channel 0) on P{value}", channel: 0 }, // C# - { search: "samp_sw_pin", role: "BAT_Relay", desc: "- Battery Relay on P{value}", nochan: true }, // C# - { search: "samp_pin", role: "BAT_ADC", desc: "- Battery ADC on P{value}", nochan: true }, // C# - { search: "i2c_scl_pin", role: "I2C_SCL", desc: "- I2C SCL on P{value}", nochan: true }, // C# - { search: "i2c_sda_pin", role: "I2C_SDA", desc: "- I2C SDA on P{value}", nochan: true }, // C# - { search: "alt_pin_pin", role: "ALT", desc: "- ALT pin on P{value}", nochan: true }, // C# - { search: "one_wire_pin", role: "OneWire", desc: "- OneWire IO pin on P{value}", nochan: true }, // C# - { search: "backlit_io_pin", role: "LED", desc: "- Backlit IO pin on P{value}", nochan: true }, // C# - { search: "max_V", role: null, desc: "- Battery Max Voltage: {value}", nochan: true }, // C# - { search: "min_V", role: null, desc: "- Battery Min Voltage: {value}", nochan: true }, // C# - { search: "pwmhz", role: null, desc: "- PWM Frequency {value}", nochan: true }, // C# - // PIR-related keys (settings, not pins) - { search: "pirsense_pin", role: null, desc: "- PIR Sensitivity {value}", nochan: true }, // C# - { search: "pirlduty", role: null, desc: "- PIR Low Duty {value}", nochan: true }, // C# - { search: "pirfreq", role: null, desc: "- PIR Frequency {value}", nochan: true }, // C# - { search: "pirmduty", role: null, desc: "- PIR High Duty {value}", nochan: true }, // C# - { search: "pirin_pin", role: null, desc: "- PIR Input {value}", nochan: true }, // C# - - // SPI pins (mosi/miso/SCL/CS) - { search: "mosi", role: "SM16703P_DIN", desc: "- SPI MOSI P{value}", nochan: true }, // C# - { search: "miso", role: null, desc: "- SPI MISO P{value}", nochan: true }, // C# - { search: "SCL", role: null, desc: "- SPI SCL P{value}", nochan: true }, // C# - { search: "CS", role: null, desc: "- SPI CS P{value}", nochan: true }, // C# - - // Buzzer related keys (from C# cases) - { search: "buzzer_io", role: null, desc: "- Buzzer Pin (TODO) on P{value}", nochan: true }, // C# - { search: "bz_pin_pin", role: null, desc: "- Buzzer Pin (bz_pin_pin) on P{value}", nochan: true }, // C# alias - { search: "status_led_pin", role: "WifiLED_n", desc: "- Status LED on P{value}", nochan: true }, // C# - - // Baud and related - { search: "baud", role: null, desc: "UART baud {value}", nochan: true }, // C# looked at baud - { search: "baud_cfg", role: null, desc: "baud_cfg present", nochan: true }, // C# references baud_cfg earlier - - // I2C detection trigger - handled by special handler after table processing - { search: "iicscl", role: null, desc: "- I2C SCL (iicscl) on P{value}", nochan: true, special: "i2c" }, // original code used iicscl/iicsda - { search: "iicsda", role: null, desc: "- I2C SDA (iicsda) on P{value}", nochan: true, special: "i2c" } // original code used iicscl/iicsda -]; - -// Normalize once to speed runtime -const PROCESSING_TABLE = PROCESSING_TABLE_RAW.map(normalizeSearch); - -// I2C/LED detection handler (moved from earlier special block) -// It examines many iic... keys to decide ledType and produce structured entries + script/desc -function handleI2cBlock(user_param_key, pinEntries, description, script, tmpl) { - const iicscl = user_param_key.iicscl ?? user_param_key["iicscl"]; - const iicsda = user_param_key.iicsda ?? user_param_key["iicsda"]; - if (iicscl === undefined || iicsda === undefined) return; - - const iicr = user_param_key.iicr ?? user_param_key["iicr"] ?? "-1"; - const iicg = user_param_key.iicg ?? user_param_key["iicg"] ?? "-1"; - const iicb = user_param_key.iicb ?? user_param_key["iicb"] ?? "-1"; - const iicc = user_param_key.iicc ?? user_param_key["iicc"] ?? "-1"; - const iicw = user_param_key.iicw ?? user_param_key["iicw"] ?? "-1"; - - let ledType = "Unknown"; - const iicccur = user_param_key.iicccur ?? ""; - const iicwcur = user_param_key.iicwcur ?? ""; - const campere = user_param_key.campere ?? ""; - const wampere = user_param_key.wampere ?? ""; - const ehccur = user_param_key.ehccur ?? ""; - const ehwcur = user_param_key.ehwcur ?? ""; - const drgbcur = user_param_key.drgbcur ?? ""; - const dwcur = user_param_key.dwcur ?? ""; - const dccur = user_param_key.dccur ?? ""; - const cjwcur = user_param_key.cjwcur ?? ""; - const cjccur = user_param_key.cjccur ?? ""; - const _2235ccur = user_param_key["2235ccur"] ?? ""; - const _2235wcur = user_param_key["2235wcur"] ?? ""; - const _2335ccur = user_param_key["2335ccur"] ?? ""; - const kp58wcur = user_param_key["kp58wcur"] ?? ""; - const kp58ccur = user_param_key["kp58ccur"] ?? ""; - - // Use current (color/cw) settings to decide driver - if (ehccur.length > 0 || wampere.length > 0 || iicccur.length > 0) { - ledType = "SM2135"; - // emit init lines if numeric values present - let rgbcurrent = 1, cwcurrent = 1; + description.push(`- ${dat_name} on P${iicsda}`); + description.push(`- ${clk_name} on P${iicscl}`); + + let map = `${iicr} ${iicg} ${iicb} ${iicc} ${iicw}`; try { - rgbcurrent = ehccur.length > 0 ? Number(ehccur) : (iicccur.length > 0 ? Number(iicccur) : (campere.length > 0 ? Number(campere) : 1)); - cwcurrent = ehwcur.length > 0 ? Number(ehwcur) : (iicwcur.length > 0 ? Number(iicwcur) : (wampere.length > 0 ? Number(wampere) : 1)); - script.push(`SM2135_Current ${rgbcurrent} ${cwcurrent}`); - } catch (ex) { - // ignore numeric parse errors + map = `${Number(iicr)} ${Number(iicg)} ${Number(iicb)} ${Number(iicc)} ${Number(iicw)}`; + script.push(`LED_Map ${map}`); + } catch { + script.push(`LED_Map ${map}`); } - } else if (dccur.length > 0) { - ledType = "BP5758D_"; - try { - const rgbcurrent = drgbcur.length > 0 ? Number(drgbcur) : 1; - const wcurrent = dwcur.length > 0 ? Number(dwcur) : 1; - const ccurrent = dccur.length > 0 ? Number(dccur) : 1; - script.push(`BP5758D_Current ${rgbcurrent} ${Math.max(wcurrent, ccurrent)}`); - } catch { } - } else if (cjwcur.length > 0) { - ledType = "BP1658CJ_"; - try { - const rgbcurrent = cjccur.length > 0 ? Number(cjccur) : 1; - const cwcurrent = cjwcur.length > 0 ? Number(cjwcur) : 1; - script.push(`BP1658CJ_Current ${rgbcurrent} ${cwcurrent}`); - } catch { } - } else if (_2235ccur.length > 0) { - ledType = "SM2235"; - try { - const rgbcurrent = Number(_2235ccur || "1"); - const cwcurrent = Number(_2235wcur || "1"); - script.push(`SM2235_Current ${rgbcurrent} ${cwcurrent}`); - } catch { } - } else if (kp58wcur.length > 0) { - ledType = "KP18058_"; - try { - const rgbcurrent = Number(kp58wcur || "1"); - const cwcurrent = Number(kp58ccur || "1"); - script.push(`KP18058_Current ${rgbcurrent} ${cwcurrent}`); - } catch { } - } else { - // leave Unknown - } - const dat_name = `${ledType}DAT`; - const clk_name = `${ledType}CLK`; + script.unshift(`startDriver ${ledType.replace("_", "")} // so we have led_map available`); + script.push(`setPinRole ${iicsda} ${dat_name}`); + script.push(`setPinRole ${iicscl} ${clk_name}`); - description.push(`- ${dat_name} on P${iicsda}`); - description.push(`- ${clk_name} on P${iicscl}`); + pinEntries.push({ + key: "iicsda", + value: iicsda, + role: dat_name, + number: 0, + nochan: true, + desc: `- ${dat_name} on P${iicsda}`, + scriptLines: [`setPinRole ${iicsda} ${dat_name}`], + pinId: String(iicsda) + }); + pinEntries.push({ + key: "iicscl", + value: iicscl, + role: clk_name, + number: 0, + nochan: true, + desc: `- ${clk_name} on P${iicscl}`, + scriptLines: [`setPinRole ${iicscl} ${clk_name}`], + pinId: String(iicscl) + }); - // Build map string; try numeric parse but fallback to original - let map = `${iicr} ${iicg} ${iicb} ${iicc} ${iicw}`; - try { - map = `${Number(iicr)} ${Number(iicg)} ${Number(iicb)} ${Number(iicc)} ${Number(iicw)}`; - script.push(`LED_Map ${map}`); - } catch { - script.push(`LED_Map ${map}`); + tmpl.pins[String(iicsda)] = `${dat_name};0`; + tmpl.pins[String(iicscl)] = `${clk_name};0`; } - // push driver start and setPinRole lines - script.unshift(`startDriver ${ledType.replace("_", "")} // so we have led_map available`); - script.push(`setPinRole ${iicsda} ${dat_name}`); - script.push(`setPinRole ${iicscl} ${clk_name}`); - - // push structured pin entries for iics pins - pinEntries.push({ - key: "iicsda", - value: iicsda, - role: dat_name, - number: 0, - nochan: true, - desc: `- ${dat_name} on P${iicsda}`, - scriptLines: [`setPinRole ${iicsda} ${dat_name}`], - pinId: String(iicsda) - }); - pinEntries.push({ - key: "iicscl", - value: iicscl, - role: clk_name, - number: 0, - nochan: true, - desc: `- ${clk_name} on P${iicscl}`, - scriptLines: [`setPinRole ${iicscl} ${clk_name}`], - pinId: String(iicscl) - }); - - // also record in tmpl.pins for compatibility - tmpl.pins[String(iicsda)] = `${dat_name};0`; - tmpl.pins[String(iicscl)] = `${clk_name};0`; -} - -function processTableEntries(user_param_key, tmpl) { - const pinEntries = []; - const description = []; - const script = []; - - // --- special handling for BL0937SEL_n (if sel_pin_lv == 0) --- - const useBL0937SEL_n = (user_param_key.sel_pin_lv !== undefined && Number(user_param_key.sel_pin_lv) === 0) ? 1 : 0; - - - for (spec of PROCESSING_TABLE) { - if (spec._searchType === 'key') { - // exact lookup - const k = spec._key; - const val = user_param_key[k]; - if (val === undefined) continue; - // if this spec is special i2c trigger, we'll handle later (but still collect desc) - if (spec.special === "i2c") { - // add a small description so it's visible; full handling below - description.push((spec.desc || "").replace("{value}", val)); - // don't add script lines here - i2c block will produce them - // but still add a placeholder entry (so user sees the pin mapped) - pinEntries.push({ key: k, value: val, role: spec.role, number: null, nochan: true, desc: (spec.desc || "").replace("{value}", val), scriptLines: [], pinId: String(val) }); - continue; - } - // --- special handling for BL0937SEL_n (if sel_pin_lv == 0) --- - if (spec.role == "BL0937SEL" && useBL0937SEL_n == 1){ - spec.role = "BL0937SEL_n"; - spec.desc = spec.desc.replace("SEL","SEL_n"); - } + function processTableEntries(user_param_key, tmpl) { + const pinEntries = []; + const description = []; + const script = []; + const selLvIsZero = (user_param_key.sel_pin_lv !== undefined && Number(user_param_key.sel_pin_lv) === 0); - const channel = (typeof spec.channel === 'number') ? spec.channel : (spec.nochan ? null : 0); - const nochan = !!spec.nochan; - const descLine = (spec.desc || "").replace("{value}", val).replace("{number}", channel === null ? "" : String(channel)); - const scriptLines = spec.role ? makeSetPinLines(val, spec.role, channel || 0, nochan) : (k === "ctrl_pin" ? [`// TODO: ctrl on ${val}`] : []); - const entry = { key: k, value: val, role: spec.role, number: channel, nochan, desc: descLine, scriptLines, pinId: String(val) }; - pinEntries.push(entry); - description.push(descLine); - scriptLines.forEach(l => script.push(l)); - } else if (spec._searchType === 'regex') { - // iterate keys and match regex - for (const k in user_param_key) { - const m = k.match(spec._regex); - if (!m) continue; + for (const spec of PROCESSING_TABLE) { + if (spec._searchType === 'key') { + const k = spec._key; const val = user_param_key[k]; if (val === undefined) continue; - const number = extractNumberFromMatch(m, spec); - const nochan = !!spec.nochan || number === null; - const descLine = (spec.desc || "").replace("{value}", val).replace("{number}", number === null ? "" : String(number)); - const channelForScript = number === null ? 0 : number; - const scriptLines = spec.role ? makeSetPinLines(val, spec.role, channelForScript, nochan) : []; - const pinEntry = { key: k, value: val, role: spec.role, number, nochan, desc: descLine, scriptLines, pinId: String(val) }; - pinEntries.push(pinEntry); + + if (spec.special === "i2c") { + description.push((spec.desc || "").replace("{value}", val)); + pinEntries.push({ key: k, value: val, role: spec.role, number: null, nochan: true, desc: (spec.desc || "").replace("{value}", val), scriptLines: [], pinId: String(val) }); + continue; + } + + let effectiveRole = spec.role; + let effectiveDescTemplate = spec.desc; + + if (spec.role === "BL0937SEL" && selLvIsZero) { + effectiveRole = "BL0937SEL_n"; + if (effectiveDescTemplate) effectiveDescTemplate = effectiveDescTemplate.replace("SEL", "SEL_n"); + } + + const channel = (typeof spec.channel === 'number') ? spec.channel : (spec.nochan ? null : 0); + const nochan = !!spec.nochan; + const descLine = (effectiveDescTemplate || "").replace("{value}", val).replace("{number}", channel === null ? "" : String(channel)); + const scriptLines = (effectiveRole && effectiveRole != "VALUEONLY") ? makeSetPinLines(val, effectiveRole, channel || 0, nochan) : (k === "ctrl_pin" ? [`// TODO: ctrl on ${val}`] : []); + const entry = { key: k, value: val, role: effectiveRole, number: channel, nochan, desc: descLine, scriptLines, pinId: (spec.role === "VALUEONLY" ? null : String(val)) }; + if (spec.role != "VALUEONLY") pinEntries.push(entry); description.push(descLine); + script.push("// "+ descLine); scriptLines.forEach(l => script.push(l)); + } else if (spec._searchType === 'regex') { + for (const k in user_param_key) { + const m = k.match(spec._regex); + if (!m) continue; + const val = user_param_key[k]; + if (val === undefined) continue; + const number = extractNumberFromMatch(m, spec); + const nochan = !!spec.nochan || number === null; + const descLine = (spec.desc || "").replace("{value}", val).replace("{number}", number === null ? "" : String(number)); + const channelForScript = number === null ? 0 : number; + const scriptLines = (spec.role && spec.role != "VALUEONLY") ? makeSetPinLines(val, spec.role, channelForScript, nochan) : []; + const pinEntry = { key: k, value: val, role: spec.role, number, nochan, desc: descLine, scriptLines, pinId: (spec.role === "VALUEONLY" ? "VALUEONLY" : String(val)) }; + if (spec.role != "VALUEONLY") pinEntries.push(pinEntry); + description.push(descLine); + script.push("// "+ descLine); + scriptLines.forEach(l => script.push(l)); + } } } - } - // After processing all specs, run special I2C handler if iicscl/iicsda present - if ((user_param_key.iicscl !== undefined || user_param_key.iicsda !== undefined)) { - handleI2cBlock(user_param_key, pinEntries, description, script, tmpl); - } else { - // Also check "i2c_scl_pin" / "i2c_sda_pin" variants (C# used these names) - if (user_param_key.i2c_scl_pin !== undefined || user_param_key.i2c_sda_pin !== undefined) { + if ((user_param_key.iicscl !== undefined || user_param_key.iicsda !== undefined)) { + handleI2cBlock(user_param_key, pinEntries, description, script, tmpl); + } else if (user_param_key.i2c_scl_pin !== undefined || user_param_key.i2c_sda_pin !== undefined) { if (user_param_key.i2c_scl_pin !== undefined) user_param_key.iicscl = user_param_key.i2c_scl_pin; if (user_param_key.i2c_sda_pin !== undefined) user_param_key.iicsda = user_param_key.i2c_sda_pin; handleI2cBlock(user_param_key, pinEntries, description, script, tmpl); } - } - return { pinEntries, description, script }; -} + // Append human-readable value mappings (VALUE_MAPS) + for (const mapKey in VALUE_MAPS) { + if (!Object.prototype.hasOwnProperty.call(user_param_key, mapKey)) continue; + const raw = user_param_key[mapKey]; + const map = VALUE_MAPS[mapKey]; + const rawStr = String(raw); + let human = map[rawStr]; + if (human === undefined) { + const possible = Object.keys(map).map(v => `${v}=${map[v]}`).join(", "); + human = `Unknown (${rawStr}). Known: ${possible}`; + } + description.push(`- ${mapKey} = ${rawStr} (${human})`); + } -function processJSON_UserParamKeyStyle(js, user_param_key) { - const tmpl = { - vendor: "Tuya", - bDetailed: "0", - name: "TODO", - model: "TODO", - chip: "BK7231T", - board: "TODO", - keywords: [], - pins: {}, - command: "", - image: "https://obrazki.elektroda.pl/YOUR_IMAGE.jpg", - wiki: "https://www.elektroda.com/rtvforum/topic_YOUR_TOPIC.html", - flags: js.flags - }; - - // preserve original informational lines from the original templateParser.js - const description = []; - const script = []; - - if (js.name !== undefined) { - tmpl.name = js.name; - // Original code: desc += "Device name seems to be " + js.name +"\n"; - description.push("Device name seems to be " + js.name); + return { pinEntries, description, script }; } - if (js.manufacturer !== undefined) { - tmpl.vendor = js.manufacturer; - // Original code: desc += "Device manufacturer seems to be " + js.manufacturer +"\n"; - description.push("Device manufacturer seems to be " + js.manufacturer); - } - if (js.module !== undefined) { - tmpl.board = js.module; - if (tmpl.board[0] === 'C' || tmpl.board[0] === 'T') tmpl.chip = "BK7231N"; - if (tmpl.board[0] === 'W') tmpl.chip = "BK7231T"; - // Original code: desc += "Device seems to be using " + tmpl.board + " module, which is " + tmpl.chip + " chip."+"\n"; - description.push("Device seems to be using " + tmpl.board + " module, which is " + tmpl.chip + " chip."); - } - - // Run table (pass tmpl so i2c handler can modify tmpl.pins) - const { pinEntries, description: descFromTable, script: scriptFromTable } = processTableEntries(user_param_key, tmpl); - // merge table-generated description/script into initial description/script preserving order: - // original behavior appended pin descriptions after those top lines, so do the same. - descFromTable.forEach(d => description.push(d)); - scriptFromTable.forEach(s => script.push(s)); + // OpenBeken compatibility handler + function processJSON_OpenBekenTemplateStyle(tmpl) { + const pinEntries = []; + const description = []; + const script = []; + + for (const pin in tmpl.pins) { + const pinDesc = tmpl.pins[pin]; + const [roleNameRaw, channelRaw, channel2Raw] = pinDesc.split(';'); + let roleName = roleNameRaw; + const channel = channelRaw !== undefined ? Number(channelRaw) : 0; + const channel2 = channel2Raw !== undefined ? Number(channel2Raw) : 0; + + if (roleName === "Button") roleName = "Btn"; + if (roleName === "Button_n") roleName = "Btn_n"; + if (roleName === "Relay") roleName = "Rel"; + if (roleName === "Relay_n") roleName = "Rel_n"; + + const descLine = `- P${pin} is ${roleName} on channel ${channel}`; + description.push(descLine); - // misc info lines (these were previously appended after pin processing) - if (user_param_key["baud"] !== undefined) { - description.push(`This device seems to be using UART at ${user_param_key["baud"]}, maybe it's TuyaMCU or BL0942?`); - } - if (user_param_key["buzzer_io"] !== undefined) { - description.push(`There is a buzzer on P${user_param_key["buzzer_io"]}`); - } - if (user_param_key["buzzer_pwm"] !== undefined) { - description.push(`Buzzer frequency is ${user_param_key["buzzer_pwm"]}Hz`); - } - if (user_param_key.ele_rx !== undefined) { - description.push(`- BL0942 (?) RX on P${user_param_key.ele_rx}`); - description.push(`- BL0942 (?) TX on P${user_param_key.ele_tx}`); - script.push(`StartupCommand "startDriver BL0942"`); - } + let scriptLine = `backlog setPinRole ${pin} ${roleName}; setPinChannel ${pin} ${channel}`; + if (channel2 !== 0 && !Number.isNaN(channel2)) scriptLine += ` ${channel2}`; + script.push(scriptLine); + + pinEntries.push({ + key: `P${pin}`, + value: pin, + role: roleName, + number: channel, + extra: channel2, + nochan: false, + desc: descLine, + scriptLines: [scriptLine], + pinId: String(pin) + }); + } + if (tmpl.flags !== undefined) { + script.push(`Flags ${tmpl.flags}`); + description.push(`- Flags are set to ${tmpl.flags}`); + } + if (tmpl.command !== undefined && tmpl.command.length > 0) { + script.push(`StartupCommand "${tmpl.command}"`); + description.push(`- StartupCommand is set to ${tmpl.command}`); + } - // Build tmpl.pins in legacy "role;channel" string format for compatibility - pinEntries.forEach(e => { - tmpl.pins[e.pinId] = `${e.role || "Unknown"};${e.number === null ? 0 : e.number}`; - }); - - return { - tmpl, - pins: pinEntries, - description, - script, - // legacy strings kept for compatibility with callers expecting desc/scr - desc: description.join("\n"), - scr: script.join("\n") - }; -} + return { + tmpl, + pins: pinEntries, + description, + script, + desc: description.join("\n"), + scr: script.join("\n") + }; + } -function processJSON_OpenBekenTemplateStyle(tmpl) { - const pinEntries = []; - const description = []; - const script = []; + function findUserParamKey(js) { + if (js.user_param_key !== undefined) return js.user_param_key; + if (js.device_configuration !== undefined) return js.device_configuration; + return js; + } - for (const pin in tmpl.pins) { - const pinDesc = tmpl.pins[pin]; - const [roleNameRaw, channelRaw, channel2Raw] = pinDesc.split(';'); - let roleName = roleNameRaw; - const channel = channelRaw !== undefined ? Number(channelRaw) : 0; - const channel2 = channel2Raw !== undefined ? Number(channel2Raw) : 0; + function processJSON_UserParamKeyStyle(js, user_param_key) { + const tmpl = { + vendor: "Tuya", + bDetailed: "0", + name: "TODO", + model: "TODO", + chip: "BK7231T", + board: "TODO", + keywords: [], + pins: {}, + command: "", + image: "https://obrazki.elektroda.pl/YOUR_IMAGE.jpg", + wiki: "https://www.elektroda.com/rtvforum/topic_YOUR_TOPIC.html", + flags: js.flags + }; + + const description = []; + const script = []; + + if (js.name !== undefined) { + tmpl.name = js.name; + description.push("Device name seems to be " + js.name); + } + if (js.manufacturer !== undefined) { + tmpl.vendor = js.manufacturer; + description.push("Device manufacturer seems to be " + js.manufacturer); + } + if (js.module !== undefined) { + tmpl.board = js.module; + if (tmpl.board[0] === 'C' || tmpl.board[0] === 'T') tmpl.chip = "BK7231N"; + if (tmpl.board[0] === 'W') tmpl.chip = "BK7231T"; + description.push("Device seems to be using " + tmpl.board + " module, which is " + tmpl.chip + " chip."); + } - // remap some old convention - if (roleName === "Button") roleName = "Btn"; - if (roleName === "Button_n") roleName = "Btn_n"; - if (roleName === "Relay") roleName = "Rel"; - if (roleName === "Relay_n") roleName = "Rel_n"; + const { pinEntries, description: descFromTable, script: scriptFromTable } = processTableEntries(user_param_key, tmpl); - const descLine = `- P${pin} is ${roleName} on channel ${channel}`; - description.push(descLine); + descFromTable.forEach(d => description.push(d)); + scriptFromTable.forEach(s => script.push(s)); - let scriptLine = `backlog setPinRole ${pin} ${roleName}; setPinChannel ${pin} ${channel}`; - if (channel2 !== 0 && !Number.isNaN(channel2)) scriptLine += ` ${channel2}`; - script.push(scriptLine); + if (user_param_key["baud"] !== undefined) { + description.push(`This device seems to be using UART at ${user_param_key["baud"]}, maybe it's TuyaMCU or BL0942?`); + } + if (user_param_key["buzzer_io"] !== undefined) { + description.push(`There is a buzzer on P${user_param_key["buzzer_io"]}`); + } + if (user_param_key["buzzer_pwm"] !== undefined) { + description.push(`Buzzer frequency is ${user_param_key["buzzer_pwm"]}Hz`); + } + if (user_param_key.ele_rx !== undefined) { + description.push(`- BL0942 (?) RX on P${user_param_key.ele_rx}`); + description.push(`- BL0942 (?) TX on P${user_param_key.ele_tx}`); + script.push(`StartupCommand "startDriver BL0942"`); + } - pinEntries.push({ - key: `P${pin}`, - value: pin, - role: roleName, - number: channel, - extra: channel2, - nochan: false, - desc: descLine, - scriptLines: [scriptLine], - pinId: String(pin) + pinEntries.forEach(e => { + tmpl.pins[e.pinId] = `${e.role || "Unknown"};${e.number === null ? 0 : e.number}`; + console.log( `e=${e} - e.pinId=${e.pinId} e.role=${e.role || "Unknown"}; e.number=${e.number === null ? 0 : e.number}`); }); - } - if (tmpl.flags !== undefined) { - script.push(`Flags ${tmpl.flags}`); - description.push(`- Flags are set to ${tmpl.flags}`); - } - if (tmpl.command !== undefined && tmpl.command.length > 0) { - script.push(`StartupCommand "${tmpl.command}"`); - description.push(`- StartupCommand is set to ${tmpl.command}`); + return { + tmpl, + pins: pinEntries, + description, + script, + desc: description.join("\n"), + scr: script.join("\n") + }; } - return { - tmpl, - pins: pinEntries, - description, - script, - desc: description.join("\n"), - scr: script.join("\n") - }; -} + function processJSONInternal(txtOrObj) { + let js; + if (typeof txtOrObj === 'string') js = JSON.parse(txtOrObj); + else js = txtOrObj; -function fetchJSONSync(url) { - const xhr = new XMLHttpRequest(); - xhr.open('GET', url, false); // synchronous - xhr.send(); - if (xhr.status === 200) return xhr.responseText; - throw new Error('Failed to fetch JSON: ' + xhr.status); -} + if (js && js.pins !== undefined && js.chip !== undefined && js.board !== undefined) { + return processJSON_OpenBekenTemplateStyle(js); + } -function processJSONInternal(txt) { - const js = JSON.parse(txt); - if (js.pins !== undefined && js.chip !== undefined && js.board !== undefined) { - return processJSON_OpenBekenTemplateStyle(js); + const user_param_key = findUserParamKey(js); + return processJSON_UserParamKeyStyle(js, user_param_key); } - const user_param_key = findUserParamKey(js); - return processJSON_UserParamKeyStyle(js, user_param_key); -} -function processJSON(txt) { - if (typeof txt === "string" && txt.startsWith("http")) { - txt = fetchJSONSync(txt); + function processJSON(txt) { + if (typeof txt === "string" && txt.startsWith("http")) { + const xhr = new XMLHttpRequest(); + xhr.open('GET', txt, false); + xhr.send(); + if (xhr.status === 200) txt = xhr.responseText; + else throw new Error('Failed to fetch JSON: ' + xhr.status); + } + return processJSONInternal(txt); } - return processJSONInternal(txt); -} - -// Helper to make safe filenames -function sanitizeFilename(name) { - if (!name) name = "unknown"; - name = name.replace(/[<>:"/\\|?*\s.,&#+-]/g, "_"); - name = name.replace(/_+/g, "_"); - name = name.replace(/^_+|_+$/g, ""); - return name || "unknown"; -} -function pageNameForDevice(device) { - const start = (device.vendor || "Unknown"); - const sub = (device.model || device.name || "NA"); - const baseName = (sub.startsWith(start) ? sub : `${start}_${sub}`); - return sanitizeFilename(baseName); -} + function sanitizeFilename(name) { + if (!name) name = "unknown"; + name = name.replace(/[<>:"/\\|?*\s.,&#+-]/g, "_"); + name = name.replace(/_+/g, "_"); + name = name.replace(/^_+|_+$/g, ""); + return name || "unknown"; + } -// UMD export wrapper -(function (root, factory) { - if (typeof module !== 'undefined' && module.exports) { - module.exports = factory(); // Node.js - } else { - root.TemplateParser = factory(); // Browser + function pageNameForDevice(device) { + const start = (device.vendor || "Unknown"); + const sub = (device.model || device.name || "NA"); + const baseName = (sub.startsWith(start) ? sub : `${start}_${sub}`); + return sanitizeFilename(baseName); } -}(typeof self !== 'undefined' ? self : this, function () { - - return { - processJSON_OpenBekenTemplateStyle, - processJSON_UserParamKeyStyle, - processJSON, - processJSONInternal, - pageNameForDevice, - sanitizeFilename, - // Export the raw and normalized table so callers can inspect/extend - PROCESSING_TABLE_RAW, - PROCESSING_TABLE - }; -})); + + diff --git a/vue/import.vue b/vue/import.vue index 194d6778..1372daad 100644 --- a/vue/import.vue +++ b/vue/import.vue @@ -287,6 +287,21 @@ const startupscript = Array.from(document.scripts).find(script => script.src.includes("startup.js")); const baseUrl = startupscript.src.substring(0, startupscript.src.lastIndexOf('/')); + const valuemaps = document.createElement("script"); + valuemaps.setAttribute( + "src", + `${baseUrl}/generated/generated_value_maps.js` + ); + valuemaps.async = true; + document.head.appendChild(valuemaps); + const procTable = document.createElement("script"); + procTable.setAttribute( + "src", + `${baseUrl}/generated/generated_processing_table.js` + ); + procTable.async = true; + document.head.appendChild(procTable); + const plugin = document.createElement("script"); plugin.setAttribute( "src", From 3022ca541a729b1dcd72fdf7f78b45052afc22fa Mon Sep 17 00:00:00 2001 From: maxinemuster Date: Sun, 15 Feb 2026 13:53:24 +0100 Subject: [PATCH 5/5] Add two missing values --- spec/tuya-spec.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spec/tuya-spec.json b/spec/tuya-spec.json index aaac19f9..9d6a4d51 100644 --- a/spec/tuya-spec.json +++ b/spec/tuya-spec.json @@ -27,6 +27,7 @@ { "search": "wfst_pin", "role": "WifiLED_n", "desc": "- WiFi LED on P{value}", "nochan": true }, { "search": "wfst", "role": "WifiLED_n", "desc": "- WiFi LED (wfst) on P{value}", "nochan": true }, + { "search": "irpin", "role": "IRRecv", "desc": "- IR Receiver on P{value}", "nochan": true }, { "search": "infrr", "role": "IRRecv", "desc": "- IR Receiver on P{value}", "nochan": true }, { "search": "infre", "role": "IRSend", "desc": "- IR Sender on P{value}", "nochan": true }, { "search": "remote_io", "role": "RCRecv", "desc": "- RF Remote on P{value}", "nochan": true }, @@ -77,6 +78,7 @@ { "search": "CS", "role": null, "desc": "- SPI CS P{value}", "nochan": true }, { "search": "buzzer_io", "role": null, "desc": "- Buzzer Pin (TODO) on P{value}", "nochan": true }, + { "search": "buzzer_pwm", "role": "VALUEONLY", "desc": "- Buzzer Frequency {value}Hz", "nochan": true }, { "search": "bz_pin_pin", "role": null, "desc": "- Buzzer Pin (bz_pin_pin) on P{value}", "nochan": true }, { "search": "status_led_pin", "role": "WifiLED_n", "desc": "- Status LED on P{value}", "nochan": true },