Concatenate numbers since digits from keyboard

good morning,

thats the end of your flow:

in this case the csv node passes the complete array of 250 entries to your exec node but we have created a "lookup function" especially for filtering the entry you are looking for, so you should position your exec node like that:

now chromium gets only the URL you wanted to look up!

i suggest, that you put a debug with "full msg object" to all of the nodes and have a look at one after an other what they send to understand what they do internally by viewing and retracing the code i wrote for you (in function nodes).
i have to tell you that i am not a programmer! i am a mechanical engineer, but i tried to do no fancy stuff
(not that i could :wink: ) and wrote the code as simple as possible to make it easy to understand, including the comments.

with reference to your post scriptum:
you should keep it. our corrtable is not persistently stored anyway. in the way we do this now it is executed once every start up of NR.
it then stores the table to your context data which you acess in the "replace keyboard input with numbers" node.

it will stay there until the moment you restart NR.
then it would be gone

the inject node triggers the function that sets the corrtable 1second after NR starts up. but only once every start up

when you are interested in storing the corr table permanently, you could read some stuff in the forum about "persistent context".
but unless you are not 100 percent shure that you dont have to edit the correspondance table anymore, i would keep that node. otherwise you'd run into new problems like editing persistent context. thats something for your agenda (not that it is necessary) when your flows work like a charm.
just FYI in my NR I have about 20000 lines of code running and i do not once use persistent context. one does only do that when reading it from the file is faster than generating it new.
when you use a raspi with a microsd you will f*** your sd card up. those solid state storages dont survive, when a "bit" is rewritten more than a few thousand times. but your RAM you can "rewrite" as often as you like it

BTW a website I really like is

https://www.w3schools.com/jsref/default.asp

Everytime I don't know something about js prototype functions and how to use them I look it up there.

1 Like

Hello and thank you for your help and advice: I now display the url well and it's great!

Indeed, I did not think of consulting the site of w3schools, I will dive into it now.

As for the "persistent context", I do plan for later. I'm not there yet. First I need to better understand the mapping table node that you created.

My first job then will be to see how to use the notion of buffer to display, with the page-up and page-down arrows, the previous url and the next one in the list. With that I should have taken the first step.

I then plan to deepen the notions of flow and sub-flow to see how to assign to the keys of my keyboard different actions depending on whether I am in an application or another. Indeed if I am on libreOffice for example, I have to neutralize the flow which allows to display the url, otherwise impossible to use the program !!!

All my thanks to the German mechanic who knew how to advance a french IT project with so much pedagogy !!!!

You're very welcome!
If there's anything else coming up don't hesitate to ask here. Many of the tasks that we have discussed ( lookup from Excel, send msg after timeout, set up context variables, function in function node ) are useful for many different applications, not only for your particular problem. So far your project is a nice one to do some applied problem solving instead of solving academically given ones.
Even maybe there is one of the @moderators reading and can move this thread to a, as a suggestion, to be established "Getting Started with node red " section.

Sorry to come back to you for this project but I have a problem with the correspondence table.
I aim to do a full test:
-1- It works perfectly with the values ​​1 (79), 2 (80) and 3 (81), -2- It provides an erroneous result for:

  • 0 (82), returns "4"
  • For the values ​​4 to 9 (75, 76,77, 71, 72 and 73), the result is "-1".

In addition, I must also integrate the following values ​​corresponding to some other keys on the keyboard: 29, 55, 59 to 68, 71 to 83, 87, 88, 92, 98, 102, 104, 107 and 109.
Could you explain a little to me the code you inserted and how to make this table evolve?
Yours

okay, please set up a complete table with what comes from the keyboard
and what should come out and paste it here

in the correspondance table we did following: we set up a simple for loop while whe thought 0-9 is listed in a row: take look in my post above how to look at the context data:

this:

startindex = 78;
for (i=0;i<10;i++){
    corrtable[i] = String(startindex);
    startindex++;
}

