Help with a function node

Hi Andrei

Yes, you are on the correct track.

I need to check a csv file to see if a payload from topic cardUID exists in the csv, if it exists, then parse a notification either false or 0 or "Card Exists" on. If it doesn't exist, then add the payload along with a name to the csv file using the below function node.

[{"id":"bc7b9f5e.5a7fe","type":"function","z":"e79d0bbd.096098","name":"Merge the two","func":"context.data = context.data || {};\n\nswitch (msg.topic) {\n    case \"Name\":\n        context.data.Name = msg.payload;\n        msg = null;\n        break;\n    case \"cardUID\":\n        context.data.cardUID = msg.payload;\n        msg = null;\n        break;\n        \n    default:\n        msg = null;\n    \tbreak;\n\n}\n\nif(context.data.Name != null && context.data.cardUID != null ) {\n\tmsg2 = {};\n    msg2.payload = \"\"+context.data.Name+\",\"+context.data.cardUID;\n    \n    context.data=null;\n\treturn msg2;\n} ","outputs":1,"noerr":0,"x":754,"y":96,"wires":[["25fe6988.b556be","281ac8e5.2641e8"]]}]

This is for a simple door access system with a csv file that holds card numbers and names. Basic security system for my son who is trying to keep his sister out of his room.

Thanks

I see. In such case it is likely that the flow can be simpler than I imagined. I will see if I can work out another suggestion. In the mean time keep an eye on your children, specially if your daughter watches the series "Dexter´s Laboratory" :smile:

Thanks @Andrei

I look forward to see what you suggest. I am being bugged to get this sorted out..........I do think he got the idea from "Dexter's Laboratory" !!!

:slight_smile:

ok, before I continue please test this partial flow and tell me if I am still on the right track.

Given a cvs file like this:

UID, Name
UID001,Dexter
UID002,Dee Dee
UID003,Mandark
UID004,Simion
UID005,Major Glory
UID006,Val Hallen
UID007,Dad
UID008,Mom

In the first step the flow will read the file and have it stored in the flow context.

In a second step a jsonata expression will check if msg.topic is a value included in the UID list and will return true or false.

[{"id":"bf609542.556b38","type":"tab","label":"Flow 6","disabled":false,"info":""},{"id":"ef80926.9986d7","type":"inject","z":"bf609542.556b38","name":"Trigger Flow","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":130,"y":80,"wires":[["f620a872.527c18"]]},{"id":"f620a872.527c18","type":"file in","z":"bf609542.556b38","name":"Read CSV","filename":"C:\\Users\\OCM\\.node-red\\static\\nrfiles\\dexter.csv","format":"utf8","chunk":false,"sendError":false,"x":290,"y":80,"wires":[["7837e666.ccbed8"]]},{"id":"5ffe2334.f88e9c","type":"debug","z":"bf609542.556b38","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":730,"y":80,"wires":[]},{"id":"7837e666.ccbed8","type":"csv","z":"bf609542.556b38","name":"","sep":",","hdrin":true,"hdrout":"","multi":"mult","ret":"\\n","temp":"","skip":"0","x":430,"y":80,"wires":[["3d4fdc44.b1c5b4"]]},{"id":"b6b0d2ab.f0843","type":"debug","z":"bf609542.556b38","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"result","x":530,"y":160,"wires":[]},{"id":"3d4fdc44.b1c5b4","type":"change","z":"bf609542.556b38","name":"","rules":[{"t":"set","p":"csv","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":570,"y":80,"wires":[["5ffe2334.f88e9c"]]},{"id":"87fc7f29.ab3a1","type":"inject","z":"bf609542.556b38","name":"","topic":"UID007","payload":"","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":120,"y":160,"wires":[["b998fe74.fbb4c"]]},{"id":"b998fe74.fbb4c","type":"change","z":"bf609542.556b38","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"csv","tot":"flow"},{"t":"set","p":"result","pt":"msg","to":"topic in $.[payload.UID]","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":340,"y":160,"wires":[["b6b0d2ab.f0843"]]}]

If this is good then it is missing to work on the last part to comply with this requirement:

If it doesn't exist, then add the payload along with a name to the csv file using the below function node.

mmm.. silly me.. what I did above is no sense (vis-a-vis with topic and payload usage)... I guess I got the idea now.

Probably this is what you are expecting:

