Why am I getting the wrong *type* of data in this message?

I am TRYING to test some code for weather (rain) detection.

Originally it wasn't this smart, but now I am wanting to improve it.

I've cut down a lot of stuff and tried to cut it to what is THE problem.

If it is raining part of the message exists and it is parsed.
If it isn't raining that part doesn't exist and a dummy is made. (The value is set to 0)
That is then sent on and rounded to 1 decimal place - yeah: deal with the numbers as given - and stored (flow context)
If the flow.MODE is NOT B then it adds the values.
That was painful as + was being done as stick to the end of rather than ADD.
This was because the values were TEXT rather than numbers.

I got around that problem and it started to work.
Then it stopped and now doesn't seem to want to play the game.

No matter what I do I am NOT getting NUMBERS out of the function node in msg.rain_now
The top function node after the switch node.

Ok, I could deal with the entire value given and tidy it up later on for display purposes.
That is a whole problem unto itself.

So TRYING to get it working:
Why is it that msg.rain_now is TEXT and not NUMBER?

[{"id":"89c34e754b89497e","type":"inject","z":"05319bbbba06da29","name":"0.818181","props":[{"p":"data.rain[\"1h\"]","v":"0.818181","vt":"num"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":470,"y":810,"wires":[["4da4a97fbc646912"]]},{"id":"37a2bea6f1a3d08f","type":"inject","z":"05319bbbba06da29","name":"0.1616161616","props":[{"p":"data.rain[\"1h\"]","v":"0.1616161616","vt":"num"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":480,"y":860,"wires":[["4da4a97fbc646912"]]},{"id":"4da4a97fbc646912","type":"switch","z":"05319bbbba06da29","name":"Raining?","property":"data.rain[\"1h\"]","propertyType":"msg","rules":[{"t":"gt","v":"0","vt":"num"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":925,"y":910,"wires":[["bbf430450a163bba","f082cb9f8d1294b0","61d5dac37fcb1773"],["90d76570e9d18b83","52c64f8853124708"]],"l":false},{"id":"61d5dac37fcb1773","type":"function","z":"05319bbbba06da29","name":"Set `rain_now` to value.","func":"let rain_now = parseFloat(msg.data.rain[\"1h\"]).toFixed(1);\nmsg.rain_now = rain_now;\n\nnode.warn(\"Rain now \" + rain_now);\n//msg.something = 0.6;\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1005,"y":910,"wires":[["2b5173aa2b316920","0b369a7bec8d4350"]],"l":false},{"id":"52c64f8853124708","type":"function","z":"05319bbbba06da29","name":"No rain detected","func":"msg.rain_now = 0;\nmsg.payload = 0;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1005,"y":950,"wires":[["0ad56c2f26fe2166"]],"l":false},{"id":"2b5173aa2b316920","type":"switch","z":"05319bbbba06da29","name":"==B?","property":"mode","propertyType":"flow","rules":[{"t":"eq","v":"B","vt":"str"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":1075,"y":910,"wires":[["13659bc680d6437d"],["0ad56c2f26fe2166"]],"l":false},{"id":"0ad56c2f26fe2166","type":"junction","z":"05319bbbba06da29","x":1090,"y":1010,"wires":[["4f3f2b25c2f8d6d6"]]},{"id":"04eeefaf3ac83cdd","type":"function","z":"05319bbbba06da29","name":"Store rain.","func":"//      If `reset` message received.\nif (msg.reset == \"reset\")\n{\n    msg.rain_now = flow.get(\"rain\");\n    return msg;\n}\n\nlet todays_rain = parseInt(flow.get(\"rain\")) || 0;\n//node.warn(\"Today's rain is \" + todays_rain);\n//node.status({ text: todays_rain });\n\nlet rain_now = parseInt(msg.rain_now);\n\n//todays_rain = todays_rain + rain_now;\n//todays_rain = parseFloat(todays_rain + rain_now).toFixed(1);\ntodays_rain = todays_rain + rain_now;\n\nnode.warn(\"Adding rain totals together\");\nnode.warn(\"rain now \" + rain_now);\nnode.warn(\"Today's rain \" + todays_rain);\n\nflow.set(\"rain\",todays_rain);\n\nnode.status({ text: todays_rain });\n\nmsg.payload = todays_rain;\nmsg.topic = null;\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1380,"y":1010,"wires":[["ea17db4b610c2bca","f70d474fa4e6a312","0a7ddf199d71ddce","0543926b6e610c6f"]]}]

This is a rewrite of what I had and worked.
Well, it worked in that values seemed to be passed correctly.

But to simplify things, I moved things around and processed the original message to give me a usable message.
This becomes/became msg.rain_now and it a number with 1 needed decimal place accuracy.
From here on it is only one message that needs to be handled and if/when a new message arrives, the context value is added then stored back to the context.
If/when the ONE ONLY message arrives requesting the value, it is taken and then on-sent.

Should be easy, but I am getting text rather than number values sent.
As I'm already down this rabbit hole, going back to the older way - which had it's own set of problems - I would prefer to get this working.

So although you are seeing something that doesn't work - why would I ask for help if it did work? - I hope you can see what it is and HOW I want to do it.

Again: Thanks.

//with this you'll get a string 
// because parseFloat(msg.data.rain["1h"]) returns a number
// and then you make it a string by .toFixed(1)
let rain_now = parseFloat(msg.data.rain["1h"]).toFixed(1);

node.warn("rain_now is "+ typeof rain_now)

// to get a number
let rain_now_as_number = parseFloat(msg.data.rain["1h"].toFixed(1));
node.warn("rain_now_as_number is " + typeof rain_now_as_number)
1 Like

Indulge me this question after I've said that was the solution:

This is what I am seeing - and things are good.

I have sent a value from one of the two inject nodes.
0.81818181
That stored 0.8 and then I sent the value again.

That gives me the 1.6 which is shown at the bottom as the payload.

I then inject a third 0.81818181 and this happens.

2.4000000000000004

ARGH!

Ok, it is ONLY a value and it makes no difference at the end of the day.

But I've "tidied up" the values so they are only 1 digit (after decimal) correct.
So HOW does the 0000000000004 get involved?

I'm guessing float has something to do with it.

P.S.

I CHECKED the flow.context and it was 1.6 and not.... 1.600000000000000000000 or anything like that.
The THIRD injection showed 0.8 going through the nodes.
So.... As much as I admit I suspect float doing it: I don't see how.
There is no error there to start with to invoke the 00000000000004 after the third injection.

If you're after siencific answer, do google something like "JavaScript floating point precision"

But actually you shouldn't care about it that too much.

1 Like

I have read things like that.

Yes, I get it that I SHOULDN'T let it get to me.

Alas: It does. Not seriously, but I just don't get it.
I'll go and read it again to try and put myself at ease.

It is called floating point rounding error. Float values are stored with a finite number of bits (because computers so far at least, don't have an infinite number of bits) so often numbers are not held or calculated accurately. You said yourself in your other thread that 20/3 is 6.666666666667, but that is not correct. The sixes should go on for ever. Whenever you do a calculation there is the possibility of a 1 bit error. The moral is don't reduce the precision of numbers until you absolutely need to. Keep them as numbers (not strings) to full precision and only round them when you need to display them.

(all)

So one way I could get around this problem is:
(Please indulge me here)

Get the original payload
multiply it by 10.
convert it to an integer. (gets rid of all after the decimal point)
do all my magic stuff with that number.
When displaying it: divide the number by 10 and .toFixed(1) to give it a fixed (forced) decimal accuracy.

As that converts it to a string (late with being told that - another example didn't exactly show/declare that when they showed it - another story) thing should then show correctly.

Would that work?

I haven't looked at your magic stuff, but does it need to be converted to an integer for that to work?

Well, (say) I am getting values like:
1.2345
and 2.3456
I don't want/need anything more than either 1.2 or 2.3.
Their total doesn't matter if it is 3.3 or 3.4

As I am wanting to allow 0.1 accuracy I need to keep the first number after the decimal point.

So if I *10 the value then parseInt() that value: that would then give me (from the above example) 12 or 23 - yes?
Then I have the right number of digits after the decimal point included in my stuff/magic.

So as long as I allow from the value being ten times bigger, I can allow for that with any downstream maths I want to do.

Don't do that until later. Just add them up (if that is what you are doing) which will give 3.5841, then when you come to display it do the rounding, to give you 3.6

1 Like

Yes, I understand that is THE way to do it.
All the stuff happens under the bonnet and the user only needs to see the formatted stuff.

I am doing this only as an exercise in ....... making my life difficult? :wink:
how to constrain inputs to limits.

So if I am getting squillion digit inputs and only need limited digit inputs: how do I do it.
Yes, this does directly arc back to what was first said.
But to learn how to constrain things it seems like a good idea.

You ignore it till you are about to display it and then used toFixed(1) or {{msg.payload | number: 1}} where appropriate.

Yes, we've agreed that is the way to do it.

But I am wanting to learn how to constrain inputs.

This all may have been brought on because I am learning how to use CAD programs.
Dunno.
But the word CONSTRAINT is sticking in my vocabulary just now and what it means.

What do you mean by constrain inputs? Do you mean operator inputs on the dashboard?

I am not sure what I mean.

But if i am writing a function node using a function node and have things need to be parsed/checked they are of certain ..... things.......

And subflows - which I am sure you know I seem to like. :wink:

Maybe (probably) I am just hung up on the word constrain.
But as it seems to be just now: I am for some reason stuck on this thing.
Sorry if it is not nice for others. But I think this group is a good place to learn about structures in languages than any other.
So alas I am asking here.
Also because we all talk javascript.
So it is a base line that can be used to establish things.

Can you give a real example of where you need to constrain something to fixed digits other than when it is being displayed?

Alas not really.

I shall have to resign myself to the theory that I am just hung up on it.

This scenario probably triggered/tripped me over the threshold of needing to ask the question.
Badly formed and poorly asked.

I was just wanting to tidy up some stuff and make the flow happen smoother than it was.
Believe me: It was a lot worse than it is now.

And while doing it the problem landed in my lap on how to do it.

If you have not got a real example then how is it a problem.

But if you do have a number and want to take it to the nearest value with fixed precision, but keep it as a number, then the easiest way is probably
const rounded = (Math.round(value * 10))/10

1 Like

It was more a question because I was trying something to simplify something else.

Yes, I make my own life harder than it needs to be. This is a good day.

That little trick you posted may be of interest.
My working solution was (for this example posted above) was to do this:
Going into the part of the flow.

let rain_now = parseInt(msg.data.rain["1h"]*10);
msg.rain_now = rain_now;

return msg;

Coming out I just reversed it.

I'll have to look more at/into what Math.round() does.

I think that probably converts it to a string and back again, which isn't very efficient.