does nothing else than:
set 78 to zero,
80 to 1
81 to 2
...
if the values are not in row we can not set up an array with a value corresponting to an index, we need to set up an object.

means:

theValueToLookUp = ["78","79","80","81","82","83","84","85","86","87"]

indices are counted from left to right off of zero
[0,1,2,3,4,5,6,7,8,9];
when doing:

theIndexYouWant = theValueToLookUp.indexOf("80");
=>  2 

we now will instead set up an object with two arrays:
now the code when setting the corrtable looks like:

corrtable = {};   //this is now an object, no array

                    
input = ["82","75","76","77"];                       //this
output = ["0","4","5","6"];                          //and this are now arrays which we will put into the object

corrtable = 
{"keyboard":input,"output":output};

flow.set("corrtable",corrtable);

return null;

the array input and output you can now fill up with the values you need.
the sequence has to obviously be the same!

input is the array with the values coming from the keyboard.
output the number you want to get

okay, now we can lookup the values from your new corrtable in the replace......function:

input = msg.payload;
input = String(input);                  //here are the variables whe need for the function to work
table = flow.get("corrtable");
//----------------------------------------------------------
// here we do the stuff we actually want to do

indexOfMyValue = table.keyboard.indexOf(input);
numberIWant = table.output[indexOfMyValue];

//----------------------------------------------------------
// now that you got the "numberIWant"

//set the payload to it
msg.payload = numberIWant;

// and send it out
return msg;

lets do an example with the code above:

input = ["82","75","76","77"];  
output = ["0","4","5","6"]; 

corrtable = 
{"keyboard":input,"output":output};

which is the same as

corrtable = 
{
"keyboard": ["82","75","76","77"],
"output": ["0","4","5","6"]
};

lets say that there comes a "77" from the keyboard
msg.payload = "77" comes in ==>

indexOfMyValue = table.keyboard.indexOf("77");
==> indexOfMyValue = 3         //because "77" is at index 3

next row:

numberIWant = table.output[3];            // now we look what number (output) is in the corresponding
                                          //array at index 3
==> numberIWant = 6

then set message

msg.payload = numberIWant       //that was 6

and send it

return msg;

Thank you for your messages.
I just looked at them and I need to read them further to understand the subtleties !
Yours

After proofreading and proofreading, I think I have understood the process:
-1- We go from an array logic to an object logic.
-2- This object consists of a list of expected inputs and an output for each input.
-3- The "Replace ... numbers" function replaces the input with its output
-4- The list of inputs can be completed, provided that an output is associated with each added input.

So I added my additional values ​​in input and output, I replaced the code of the correspondence table and of the 1st function by what you sent me.
Miracle !!! Thanks for your help, I now have a full list; that's done !!

I then had to figure out how to start the execution of this process only when I wanted to (otherwise I could not use these keys in LibreOffice in Chromium for example).

To do this I have:

  • changed the way I enter numbers: instead of entering 123, I now enter! 123. The sequence produced therefore becomes 53 79 80 81 ("!" = 53).
  • modified the first function by adding "53" in the list of inputs and "!" in the list of outputs;
  • added a "!" before each number in the first column of my database.
    2nd miracle: it still works !!!!!!!!!!!!! So thank you for your precious advice!

To end my project, I must now see how to use the Page Up (104) and Page Down (109) arrows to go to the next or previous url without entering its N °.
I imagined the following solution:
-1- Add 104 and 109 to the list of inputs and, in correspondence, Pu and Pd to that of the outputs.

-2- Insert a "switch" node between the "set the number ..." node and the File node

  • If "Pu": sends output to a Pu function to be created whose object would be to have kept the previous input (ex:! 123), to add "1" to it and send it to the node "File", which would then normally continue the process.
    --If "Pd": send the output to a Pd function identical to Pu, but which removes "1" instead of adding it.
    --Otherwise: sending the output to the "File" node