[{"id":"bf609542.556b38","type":"tab","label":"Flow 6","disabled":false,"info":""},{"id":"ef80926.9986d7","type":"inject","z":"bf609542.556b38","name":"Trigger Flow","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":130,"y":80,"wires":[["f620a872.527c18"]]},{"id":"f620a872.527c18","type":"file in","z":"bf609542.556b38","name":"Read CSV","filename":"C:\\Users\\OCM\\.node-red\\static\\nrfiles\\dexter.csv","format":"utf8","chunk":false,"sendError":false,"x":290,"y":80,"wires":[["7837e666.ccbed8"]]},{"id":"5ffe2334.f88e9c","type":"debug","z":"bf609542.556b38","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":730,"y":80,"wires":[]},{"id":"7837e666.ccbed8","type":"csv","z":"bf609542.556b38","name":"","sep":",","hdrin":true,"hdrout":"","multi":"mult","ret":"\\n","temp":"","skip":"0","x":430,"y":80,"wires":[["3d4fdc44.b1c5b4"]]},{"id":"b6b0d2ab.f0843","type":"debug","z":"bf609542.556b38","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":510,"y":160,"wires":[]},{"id":"3d4fdc44.b1c5b4","type":"change","z":"bf609542.556b38","name":"","rules":[{"t":"set","p":"csv","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":570,"y":80,"wires":[["5ffe2334.f88e9c"]]},{"id":"87fc7f29.ab3a1","type":"inject","z":"bf609542.556b38","name":"","topic":"cardUID","payload":"UID007","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":140,"y":160,"wires":[["b998fe74.fbb4c"]]},{"id":"b998fe74.fbb4c","type":"change","z":"bf609542.556b38","name":"","rules":[{"t":"set","p":"csv","pt":"msg","to":"csv","tot":"flow"},{"t":"set","p":"result.cardUID","pt":"msg","to":"payload in $.[csv.UID]","tot":"jsonata"},{"t":"set","p":"result.Name","pt":"msg","to":"payload in $.[csv.Name]","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":340,"y":160,"wires":[["b6b0d2ab.f0843"]]},{"id":"5b4302fc.fd2dcc","type":"inject","z":"bf609542.556b38","name":"","topic":"Name","payload":"Mom","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":130,"y":220,"wires":[["b998fe74.fbb4c"]]}]

It is not clear for me what information and what is the rule to append something to the file. Considering this is an access system then probably you want to log in a file each attempt made ? The flow below saves a log file (logline.txt).

[{"id":"c8c1ca3b.e4d8c8","type":"tab","label":"Dexter List","disabled":false,"info":""},{"id":"b3b4a21c.2f6c9","type":"inject","z":"c8c1ca3b.e4d8c8","name":"Trigger Flow","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":150,"y":160,"wires":[["45f7c805.f01608"]]},{"id":"45f7c805.f01608","type":"file in","z":"c8c1ca3b.e4d8c8","name":"Read CSV","filename":"C:\\Users\\OCM\\.node-red\\static\\nrfiles\\dexter.csv","format":"utf8","chunk":false,"sendError":false,"x":310,"y":160,"wires":[["a97ac176.6e676"]]},{"id":"a97ac176.6e676","type":"csv","z":"c8c1ca3b.e4d8c8","name":"","sep":",","hdrin":true,"hdrout":false,"multi":"mult","ret":"\\n","temp":"","skip":"0","x":450,"y":160,"wires":[["3b798098.1b92f"]]},{"id":"b7c75a79.0c2a28","type":"debug","z":"c8c1ca3b.e4d8c8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":850,"y":260,"wires":[]},{"id":"3b798098.1b92f","type":"change","z":"c8c1ca3b.e4d8c8","name":"","rules":[{"t":"set","p":"csv","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":630,"y":160,"wires":[[]]},{"id":"61923d0.76ef2c4","type":"inject","z":"c8c1ca3b.e4d8c8","name":"Valid card","topic":"","payload":"UID003","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":140,"y":260,"wires":[["5f821927.3bafc8"]]},{"id":"5f821927.3bafc8","type":"change","z":"c8c1ca3b.e4d8c8","name":"","rules":[{"t":"set","p":"csv","pt":"msg","to":"csv","tot":"flow"},{"t":"set","p":"list","pt":"msg","to":"csv{UID : Name}","tot":"jsonata"},{"t":"set","p":"valid","pt":"msg","to":"payload in $keys(list)","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":360,"y":260,"wires":[["94e8cfd6.08269"]]},{"id":"76e40342.b4463c","type":"inject","z":"c8c1ca3b.e4d8c8","name":"Invalid card","topic":"","payload":"UID022","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":130,"y":320,"wires":[["5f821927.3bafc8"]]},{"id":"94e8cfd6.08269","type":"function","z":"c8c1ca3b.e4d8c8","name":"Logline","func":"let pay = msg.payload;\nif (msg.valid) {\n    msg.payload = `Pass:      ${pay},${msg.list[pay]}`} else {\n    msg.payload = `Violation: ${pay}, Not in the list`    \n    }\n\nreturn msg;","outputs":1,"noerr":0,"x":540,"y":260,"wires":[["3077ba82.c54b06"]]},{"id":"47fe33d4.d0d61c","type":"comment","z":"c8c1ca3b.e4d8c8","name":"Dexter file","info":"UID, Name\nUID001,Dexter\nUID002,Dee Dee\nUID003,Mandark\nUID004,Simion\nUID005,Major Glory\nUID006,Val Hallen\nUID007,Dad\nUID008,Mom","x":140,"y":80,"wires":[]},{"id":"3077ba82.c54b06","type":"file","z":"c8c1ca3b.e4d8c8","name":"Save file","filename":"C:\\Users\\OCM\\.node-red\\static\\nrfiles\\logline.csv","appendNewline":true,"createDir":false,"overwriteFile":"false","x":680,"y":260,"wires":[["b7c75a79.0c2a28"]]}]

HI Andrei

Thanks for your help on that flow it worked nearly 100%.

1 last question, the card reader is introducing a carrige return which is flowing through and messing up the csv file.

Any idea on a function I can use to remove this?

Carrige%20Return

Thanks

Use a change node

Change msg.payload

I am happy to know about the progress.

As for the question on removing the CR it is possible to use a regex expression. I prefer however using the string slice method. In windows I would need to remove two characters. There are possibly other ways though.

let bad = "3141592\n\r";
let good = bad.slice(0,-2);

Flow:

[{"id":"1faf8001.775d3","type":"tab","label":"Flow 5","disabled":false,"info":""},{"id":"a533de7a.1c0f9","type":"inject","z":"1faf8001.775d3","name":"","topic":"","payload":"testing ended","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":210,"y":180,"wires":[["76fa6197.c2c7c"]]},{"id":"1b85b7b4.1c1678","type":"debug","z":"1faf8001.775d3","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":570,"y":180,"wires":[]},{"id":"76fa6197.c2c7c","type":"function","z":"1faf8001.775d3","name":"Remove CR","func":"let bad = \"3141592\\n\\r\";\nlet good = bad.slice(0,-2);\nnode.warn(bad);\nnode.warn(good);\nreturn msg;","outputs":1,"noerr":0,"x":390,"y":180,"wires":[["1b85b7b4.1c1678"]]}]

r-01

1 Like

.trim() is often simpler as it will remove whitespace at beginning or end only if it is there or not... so is safe to apply "just in case"

let good = bad.trim();
1 Like

Thanks @Andrei and @dceejay for the help.

Works great in the test flow sent both with trim and slice, but how do I add to that function where the inbound msg.payload contains the CR.

Works great if I specify a fixed number with a carige return, how do I add msg.payload to the "let bad =" statement as I have tried several way and none seem to change.

Excuse what may be a simple question, but I am fairly new to JS and NR.

Hi @RackIoT, your welcomed.

What is nice about the string method trim() is that it will remove trailing and leading "garbage (cr, lf, tab, space, etc)" from the string. Also it will remove automatically any amount of characters (not only one).

The best place to use the function is right after the string is created , maybe the card reader ?

I assume you made some adjustments to the flow. If you provide a new picture of the flow (or its code) we can have a look to check the placement of the trim() function.

Hi @Andrei

Below is a screen shot of the flow:

Reader%20Flow

The Wiegand Decoder just calls a python script and the script/reader combination is what is introducing the CR.

I have placed teh function node from you as above. Below is what I get out of the wiegand decoder node on a debug.

msgpayload

It is just a standadr msg.payload format, but it contains the CR.

So in short all I want to do is remove the CR from a variable msg.payload as the cadr numbers are all different and my son wants to see who has gone in his room........fussy like that I suppose.

thanks

ok, understood. In such case it is very simple.

Just use this code inside your function node (replacing everything else that is there):

let good = msg.payload.trim();
msg.payload = good;
return msg;

perfect thanks @Andrei

HI @Andrei

I have been playing with this flow a lot lately and managed to insert a timestamp into the log file which was needed, but the one thing I cant seem to get correct, is to change the msg.topic that gets to your LogLine function to be the name that is associated cardUID when it check the cardUID to return a true or false.

The idea would be that if the change: 3 rules returns a true for cardUID, that it then reads the Name of line in the csv or object where it matched and inserts that as the msg.topic so that when the log file lines are created I can see who successfully presented a card.

Hope you done mind helping with this as I have tried all sorts of methods, but cant get it correct.

Thanks

sure, I will check and reply you back. Question: Do you want keep authenticating also by username ? This is in place in the code but not sure if it is of any use for your case. perhaps it is better to authenticate only based on cardID ?

@ Andrei - I need to only authenticate/check on the cardUID. Great suggestion.

If the cardUID exists, i need some way of recovering the Name field associated with the cardUID.

Thanks

Hi @RackIoT,

It took me a while to refactor the code. This use case was great for me to practice using jsonata. It is amazing how fast we can forget something when not using it for ... 1 or 2 days (yep, I hope one day science will find a cure for Anterograde Amnesia).

Here is the code , I hope it works..

[{"id":"c8c1ca3b.e4d8c8","type":"tab","label":"Dexter List","disabled":false,"info":""},{"id":"b3b4a21c.2f6c9","type":"inject","z":"c8c1ca3b.e4d8c8","name":"Trigger Flow","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":150,"y":160,"wires":[["45f7c805.f01608"]]},{"id":"45f7c805.f01608","type":"file in","z":"c8c1ca3b.e4d8c8","name":"Read CSV","filename":"C:\\Users\\OCM\\.node-red\\static\\nrfiles\\dexter.csv","format":"utf8","chunk":false,"sendError":false,"x":310,"y":160,"wires":[["a97ac176.6e676"]]},{"id":"a97ac176.6e676","type":"csv","z":"c8c1ca3b.e4d8c8","name":"","sep":",","hdrin":true,"hdrout":false,"multi":"mult","ret":"\\n","temp":"","skip":"0","x":450,"y":160,"wires":[["3b798098.1b92f"]]},{"id":"b7c75a79.0c2a28","type":"debug","z":"c8c1ca3b.e4d8c8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":710,"y":260,"wires":[]},{"id":"3b798098.1b92f","type":"change","z":"c8c1ca3b.e4d8c8","name":"","rules":[{"t":"set","p":"csv","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":630,"y":160,"wires":[[]]},{"id":"61923d0.76ef2c4","type":"inject","z":"c8c1ca3b.e4d8c8","name":"Valid card","topic":"","payload":"UID007","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":140,"y":260,"wires":[["5f821927.3bafc8"]]},{"id":"5f821927.3bafc8","type":"change","z":"c8c1ca3b.e4d8c8","name":"","rules":[{"t":"set","p":"csv","pt":"msg","to":"csv","tot":"flow"},{"t":"set","p":"list","pt":"msg","to":"csv{UID : Name}","tot":"jsonata"},{"t":"set","p":"valid","pt":"msg","to":"payload in $keys(list)","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":360,"y":260,"wires":[["94e8cfd6.08269"]]},{"id":"76e40342.b4463c","type":"inject","z":"c8c1ca3b.e4d8c8","name":"Invalid card","topic":"","payload":"UID022","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":130,"y":320,"wires":[["5f821927.3bafc8"]]},{"id":"94e8cfd6.08269","type":"function","z":"c8c1ca3b.e4d8c8","name":"Logline","func":"let pay = msg.payload;\nif (msg.valid) {\n    msg.payload = `Pass:      ${pay},${msg.list[pay]}`} else {\n    msg.payload = `Violation: ${pay}, Not in the list`    \n    }\n\nreturn msg;","outputs":1,"noerr":0,"x":540,"y":260,"wires":[["b7c75a79.0c2a28"]]},{"id":"47fe33d4.d0d61c","type":"comment","z":"c8c1ca3b.e4d8c8","name":"Dexter file","info":"UID, Name\nUID001,Dexter\nUID002,Dee Dee\nUID003,Mandark\nUID004,Simion\nUID005,Major Glory\nUID006,Val Hallen\nUID007,Dad\nUID008,Mom","x":140,"y":80,"wires":[]}]

Hey,

This is a very interesting flow which I might be able to use for my project. Does anyone know how to scan a new rfid user in the csv file and use the touch panel to enter the name?