Need help with expresion with variable for setting temp set points in node red

I am trying to get two thermostats synced using node red. I am able to pull the values i need to set the thermostat to but i get an error “Expected “:”, got “}”” every time i try to run it. If i replace the variables with numbers it works fine. I am thinking maybe it is because the numbers being passed are not float values but integers. Can anyone tell me how to fix this?

{
“target_temp_low”: {{msg.number}},
“target_temp_high”: {{msg.message}}
}

Looks as if you are using Mustache Templates in a JSONata expression.

Mustache templates work well for simple strings like “my target temperature is {{msg.number}}” but it gets muddled with trying to build objects. Better to use JSONata to do this.

No idea what node you are using or what UI field or what option, or what your data is, but assuming that you want a J: expression (JSONata) then

{ “target_temp_low”: number, “target_temp_high”: message}

should do the trick. JSONata will evaulate the above, and number will be replaced with the value of msg.number etc.

After that, it will be a question of what exactly is in msg.number, and what exactly your, I assume, service call requires. You probably need $number(number) and $number(message):

{ "target_temp_low": $number(number), "target_temp_high": $number(message)}

Hope this works!

For Posterity

If anyone is interested, "Expected “:”, got “}” " is JSONata complaining that you are not building an object correctly.

In JSONata { } is used to build a JSON object, and it expects {“key”: “value”} where key must be either a string literal or an expression that returns a string, and value must be either a valid JSON literal or an expression that returns a valid JSON value.

Since you have entered {{msg.number}} in the place where JSONata is expecting a JSON value for the “target_temp_low” object key,

JSONata starts to build an object,
then a second object nested in the first object, (this is why Mustache templates do not work in JSONata!)
then evaluates msg.number (you don’t actually need the leading msg.) to return its value - probably finds a string so this is OK for a key, then starts looking for the “:” separator and bumps into the object closing } instead - hence the error.

If your msg.number is “23” you are in effect building an object {“23”: value}. This is actually a neat feature of JSONata, as the object key can be any expression as long as it returns a string!

This certainly suggests that your msg.number is a string, otherwise JSONata would complain with a different message since you can’t have numbers as JSON keys (error - invalid key type)

As HA holds entity state values as strings, I expect the msg.number value is “23” and we need $number(number) to turn the string into a number. This can arrive as a float, and so sometimes it may be better to use $parseInteger(number, ‘0’) although I find getting the parsing picture string correct can be tricky.
https://docs.jsonata.org/numeric-functions#parseinteger

1 Like

Neither of those worked :frowning: you are correct I am trying to do this in a JSONata allthough I am willing to do whatever method just trying to sync two thermostats I have been messing with this for days in yaml files and node red cant get anything to work. Here are the full details:

The error is occuring in the call service node called set temp
image

Here is what the node looks like:

Both methods you misted resulted in the same error You can see the data from the debug node 3 that is being fed into the call service node:
image

I believe this value should be a float value it turns green if i just type in a number in rather then a variable and then it works. I also tried $float() and that did not work either. Any ideas, and thank you?!?!?

***I tried passing the values as a string as well with the same results

As a general tip, if you ask for help with a Node-RED problem, it assists everyone if you post

  • what you are trying to do
  • what the symptoms of the problem are
  • your flow: a picture is nice, the exported flow is better
  • the data you are working with (both input and output)

So, we fixed the first problem - the code I provided is working. Now I can see your flow (picture) I can see the big problem with the flow itself.

When you have multiple lines coming out from a node, Node-RED makes copies of the message and sends one copy down each path. These messages run through the rest of the flow, individually, and do not ‘join up’ unless you specifically join them with a ‘join node’.

So, your inject node is sending one message to pick up the ‘number’, and another separate message to pick up the ‘message’ variables.
When these messages arrive at your service call - they each run a service call and do so without any knowledge of the other message.

If you hard code your values, then it works, but effectively you are calling the service twice.

When each message arrives on its own, the service call has either ‘number’ or ‘message’ but not both. Hence the JSONata code builds an incomplete object, and naturally the service call complains because it gets either the temp_low, or the temp_high, but not both as it requires. So you get two error messages.

You need to join the messages up, so as to have both values in the same message (before using them) or just one message flow and collect both values as you go.

My suggestion:

Link from your inject node to your first current:state node.
Set this to output msg.temp_low as ‘entity state’
Remove the msg.data output (you don’t need this)

Join the output to your second current:state node.
Set this one to output msg.temp_high as ‘entity state’
Remove the msg.data output

Make sure both current:state nodes have ‘state type’ set to ‘number’ to ensure you are getting the state values out as integers and not strings.

Join the output directly to your call service node.

Use the following JSONata for Data

{ "target_temp_low": temp_low, "target_temp_high": temp_high}

The single message will collect the low temperature setting in the first current state node, and keep this in msg.temp_low. This will pass through the second current state node, which also collects the high temperature setting, and adds this in msg.temp_high.

Now, when this single message arrives at the call service node, the JSONata above has both msg.temp_low and msg.temp_high, and can correctly build the data object you need.

This should work.

To help you, and others reading this later, it is worth noting that whilst most nodes work on msg.payload, some have more flexibility. Clearly two nodes in sequence outputting to msg.payload would have the second overwrite the work of the first node. However, the current:state node allows you to build a list of msg.output_fields, and as long as what you use does not get recognised as input or overwritten as output in the next node, it should ‘pass through’ the next node unscathed. For nodes that just work on / output to msg.payload, you can always use a following change node to move msg.payload to something else.

2 Likes

THANK YOU!!! This all worked as you recommended. I was obviously lacking some basic knowledge about how node-red/JSONata worked and your posts have been highly informative. You have no idea how long I have been trying to get these thermostats to sync up. I actually tried Home assistant about a year ago and gave up and went have to Homeseer after about a week of unsuccessfully trying to get this automation working. I finally got frustrated with Homeseer and wanted to give it another try. I was about to just run two systems just let Homeseer manage the thermostats lol. Thank you again!

Great post!