Beyond the switch node, I searched all the contribs to find nodes that would allow me to perform these functions but I can't get out of it.

What do you think of this process? Should I continue in this direction?
Do you have an idea for this function?
Yours
[/quote]

Well done, you undestood it perfectly well!

in fact the switch noe would be one option to accomplish this. but unfortunately it would not be done with using a simlple switch.

  • the switch node can indeed "fork" your "signal"
  • it cannot do mathematical operation
  • it cannot remember the last value
  • it cannot parse numbers from strings
  • for that reason you would need to combine nodes that do that, for ex
    you were in need for switch, change and "a sort of" mathematical node

that way one can become creative in combining nodes until it works, but we try to avoid that, when we can do a simple "two-liner" in a function node.

first of all its a good idea to add "104" -> "PU" and "109" -> "PD"
to your "translation- table"
we'll pass these values to the function, that merges the numbers as they were a numerical input
because this is the place were the magic happens.

last version:

input = msg.payload;
input = String(input);                          //just in case
storage = context.get("storage");               // recall your storage
timer = context.get("timer")||null;             // recall last timer if exists
clearTimeout(timer);                            // clear the last timer
context.set("timer",timer);                     // set it to be emtpty
if (storage === undefined){
    storage = [];
}


//________________________________________________________________this is only called when sending the code
run = function mytimer(){                             //define a function that does all the stuff when you want to send the code

    output = storage.join()                     //put all array indices together in one string
    output = output.replace(/,/gi,"")          // get rid of the commas e.g. replace them by an empty string
    msg = {payload:output}                   
    node.send(msg)
    context.set("storage",[]);
    return null;
}

//________________________________________________________________


timer = setTimeout(run,1000);               // set your new timer (1sec is 1000 milliseconds)
storage.push(input);                        // append them to the end of your storage array
context.set("storage",storage);             // set your new array back to storage
context.set("timer",timer);                 // store your new timer
return null;

we do something new in here right now but

first thing first.
we're dealing with Strings right now, those are not numbers and are by the computer not interpreted as so and thats a good thing because strings stay the same unless you change them, thats very important for our correspondance table to work
because when dealing with numbers computers often do stuff we in some cases dont like them to do due to machine presicion
example:
could happen that you do a simple math operation like

y = 1 / 4;

obviously y should be 0.25
what can happen is that you get s.th. like

y = 0.25000000000000000000001;

this leads to the problem that y is NOT equal y
(this won't happen in this case, but as an example)

for this reason its good that were using stings.

easy task for the computer is:

newDomain = 123 + 1;
=> 124

but those are numbers, not strings! if you tried sth like we do

newDomain = "123"+1    // this results in

=> "1231"

newDomain = 1 + "123";      // this results in
=> "1123"

not what we actually want!
so we have to tell the programm we want it to interpret the value as a number and not as a following of characters
we do that by telling the prog that our string is a whole number (Integer, Int) it should parse from the String

value = "123";

number = value.parseInt(value);
=> number = 123;

now we have a number we could mathematically interoperate with and can implement that to our function above.

first of all we have to remember the last value our function sent in the myTimer() function

//store the output to a contextvariable "lastOut"
context.set("lastOut",output)

but so we can work with it we have to recall it everytime the function runs, as it could happen that we receive a PD or PU and want to change the number we sent

//recall the lastout and make a number out of it
lastOut = context.get("lastOut").parseInt();

now we will ask if the input is weather PD or PU and if this is the case, we need to increase the last value by +-1 send it (instantly without the second of time to wait) and store it again

if (input == "PU"){
    lastOut = lastOut + 1;
    context.set("lastOut",lastOut)
    node.send({payload: lastOut});
}else if (input == "PD"){
     lastOut = lastOut - 1;
    context.set("lastOut",lastOut)
    node.send({payload: lastOut});
}

now coders are very comfy dudes and like typeing but love results
so +- 1 we can write easier as

lastOut += 1;

// respective
lastOut -= 1
 

and because that even is too much to type and +-1 has to be done very very often the increment/decrement operator was invented

lastOut++;

// respective
lastOut--;

that all totally does the same. it adds 1 or substracts 1 from your variable
and now it looks like so

if (input == "PU"){
    lastOut++;
    context.set("lastOut",lastOut)
    node.send({payload: lastOut});
}else if (input == "PD"){
     lastOut--;
    context.set("lastOut",lastOut)
    node.send({payload: lastOut});
}

now in the case that we received PU or PD we dont have to run the rest of the scope anymore
and we abort the rest of the function here by a return;

if (input == "PU"){
    lastOut++;
    context.set("lastOut",lastOut)
    return {payload: lastOut};
}else if (input == "PD"){
     lastOut--;
    context.set("lastOut",lastOut)
    return {payload: lastOut};
}

now the new scope should look like:

input = msg.payload;
input = String(input);                          //just in case
storage = context.get("storage");               // recall your storage
timer = context.get("timer")||null;             // recall last timer if exists
clearTimeout(timer);                            // clear the last timer
context.set("timer",timer);                     // set it to be empty
if (storage === undefined){
    storage = [];
}

//recall the lastout and make a number out of it
lastOut = context.get("lastOut").parseInt();

if (input == "PU"){
    lastOut++;
    context.set("lastOut",String(lastOut));
    return {payload: lastOut};
}else if (input == "PD"){
     lastOut--;
    context.set("lastOut",String(lastOut));
    return {payload: lastOut};
}

//________________________________________________________________this is only called when sending the code
run = function mytimer(){                             //define a function that does all the stuff when you want to send the code

    output = storage.join()                     //put all array indices together in one string
    output = output.replace(/,/gi,"")          // get rid of the commas e.g. replace them by an empty string
//store the output to a contextvariable "lastOut"
context.set("lastOut",output)
    msg = {payload:output}                   
    node.send(msg)
    context.set("storage",[]);
    return null;
}

//________________________________________________________________


timer = setTimeout(run,1000);               // set your new timer (1sec is 1000 milliseconds)
storage.push(input);                        // append them to the end of your storage array
context.set("storage",storage);             // set your new array back to storage
context.set("timer",timer);                 // store your new timer
return null;

Thank you for your reply !
I dive in immediately, I think it will take me a few hours to understand all this.
Yours

I think I understand the process, at least in broad outline.
So I injected the code into the "replace keyboard input with numbers" node but it returns the following error when I type "! 1":
"TypeError: Cannot read property 'parseInt' of undefined"

I did the manipulation 2 times, but I still get this result.

Do you think the "!" disrupts operation?
The word "String" from line 2 of the code is red; is it a problem ?

Yours

Oh yes. The line has to be :

//recall the lastout and make a number out of it
lastOut = context.get("lastOut").parseInt() || 1;

Because it's not yet defined

Sorry I wrote this without approving it myself. I don't have access to NR at this moment

No it's good, String must be Red! That says it's a prototype funvtion

The code I wrote is not for the replace function!!!!
It's for the "send the number string ...." Func

Yes of course ! I made a mistake in writing the message.
je commence à tester.
Yours

I just retested and as I always have the same error message I started all the flow again following our discussions from the start.

Unfortunately, I always get the same result:
node: send the number string with 1sec timeout
function: (error)
"TypeError: Cannot read property 'parseInt' of undefined"

Yours

[{"id":"4ecd6c21.39aee4","type":"tab","label":"Flow 2","disabled":false,"info":""},{"id":"16b4bcb7.538223","type":"debug","z":"4ecd6c21.39aee4","name":"Receive","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":1440,"y":320,"wires":[]},{"id":"9c12e589.3a1f58","type":"inject","z":"4ecd6c21.39aee4","name":"","topic":"","payload":"79","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":70,"y":480,"wires":[["103e38ef.a48d37"]]},{"id":"8a029001.bf16","type":"inject","z":"4ecd6c21.39aee4","name":"","topic":"","payload":"85","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":70,"y":560,"wires":[["103e38ef.a48d37"]]},{"id":"95c31108.111c3","type":"inject","z":"4ecd6c21.39aee4","name":"","topic":"","payload":"81","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":70,"y":520,"wires":[["103e38ef.a48d37"]]},{"id":"f2a77fda.db3ab","type":"function","z":"4ecd6c21.39aee4","name":"send the number string with 1sec timeout","func":"input = msg.payload;\ninput = String(input);                          //just in case\nstorage = context.get(\"storage\");               // recall your storage\ntimer = context.get(\"timer\")||null;             // recall last timer if exists\nclearTimeout(timer);                            // clear the last timer\ncontext.set(\"timer\",timer);                     // set it to be empty\nif (storage === undefined){\n    storage = [];\n}\n\n//recall the lastout and make a number out of it\nlastOut = context.get(\"lastOut\").parseInt() || 1;\n\nif (input == \"PU\"){\n    lastOut++;\n    context.set(\"lastOut\",String(lastOut));\n    return {payload: lastOut};\n}else if (input == \"PD\"){\n     lastOut--;\n    context.set(\"lastOut\",String(lastOut));\n    return {payload: lastOut};\n}\n\n//________________________________________________________________this is only called when sending the code\nrun = function mytimer(){                             //define a function that does all the stuff when you want to send the code\n\n    output = storage.join()                     //put all array indices together in one string\n    output = output.replace(/,/gi,\"\")          // get rid of the commas e.g. replace them by an empty string\n//store the output to a contextvariable \"lastOut\"\ncontext.set(\"lastOut\",output)\n    msg = {payload:output}                   \n    node.send(msg)\n    context.set(\"storage\",[]);\n    return null;\n}\n\n//________________________________________________________________\n\n\ntimer = setTimeout(run,1000);               // set your new timer (1sec is 1000 milliseconds)\nstorage.push(input);                        // append them to the end of your storage array\ncontext.set(\"storage\",storage);             // set your new array back to storage\ncontext.set(\"timer\",timer);                 // store your new timer\nreturn null;","outputs":1,"noerr":0,"x":700,"y":380,"wires":[["bb056c77.72a3a","9dd13afe.f247c8"]]},{"id":"103e38ef.a48d37","type":"function","z":"4ecd6c21.39aee4","name":"replace keyboard input with numbers","func":"input = msg.payload;\ninput = String(input);                  //here are the variables whe need for the function to work\ntable = flow.get(\"corrtable\");\n//----------------------------------------------------------\n// here we do the stuff we actually want to do\n\nindexOfMyValue = table.keyboard.indexOf(input);\nnumberIWant = table.output[indexOfMyValue];\n\n//----------------------------------------------------------\n// now that you got the \"numberIWant\"\n\n//set the payload to it\nmsg.payload = numberIWant;\n\n// and send it out\nreturn msg;","outputs":1,"noerr":0,"x":350,"y":380,"wires":[["f2a77fda.db3ab","7790398e.5fe8f8"]]},{"id":"9dd13afe.f247c8","type":"file in","z":"4ecd6c21.39aee4","name":"","filename":"/home/pi/Desktop/collecteweb.csv","format":"utf8","chunk":false,"sendError":false,"encoding":"none","x":1040,"y":380,"wires":[["c3b23a9c.f74a78"]]},{"id":"c3b23a9c.f74a78","type":"csv","z":"4ecd6c21.39aee4","name":"","sep":";","hdrin":false,"hdrout":"","multi":"mult","ret":"\\n","temp":"","skip":"0","strings":false,"x":1270,"y":380,"wires":[["4321c23b.42b8ec","cf3d5a6e.63d2d8"]]},{"id":"4321c23b.42b8ec","type":"function","z":"4ecd6c21.39aee4","name":"your lookup func","func":"lookup = msg.lookup;\nvar output;\ndata = msg.payload;\n\nfor (i=0;i<data.length;i++){\n    if (data[i].col1 == lookup){\n        output = data[i].col2;\n        context.set(\"lookup\",null);\n        msg = {payload:output};\n        node.send(msg);\n    }\n}\n\nreturn null;","outputs":1,"noerr":0,"x":1440,"y":380,"wires":[["16b4bcb7.538223","def8e270.a643e"]]},{"id":"bb056c77.72a3a","type":"debug","z":"4ecd6c21.39aee4","name":"send","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"lookup","targetType":"msg","x":670,"y":320,"wires":[]},{"id":"cf3d5a6e.63d2d8","type":"debug","z":"4ecd6c21.39aee4","name":"object","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":1250,"y":320,"wires":[]},{"id":"58d4b65a.80a018","type":"comment","z":"4ecd6c21.39aee4","name":"","info":"the seperator has to be semikolon (;)\nwhenn using miltiple columns, not commas\nthats an excel-thing","x":960,"y":420,"wires":[]},{"id":"def8e270.a643e","type":"exec","z":"4ecd6c21.39aee4","command":"DISPLAY=:0 chromium-browser","addpay":true,"append":"","useSpawn":"false","timer":"","oldrc":false,"name":"","x":1390,"y":580,"wires":[[],[],[]]},{"id":"581279c2.f86a48","type":"rpi-keyboard","z":"4ecd6c21.39aee4","name":"","x":350,"y":120,"wires":[["45370388.767f9c"]]},{"id":"45370388.767f9c","type":"calculate","z":"4ecd6c21.39aee4","name":"","pauseType":"rate","calculation":"max","timeout":"500","timeoutUnits":"milliseconds","rate":"1","x":350,"y":180,"wires":[["103e38ef.a48d37"]]},{"id":"286877bd.f63a58","type":"inject","z":"4ecd6c21.39aee4","name":"","topic":"","payload":"","payloadType":"date","repeat":"1","crontab":"","once":true,"onceDelay":"1","x":90,"y":40,"wires":[["58046171.80949"]]},{"id":"58046171.80949","type":"function","z":"4ecd6c21.39aee4","name":"set your correspondence table to flow","func":"corrtable = {};   //this is now an object, no array\n\n                    \ninput = [\"53\",\"71\",\"72\",\"73\",\"75\",\"76\",\"77\",\"79\",\"80\",\"81\",\"82\",\"104\",\"109\"];                       //this\noutput = [\"!\",\"7\",\"8\",\"9\",\"4\",\"5\",\"6\",\"1\",\"2\",\"3\",\"0\",\"pu\",\"pd\"];                          //and this are now arrays which we will put into the object\n\ncorrtable = \n{\"keyboard\":input,\"output\":output};\n\nflow.set(\"corrtable\",corrtable);\n\nreturn null;","outputs":0,"noerr":0,"x":350,"y":40,"wires":[]},{"id":"7790398e.5fe8f8","type":"debug","z":"4ecd6c21.39aee4","name":"replace","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":380,"y":320,"wires":[]},{"id":"5521cb26.f9e004","type":"inject","z":"4ecd6c21.39aee4","name":"","topic":"","payload":"!","payloadType":"str","repeat":"","crontab":"","once":true,"onceDelay":0.1,"x":70,"y":440,"wires":[["103e38ef.a48d37"]]}]

Bloomin heck ! This has turned into quite a saga :slight_smile:

1 Like

The saga of a nubee who is passionate about nodered!

2 Likes

Sorry my friend, my bad!

//recall the lastout and make a number out of it
lastOut = context.get("lastOut").parseInt() || 1;

That won't work!
Try

lastOut = context.get("lastOut")||"1";
lastOut = parseInt(lastOut);
//sorry